From 85a0039b785c6634ea7a9096b6eac95a15387e91 Mon Sep 17 00:00:00 2001 From: liujunfeng Date: Fri, 12 Dec 2025 18:27:34 +0800 Subject: [PATCH 01/36] fix level update bug in rw rf and resub --- src/base/abc/abcAig.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base/abc/abcAig.c b/src/base/abc/abcAig.c index e3f3ce457..1d93cc0b5 100644 --- a/src/base/abc/abcAig.c +++ b/src/base/abc/abcAig.c @@ -905,7 +905,7 @@ void Abc_AigReplace_int( Abc_Aig_t * pMan, Abc_Obj_t * pOld, Abc_Obj_t * pNew, i { Abc_ObjSetReverseLevel( pFanin1, Abc_ObjReverseLevel(pOld) ); assert( pFanin1->fMarkB == 0 ); - if ( !Abc_ObjIsCi(pFanin1) ) + if ( !Abc_ObjIsCi(pFanin1) && !Abc_AigNodeIsConst(pFanin1) ) { pFanin1->fMarkB = 1; Vec_VecPush( pMan->vLevelsR, Abc_ObjReverseLevel(pFanin1), pFanin1 ); @@ -1139,7 +1139,7 @@ void Abc_AigUpdateLevelR_int( Abc_Aig_t * pMan ) // iterate through the fanins Abc_ObjForEachFanin( pNode, pFanin, v ) { - if ( Abc_ObjIsCi(pFanin) ) + if ( Abc_ObjIsCi(pFanin) || Abc_AigNodeIsConst(pFanin) ) continue; // get the new reverse level of this fanin LevelNew = 0; From 993f30ffae5997a5dc85eb8ad6caf51ba0bbc2cb Mon Sep 17 00:00:00 2001 From: Drew Lewis Date: Thu, 18 Dec 2025 16:59:29 +0000 Subject: [PATCH 02/36] Make multiplications use unsigned to avoid UB on overflow Signed-off-by: Drew Lewis --- src/base/abc/abcAig.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/base/abc/abcAig.c b/src/base/abc/abcAig.c index e3f3ce457..78596f800 100644 --- a/src/base/abc/abcAig.c +++ b/src/base/abc/abcAig.c @@ -90,10 +90,10 @@ struct Abc_Aig_t_ static unsigned Abc_HashKey2( Abc_Obj_t * p0, Abc_Obj_t * p1, int TableSize ) { unsigned Key = 0; - Key ^= Abc_ObjRegular(p0)->Id * 7937; - Key ^= Abc_ObjRegular(p1)->Id * 2971; - Key ^= Abc_ObjIsComplement(p0) * 911; - Key ^= Abc_ObjIsComplement(p1) * 353; + Key ^= (unsigned)Abc_ObjRegular(p0)->Id * 7937; + Key ^= (unsigned)Abc_ObjRegular(p1)->Id * 2971; + Key ^= (unsigned)Abc_ObjIsComplement(p0) * 911; + Key ^= (unsigned)Abc_ObjIsComplement(p1) * 353; return Key % TableSize; } From 860b49dd80e6f20baef5840dbd43ade365e6f54c 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 03/36] 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 c21c05cf5..6ad6b0039 100644 --- a/src/aig/gia/giaMfs.c +++ b/src/aig/gia/giaMfs.c @@ -562,7 +562,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 ) From 11732d3082bcabf782fbb3eb6dff338a2bac23f3 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Wed, 10 Dec 2025 11:03:11 +0100 Subject: [PATCH 04/36] 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 e51a02531..db21c8c3b 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 35d19a9f33b93cd3578d03b1e17ce59d914201d6 Mon Sep 17 00:00:00 2001 From: Miodrag Milanovic Date: Mon, 22 Dec 2025 12:31:08 +0100 Subject: [PATCH 05/36] 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 a343c6789..fff4fa9f6 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 e7c304d3d10154ca42da608e7b88bbe53cc28aec Mon Sep 17 00:00:00 2001 From: Dino <118768649+dinoruic@users.noreply.github.com> Date: Tue, 23 Dec 2025 16:14:38 -0800 Subject: [PATCH 06/36] Fix destructor rewireMiaig.h Pointers that are allocated with a C-style malloc should be deleted with free -- not with the C++-style delete. Using delete here will trip up toolchains that enforce using free for memory allocated with malloc. --- src/opt/rar/rewireMiaig.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/opt/rar/rewireMiaig.h b/src/opt/rar/rewireMiaig.h index 8e2664653..198eca738 100644 --- a/src/opt/rar/rewireMiaig.h +++ b/src/opt/rar/rewireMiaig.h @@ -441,7 +441,7 @@ inline void Miaig::release(void) { if (_data->pRequire) free(_data->pRequire); if (_data->pTable) free(_data->pTable); if (_data->pNtkMapped) Vi_Free(_data->pNtkMapped); - delete _data; + free(_data); } } @@ -612,4 +612,4 @@ inline void Miaig::attachTiming(vi *vCiArrs, vi *vCoReqs) { ABC_NAMESPACE_CXX_HEADER_END #endif // RW_ABC -#endif // REWIRE_MIAIG_H \ No newline at end of file +#endif // REWIRE_MIAIG_H From a625ef2edcfc5e19b98839ed2ce842e814c62c0d Mon Sep 17 00:00:00 2001 From: MyskYko Date: Tue, 23 Dec 2025 23:28:31 -0800 Subject: [PATCH 07/36] update cadical to 2.2.0 --- abclib.dsp | 12 + src/sat/cadical/cadical.hpp | 267 ++- src/sat/cadical/cadicalSolver.c | 19 +- src/sat/cadical/cadical_analyze.cpp | 83 +- src/sat/cadical/cadical_assume.cpp | 21 +- src/sat/cadical/cadical_backbone.cpp | 637 +++++ src/sat/cadical/cadical_backtrack.cpp | 6 +- src/sat/cadical/cadical_ccadical.cpp | 20 +- src/sat/cadical/cadical_checker.cpp | 5 +- src/sat/cadical/cadical_clause.cpp | 57 +- src/sat/cadical/cadical_compact.cpp | 1 - src/sat/cadical/cadical_congruence.cpp | 2092 ++++++++++------- src/sat/cadical/cadical_decide.cpp | 111 +- src/sat/cadical/cadical_decompose.cpp | 5 + src/sat/cadical/cadical_drattracer.cpp | 2 +- src/sat/cadical/cadical_elim.cpp | 7 +- src/sat/cadical/cadical_elimfast.cpp | 8 +- src/sat/cadical/cadical_extend.cpp | 8 + src/sat/cadical/cadical_external.cpp | 17 +- .../cadical/cadical_external_propagate.cpp | 126 +- src/sat/cadical/cadical_factor.cpp | 106 +- src/sat/cadical/cadical_flags.cpp | 2 +- src/sat/cadical/cadical_frattracer.cpp | 2 +- src/sat/cadical/cadical_idruptracer.cpp | 2 +- src/sat/cadical/cadical_internal.cpp | 133 +- src/sat/cadical/cadical_kitten.c | 36 +- src/sat/cadical/cadical_lidruptracer.cpp | 2 +- src/sat/cadical/cadical_limit.cpp | 19 +- src/sat/cadical/cadical_logging.cpp | 5 +- src/sat/cadical/cadical_lratchecker.cpp | 10 +- src/sat/cadical/cadical_lrattracer.cpp | 2 +- src/sat/cadical/cadical_lucky.cpp | 122 +- src/sat/cadical/cadical_message.cpp | 6 + src/sat/cadical/cadical_parse.cpp | 38 +- src/sat/cadical/cadical_phases.cpp | 16 +- src/sat/cadical/cadical_probe.cpp | 13 +- src/sat/cadical/cadical_proof.cpp | 38 +- src/sat/cadical/cadical_propagate.cpp | 12 +- src/sat/cadical/cadical_reduce.cpp | 4 +- src/sat/cadical/cadical_rephase.cpp | 78 +- src/sat/cadical/cadical_report.cpp | 19 +- src/sat/cadical/cadical_restart.cpp | 53 +- src/sat/cadical/cadical_solver.cpp | 116 +- src/sat/cadical/cadical_stats.cpp | 116 +- src/sat/cadical/cadical_subsume.cpp | 1 - src/sat/cadical/cadical_sweep.cpp | 11 +- src/sat/cadical/cadical_tier.cpp | 154 +- src/sat/cadical/cadical_veripbtracer.cpp | 63 +- src/sat/cadical/cadical_version.cpp | 8 +- src/sat/cadical/cadical_vivify.cpp | 316 +-- src/sat/cadical/cadical_walk.cpp | 634 ++++- src/sat/cadical/cadical_walk_full_occs.cpp | 972 ++++++++ src/sat/cadical/cadical_warmup.cpp | 404 ++++ src/sat/cadical/ccadical.h | 7 +- src/sat/cadical/checker.hpp | 33 +- src/sat/cadical/clause.hpp | 8 +- src/sat/cadical/congruence.hpp | 144 +- src/sat/cadical/contract.hpp | 4 +- src/sat/cadical/drattracer.hpp | 16 +- src/sat/cadical/external.hpp | 26 + src/sat/cadical/flags.hpp | 14 +- src/sat/cadical/frattracer.hpp | 29 +- src/sat/cadical/idruptracer.hpp | 48 +- src/sat/cadical/internal.hpp | 95 +- src/sat/cadical/lidruptracer.hpp | 61 +- src/sat/cadical/limit.hpp | 36 +- src/sat/cadical/lratchecker.hpp | 48 +- src/sat/cadical/lrattracer.hpp | 20 +- src/sat/cadical/module.make | 3 + src/sat/cadical/options.hpp | 60 +- src/sat/cadical/parse.hpp | 3 + src/sat/cadical/phases.hpp | 13 +- src/sat/cadical/profile.hpp | 55 +- src/sat/cadical/proof.hpp | 67 +- src/sat/cadical/queue.hpp | 10 + src/sat/cadical/range.hpp | 26 + src/sat/cadical/stats.hpp | 76 +- src/sat/cadical/terminal.hpp | 4 +- src/sat/cadical/tracer.hpp | 13 +- src/sat/cadical/util.hpp | 14 + src/sat/cadical/veripbtracer.hpp | 28 +- src/sat/cadical/vivify.hpp | 18 +- src/sat/cadical/walk.hpp | 97 + src/sat/cadical/watch.hpp | 5 + 84 files changed, 6203 insertions(+), 1895 deletions(-) create mode 100644 src/sat/cadical/cadical_backbone.cpp create mode 100644 src/sat/cadical/cadical_walk_full_occs.cpp create mode 100644 src/sat/cadical/cadical_warmup.cpp create mode 100644 src/sat/cadical/walk.hpp diff --git a/abclib.dsp b/abclib.dsp index 500ffcaf0..1ab567cce 100644 --- a/abclib.dsp +++ b/abclib.dsp @@ -2911,6 +2911,10 @@ SOURCE=.\src\sat\cadical\cadical_averages.cpp # End Source File # Begin Source File +SOURCE=.\src\sat\cadical\cadical_backbone.cpp +# End Source File +# Begin Source File + SOURCE=.\src\sat\cadical\cadical_backtrack.cpp # End Source File # Begin Source File @@ -3243,6 +3247,14 @@ SOURCE=.\src\sat\cadical\cadical_walk.cpp # End Source File # Begin Source File +SOURCE=.\src\sat\cadical\cadical_walk_full_occs.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_warmup.cpp +# End Source File +# Begin Source File + SOURCE=.\src\sat\cadical\cadical_watch.cpp # End Source File # Begin Source File diff --git a/src/sat/cadical/cadical.hpp b/src/sat/cadical/cadical.hpp index 64a5d9ef7..7ceedcec4 100644 --- a/src/sat/cadical/cadical.hpp +++ b/src/sat/cadical/cadical.hpp @@ -7,6 +7,16 @@ #include #include +/*========================================================================*/ + +// We support semantic versioning (https://semver.org/), which means that we +// aim at not breaking API usage when increasing the minor or patch version, +// but assume API breaking changes when increasing the major version. + +#define CADICAL_MAJOR 2 // Major semantic version. +#define CADICAL_MINOR 2 // Minor semantic version. +#define CADICAL_PATCH 0 // Semantic patch version. + ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -46,7 +56,9 @@ enum Status { // // ------------------------------------------------------------------ // // Encode Problem and check without assumptions. // -// enum { TIE = 1, SHIRT = 2 }; +// int TIE = declare_one_more_variable (); +// int SHIRT = declare_one_more_variable (); +// CADICAL_assert (vars () >= 2); // // solver->add (-TIE), solver->add (SHIRT), solver->add (0); // solver->add (TIE), solver->add (SHIRT), solver->add (0); @@ -220,6 +232,7 @@ class Terminator; class ClauseIterator; class WitnessIterator; class ExternalPropagator; +class EquivalenceTracer; class Tracer; struct InternalTracer; class FileTracer; @@ -297,12 +310,22 @@ public: // int solve (); - // Get value (-lit=false, lit=true) of valid non-zero literal. + // Get the value of a valid non-zero literal. This follows the IPASIR + // semantics which says to return 'lit' if 'lit' is assigned to 'true' and + // '-lit' if 'lit' is assigned to false. This has the consequence that + // the returned literal is always assigned to 'true' and thus might be a + // bit confusing. To avoid the headache of these semantics (which we + // unfortunately should follow to be compatabile with IPASIR) the user can + // simply use positive variable indices instead of literals. Then the + // returned integer is negative if the variable is assigned to 'false' and + // positive it is assigned to 'true'. // // require (SATISFIED) // ensure (SATISFIED) // - int val (int lit); + int val ( + int lit, + bool use_default_value_for_declared_but_not_used_variable = true); // Try to flip the value of the given literal without falsifying the // formula. Returns 'true' if this was successful. Otherwise the model is @@ -361,7 +384,7 @@ public: void connect_terminator (Terminator *terminator); void disconnect_terminator (); - // Add call-back which allows to export learned clauses. + // Add call-back which allows to export_ learned clauses. // // require (VALID) // ensure (VALID) @@ -486,14 +509,17 @@ public: // Returns // - // 0 = UNKNOWN (unit propagation did not lead to a conflict nor to a - // complete assignment, or limit reached or interrupted - // through 'terminate') + // 0 = UNKNOWN // 10 = SATISFIABLE // 20 = UNSATISFIABLE - + // + // The 'UNKNOWN' result means that unit propagation did not lead to a + // conflict nor to a complete assignment, or limit reached or interrupted + // through 'terminate'. + // // require (READY) // ensure (INCONCLUSIVE | SATISFIED | UNSATISFIED) + // int propagate (); // @@ -502,6 +528,21 @@ public: // void implied (std::vector &implicants); + // We are not enforcing 'C++14' yet on all compilers/platforms (clang++ + // and MacOS in particular). The feature check for this warning is + // cumbersome to implement and conflicts with other checks for 'C++11'. + // After moving to 'C++14' or better 'C++17' we can add this check back + // but by then will probably have removed 'get_entrailed_literals' anyhow + // (in version '3.0.0'). + // +#if 0 + [[deprecated ("use the function implied instead with the same semantics " + "and arguments")]] +#endif + void get_entrailed_literals (std::vector &implicants) { + implied (implicants); + } + //------------------------------------------------------------------------ // This function determines a good splitting literal. The result can be // zero if the formula is proven to be satisfiable or unsatisfiable. This @@ -510,7 +551,7 @@ public: // returned but the state remains steady. // // require (READY) - // ensure (INCONCLUSIVE |SATISFIED|UNSATISFIED) + // ensure (INCONCLUSIVE | SATISFIED | UNSATISFIED) // int lookahead (void); @@ -565,12 +606,15 @@ public: // literal is used as an argument except for the functions 'val', 'fixed', // 'failed' and 'frozen'. However, the library internally keeps a maximum // variable index, which can be queried. + // // With factor (BVA) the solver might also add new variables. In that case // the user is required to use this to check which variables are currently - // free before adding new variables of their own. - // The alternative is to reserve variables in batches with - // 'reserve_difference'. Using 'reserve' in combination with any technique - // that could add variables (currently only factor) is not advised. + // free before adding new variables of their own. The alternative is to + // declare more variables in batches with 'declare_more_variables'. Using + // 'resize' in combination with any technique that could add variables + // (currently only factor) is not advised. After each application of + // `add`, `vars ()` will return an updated value, even if you did not + // import the entire clause yet. // // require (VALID | SOLVING) // ensure (VALID | SOLVING) @@ -583,21 +627,44 @@ public: // and has the same state transition and conditions as 'assume' etc. // // require (READY) - // ensure (STEADY ) + // ensure (STEADY) // - void reserve (int min_max_var); + void resize (int min_max_var); + + // We are not enforcing 'C++14' yet on all compilers/platforms (clang++ + // and MacOS in particular). The feature check for this warning is + // cumbersome to implement and conflicts with other checks for 'C++11'. + // After moving to 'C++14' or better 'C++17' we can add this check back + // but by then will have removed 'reserve' probably anyhow (in '3.0.0'). + // +#if 0 + [[deprecated ("use the function resize instead with the same semantics " + "and arguments.")]] +#endif + void reserve (int min_max_var) { resize (min_max_var); } // Increase the maximum variable index by a number of new variables. // initializes 'number_of_vars' new variables and protects them from // being used by the solver as extension variables (BVA). + // // It returns the new maximum variable index which is the highest - // variable name of the consecutive range of newly reserved variables. - // It has the same state transition and conditions as 'reserve' above. + // variable name of the consecutive range of newly delcared variables. + // It has the same state transition and conditions as 'resize' above. // // require (READY) - // ensure (STEADY ) + // ensure (STEADY) // - int reserve_difference (int number_of_vars); + int declare_more_variables (int number_of_additional_new_vars); + + // Increase the maximum variable index by one. This is a specialized + // version of declare_more_variables. + + int declare_one_more_variable (); + + // Get the value of some statistics or -1 if the statistics does not + // exist or is not support. Only requires the state to be initialized. + // + int64_t get_statistic_value (const char *const) const; #ifndef CADICAL_NTRACING //------------------------------------------------------------------------ @@ -694,19 +761,26 @@ public: // void optimize (int val); - // Specify search limits, where currently 'name' can be "conflicts", - // "decisions", "preprocessing", or "localsearch". The first two limits - // are unbounded by default. Thus using a negative limit for conflicts or - // decisions switches back to the default of unlimited search (for that - // particular limit). The preprocessing limit determines the number of - // preprocessing rounds, which is zero by default. Similarly, the local - // search limit determines the number of local search rounds (also zero by - // default). As with 'set', the return value denotes whether the limit - // 'name' is valid. These limits are only valid for the next 'solve' or - // 'simplify' call and reset to their default after 'solve' returns (as - // well as overwritten and reset during calls to 'simplify' and - // 'lookahead'). We actually also have an internal "terminate" limit - // which however should only be used for testing and debugging. + // Specify search limits, where currently 'name' can be + // + // "conflicts", + // "decisions", + // "preprocessing", or + // "localsearch". + // + // The first two limits are unbounded by default. Thus using a negative + // limit for conflicts or decisions switches back to the default of + // unlimited search (for that particular limit). The preprocessing limit + // determines the number of preprocessing rounds, which is zero by + // default. Similarly, the local search limit determines the number of + // local search rounds (zero by default). + // + // As with 'set', the return value denotes whether the limit 'name' is + // valid. These limits are only valid for the next 'solve' or 'simplify' + // call and reset to their default after 'solve' returns (as well as + // overwritten and reset during calls to 'simplify' and 'lookahead'). We + // actually also have an internal "terminate" limit which however should + // only be used for testing and debugging. // // require (READY) // ensure (READY) @@ -733,6 +807,7 @@ public: // similar to 'solve' with 'limits ("preprocessing", rounds)' except that // no CDCL nor local search, nor lucky phases are executed. The result // values are also the same: 0=UNKNOWN, 10=SATISFIABLE, 20=UNSATISFIABLE. + // // As 'solve' it resets current assumptions and limits before returning. // The numbers of rounds should not be negative. If the number of rounds // is zero only clauses are restored (if necessary) and top level unit @@ -865,18 +940,16 @@ public: void connect_proof_tracer (FileTracer *tracer, bool antecedents, bool finalize_clauses = false); - // Triggers the conclusion of incremental proofs. - // if the solver is SATISFIED it will trigger extend () - // and give the model to the proof tracer through conclude_sat () - // if the solver is UNSATISFIED it will trigger failing () - // which will learn new clauses as explained below: - // In case of failed assumptions will provide a core negated - // as a clause through the proof tracer interface. - // With a failing constraint these can be multiple clauses. - // Then it will trigger a conclude_unsat event with the id(s) - // of the newly learnt clauses or the id of the global conflict. - // In case the solver is in UNKNOWN, it will collect the currently - // entrailed literals and add them to the proof. + // Triggers the conclusion of incremental proofs. If the solver is + // 'SATISFIED' it will trigger 'extend ()' and give the model to the proof + // tracer through 'conclude_sat ()' if the solver is 'UNSATISFIED' it will + // trigger 'failing ()' which will learn new clauses as explained below: + // In case of failed assumptions will provide a core negated as a clause + // through the proof tracer interface. With a failing constraint these + // can be multiple clauses. Then it will trigger a 'conclude_unsat ()' + // event with the ids of the newly learnt clauses or the id of the global + // conflict. In case the solver is in 'UNKNOWN', it will collect the + // currently "entrailed" literals and add them to the proof. // // require (SATISFIED || UNSATISFIED || UNKNOWN) // ensure (SATISFIED || UNSATISFIED || UNKNOWN) @@ -895,20 +968,20 @@ public: //------------------------------------------------------------------------ - static void usage (); // print usage information for long options + static void usage (); // Print usage information for long options. - static void configurations (); // print configuration usage options + static void configurations (); // Print configuration usage options. // require (!DELETING) // ensure (!DELETING) // - void statistics (); // print statistics - void resources (); // print resource usage (time and memory) + void statistics (); // Print statistics. + void resources (); // Print resource usage (time and memory). // require (VALID) // ensure (VALID) // - void options (); // print current option and value list + void options (); // Print current option and value list. //------------------------------------------------------------------------ // Traverse irredundant clauses or the extension stack in reverse order. @@ -991,6 +1064,7 @@ private: // The solver is in the state ADDING if either the current clause or the // constraint (or both) is not yet terminated. + // bool adding_clause; bool adding_constraint; @@ -1002,7 +1076,7 @@ private: // public API of 'External' but hides everything else (except for the some // private functions). It is supposed to make it easier to understand the // API and use the solver through the API. - + // // This approach has the benefit of decoupling this header file from all // internal data structures, which is particularly useful if the rest of // the source is not available. For instance if only a CaDiCaL library is @@ -1046,9 +1120,9 @@ private: // The environment variable is read in the constructor and the trace is // opened for writing and then closed again in the destructor. // - // Alternatively one case use 'trace_api_calls'. Both + // Alternatively one case use 'trace_api_calls'. // - bool close_trace_api_file; // Close file if owned by solver it. + bool close_trace_api_file; // Close file if owned by solver. FILE *trace_api_file; // Also acts as flag that we are tracing. static bool tracing_api_through_environment; @@ -1057,6 +1131,7 @@ private: void trace_api_call (const char *) const; void trace_api_call (const char *, int) const; + void trace_api_call (const char *, int, int) const; void trace_api_call (const char *, const char *) const; void trace_api_call (const char *, const char *, int) const; #endif @@ -1094,9 +1169,8 @@ private: #endif #endif - // This gives warning messages for wrong 'printf' style format string - // usage. Apparently (on 'gcc 9' at least) the first argument is 'this' - // here. + // Gives warning messages for wrong 'printf' style format string usage. + // Apparently ('gcc 9' at least) the first argument is 'this' here. // // TODO: support for other compilers (beside 'gcc' and 'clang'). @@ -1113,12 +1187,12 @@ private: // require (VALID | DELETING) // ensure (VALID | DELETING) // - void section (const char *); // print section header - void message (const char *, ...) // ordinary message + void section (const char *); // Print section header. + void message (const char *, ...) // ordinary message. CADICAL_ATTRIBUTE_FORMAT (2, 3); - void message (); // empty line - only prefix - void error (const char *, ...) // produce error message + void message (); // Empty line - only prefix. + void error (const char *, ...) // Produce error message. CADICAL_ATTRIBUTE_FORMAT (2, 3); // Explicit verbose level ('section' and 'message' use '0'). @@ -1151,7 +1225,8 @@ private: // ensure (!INITIALIZING) // void dump_cnf (); - friend struct DumpCall; // Mobical calls 'dump_cnf' in 'DumpCall::execute' + friend struct DumpCall; // Mobical calls 'dump_cnf' in + // 'DumpCall::execute ()'. /*----------------------------------------------------------------------*/ @@ -1180,7 +1255,7 @@ public: virtual bool terminate () = 0; }; -// Connected learners which can be used to export learned clauses. +// Connected learners which can be used to export_ learned clauses. // The 'learning' can check the size of the learn clause and only if it // returns true then the individual literals of the learned clause are given // to the learn through 'learn' one by one terminated by a zero literal. @@ -1193,7 +1268,7 @@ public: }; // Connected listener gets notified whenever the truth value of a variable -// is fixed (for example during inprocessing or due to some derived unit +// is fixed (for example during inprocessing or due to derived unit // clauses). class FixedAssignmentListener { @@ -1212,9 +1287,10 @@ public: class ExternalPropagator { public: - bool is_lazy = false; // lazy propagator only checks complete assignments + bool is_lazy = false; // Lazy propagator only checks complete assignments. + bool are_reasons_forgettable = - false; // Reason external clauses can be deleted + false; // Reason external clauses can be deleted. virtual ~ExternalPropagator () {} @@ -1223,8 +1299,22 @@ public: // the call of propagator callbacks and when a driving clause is leading // to an assignment. // - // virtual void notify_assignment (int lit, bool is_fixed) = 0; virtual void notify_assignment (const std::vector &lits) = 0; + + // The notification for the assignement follow the standard trail + // used in SAT solvers. The assignment is a stack with (possibly + // empty) decisions. New assignments are pushed at the end of the + // stack. When backtracking, you get the information on how many + // decision (called levels in SAT solvers) you have to keep: + // new_level mean that all decision and decision and assignments + // before decision number new_level are kept, all other (at the end + // of stack) are removed. + // + // In particular, when backtracking to level '0', no decision is left and + // all assignment done before the first decision are kept. The number + // will always be lower than the number of decisions on the trail, so + // backtracking will always have an effect. + // virtual void notify_new_decision_level () = 0; virtual void notify_backtrack (size_t new_level) = 0; @@ -1236,24 +1326,24 @@ public: virtual bool cb_check_found_model (const std::vector &model) = 0; // Ask the external propagator for the next decision literal. If it - // returns 0, the solver makes its own choice. + // returns '0', the solver makes its own choice. // virtual int cb_decide () { return 0; }; // Ask the external propagator if there is an external propagation to make // under the current assignment. It returns either a literal to be - // propagated or 0, indicating that there is no external propagation under - // the current assignment. + // propagated or '0', indicating that there is no external propagation + // under the current assignment. // virtual int cb_propagate () { return 0; }; // Ask the external propagator for the reason clause of a previous - // external propagation step (done by cb_propagate). The clause must be - // added literal-by-literal closed with a 0. Further, the clause must + // external propagation step (done by 'cb_propagate ()'). The clause must + // be added literal-by-literal closed with a '0'. Further, the clause must // contain the propagated literal. // // The clause will be learned as an Irredundant Non-Forgettable Clause - // (see below at 'cb_has_external_clause' more details about it). + // (see below at 'cb_has_external_clause ()' more details about it). // virtual int cb_add_reason_clause_lit (int propagated_lit) { (void) propagated_lit; @@ -1263,22 +1353,25 @@ public: // The following two functions are used to add external clauses to the // solver during the CDCL loop. The external clause is added // literal-by-literal and learned by the solver as an irredundant - // (original) input clause. The clause can be arbitrary, but if it is - // root-satisfied or tautology, the solver will ignore it without learning - // it. Root-falsified literals are eagerly removed from the clause. - // Falsified clauses trigger conflict analysis, propagating clauses - // trigger propagation. In case chrono is 0, the solver backtracks to - // propagate the new literal on the right decision level, otherwise it - // potentially will be an out-of-order assignment on the current level. - // Unit clauses always (unless root-satisfied, see above) trigger - // backtracking (independently from the value of the chrono option and - // independently from being falsified or satisfied or unassigned) to level - // 0. Empty clause (or root falsified clause, see above) makes the problem - // unsat and stops the search immediately. A literal 0 must close the - // clause. + // (original) input clause. // - // The external propagator indicates that there is a clause to add. - // The parameter of the function allows the user to indicate that how + // The clause can be arbitrary, but if it is root-satisfied or tautology, + // the solver will ignore it without learning it. Root-falsified literals + // are eagerly removed from the clause. Falsified clauses trigger + // conflict analysis, propagating clauses trigger propagation. In case + // 'chrono' is '0', the solver backtracks to propagate the new literal on + // the correct decision level, as otherwise it potentially will be an + // out-of-order assignment on the current level. + // + // Unit clauses always (unless root-satisfied, see above) trigger + // backtracking (independently from the value of the 'chrono' option and + // independently from being falsified or satisfied or unassigned) to level + // '0'. Empty clause (or root falsified clause, see above) makes the + // problem 'UNSATISFIABLE' and stops the search immediately. A literal '0' + // must close the clause. + // + // The external propagator indicates that there is a clause to add. The + // parameter of the function allows the user to indicate that how // 'forgettable' is the external clause. Forgettable clauses are allowed // to be removed by the SAT solver during clause database reduction. // However, it is up to the solver to decide when actually the clause is @@ -1313,7 +1406,7 @@ public: // a unit literal is frozen. Falsified literals are skipped. If the solver // is inconsistent only the empty clause is traversed. // -// If 'clause' returns false traversal aborts early. +// If 'clause' returns 'false' traversal aborts early. class ClauseIterator { public: diff --git a/src/sat/cadical/cadicalSolver.c b/src/sat/cadical/cadicalSolver.c index 3a48344be..f27fe19f9 100644 --- a/src/sat/cadical/cadicalSolver.c +++ b/src/sat/cadical/cadicalSolver.c @@ -238,16 +238,17 @@ int cadical_solver_addvar(cadical_solver* s) { SeeAlso [] ***********************************************************************/ -void cadical_solver_setnvars(cadical_solver* s,int n) { - if ( n <= s->nVars ) - return; - if ( s->nVars == 0 ) - ccadical_reserve((CCaDiCaL*)s->p, n); - else { - assert( 0 ); - ccadical_reserve_difference((CCaDiCaL*)s->p, n - s->nVars); - } +void cadical_solver_setnvars(cadical_solver* s, int n) { + // make sure current number of variables are already fetched + // (also allow cases where addvar has been called) + assert(s->nVars >= ccadical_vars((CCaDiCaL*)s->p)); + // invalid to set fewer variables than current + assert(s->nVars <= n); + // set and resize if appropriate s->nVars = n; + if(ccadical_vars((CCaDiCaL*)s->p) == 0) { + ccadical_resize((CCaDiCaL*)s->p, n); + } } /**Function************************************************************* diff --git a/src/sat/cadical/cadical_analyze.cpp b/src/sat/cadical/cadical_analyze.cpp index 74b67fb06..d9351b80d 100644 --- a/src/sat/cadical/cadical_analyze.cpp +++ b/src/sat/cadical/cadical_analyze.cpp @@ -211,6 +211,7 @@ int Internal::recompute_glue (Clause *c) { int res = 0; const int64_t stamp = ++stats.recomputed; for (const auto &lit : *c) { + CADICAL_assert (val (lit)); int level = var (lit).level; CADICAL_assert (gtab[level] <= stamp); if (gtab[level] == stamp) @@ -535,7 +536,7 @@ Clause *Internal::new_driving_clause (const int glue, int &jump) { jump = var (clause[1]).level; res = new_learned_redundant_clause (glue); - res->used = 1 + (glue <= opts.reducetier2glue); + res->used = max_used; } LOG ("jump level %d", jump); @@ -912,7 +913,7 @@ void Internal::otfs_strengthen_clause (Clause *c, int lit, int new_size, mark_removed (lit); } mini_chain.clear (); - c->used = true; + c->used = max_used; LOG (c, "strengthened"); external->check_shrunken_clause (c); } @@ -1145,6 +1146,26 @@ void Internal::analyze () { uip = 0; while (!uip) { + if (!i) { + lazy_external_propagator_out_of_order_clause (uip); + if (unsat) + return; + else if (uip) { + open = 1; + break; + } else { + LOG (reason, "restarting the analysis on the new conflict"); + ++stats.conflicts; + reason = conflict; + resolvent_size = 0; + antecedent_size = 1; + resolved = 0; + open = 0; + analyze_reason (0, reason, open, resolvent_size, antecedent_size); + conflict_size = antecedent_size - 1; + CADICAL_assert (open > 1); + } + } CADICAL_assert (i > 0); const int lit = (*t)[--i]; if (!flags (lit).seen) @@ -1270,6 +1291,64 @@ void Internal::analyze () { recompute_tier (); } +// In the special case where the external propagator is lazy, the same +// invariants as OTFS break (but even more complicated). There are three +// possible cases: +// - the clause becomes empty (unsat must be answered) +// - the clause is a unit (backtrack and set the clause) +// - the clause is a new conflict on lower level and we restart the +// analysis +// +// TODO: we do not really need to keep the clause longer than the conflict +// analysis. +void Internal::lazy_external_propagator_out_of_order_clause (int &uip) { + CADICAL_assert (!opts.exteagerreasons); + CADICAL_assert (external_prop); + LOG (clause, "out-of-order conflict"); + if (clause.empty ()) { + LOG (lrat_chain, "lrat_chain:"); + LOG (clause, "clause:"); + LOG (unit_chain, "units:"); + if (lrat) { + LOG (unit_chain, "unit chain: "); + for (auto id : unit_chain) + lrat_chain.push_back (id); + unit_chain.clear (); + reverse (lrat_chain.begin (), lrat_chain.end ()); + } + LOG (lrat_chain, "lrat_chain:"); + learn_empty_clause (); + if (external->learner) + external->export_learned_empty_clause (); + conflict = 0; + } else if (clause.size () == 1) { + LOG ("found out-of-order unit"); + uip = -clause[0]; + CADICAL_assert (uip); + backtrack (var (uip).level); + CADICAL_assert (val (uip) > 0); + clause.clear (); + } else { + int jump; + const int glue = clause.size () - 1; + conflict = new_driving_clause (glue, jump); + UPDATE_AVERAGE (averages.current.level, jump); + backtrack (jump); + LOG (conflict, "new conflict"); + } + // Clean up. + // + clear_analyzed_literals (); + clear_unit_analyzed_literals (); + clear_analyzed_levels (); + clause.clear (); + + if (unsat) { + lrat_chain.clear (); + STOP (analyze); + } +} + // We wait reporting a learned unit until propagation of that unit is // completed. Otherwise the 'i' report gives the number of remaining // variables before propagating the unit (and hides the actual remaining diff --git a/src/sat/cadical/cadical_assume.cpp b/src/sat/cadical/cadical_assume.cpp index e7d5b79b7..d828bf4ab 100644 --- a/src/sat/cadical/cadical_assume.cpp +++ b/src/sat/cadical/cadical_assume.cpp @@ -11,7 +11,7 @@ namespace CaDiCaL { // adds an assumption literal onto the assumption stack. void Internal::assume (int lit) { - if (level && !opts.ilbassumptions) + if (level && !opts.ilb) backtrack (); else if (val (lit) < 0) backtrack (max (0, var (lit).level - 1)); @@ -552,9 +552,16 @@ struct sort_assumptions_smaller { // to the first place where the assumptions and the current trail differ. void Internal::sort_and_reuse_assumptions () { - CADICAL_assert (opts.ilbassumptions); - if (assumptions.empty ()) - return; + CADICAL_assert (opts.ilb >= 1); + if (assumptions.empty ()) { + if (opts.ilb == 1) { + LOG ("no assumptions, reusing nothing (ilb == 1)"); + backtrack (0); + } else { // reuse full trail + LOG ("no assumptions, reusing everything (ilb == 2)"); + return; + } + } MSORT (opts.radixsortlim, assumptions.begin (), assumptions.end (), sort_assumptions_positive_rank (this), sort_assumptions_smaller (this)); @@ -599,10 +606,12 @@ void Internal::sort_and_reuse_assumptions () { lit, alit); break; } + if (opts.ilb == 1 && + (size_t) target > assumptions.size ()) // reusing only assumptions + target = assumptions.size (); if (target < level) - backtrack (target); + backtrack_without_updating_phases (target); LOG ("assumptions allow for reuse of trail up to level %d", level); - // COVER (target > 1); if ((size_t) level > assumptions.size ()) stats.assumptionsreused += assumptions.size (); else diff --git a/src/sat/cadical/cadical_backbone.cpp b/src/sat/cadical/cadical_backbone.cpp new file mode 100644 index 000000000..d51841ec7 --- /dev/null +++ b/src/sat/cadical/cadical_backbone.cpp @@ -0,0 +1,637 @@ +#include "global.h" + +#include "internal.hpp" +#include "message.hpp" +#include "util.hpp" + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +inline void Internal::backbone_lrat_for_units (int lit, Clause *reason) { + if (!lrat) + return; + if (level) + return; // not decision level 0 + LOG ("backbone building chain for units"); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (reason); + for (auto &reason_lit : *reason) { + if (lit == reason_lit) + continue; + CADICAL_assert (val (reason_lit)); + if (!val (reason_lit)) + continue; + const int signed_reason_lit = val (reason_lit) * reason_lit; + int64_t id = unit_id (signed_reason_lit); + lrat_chain.push_back (id); + } + lrat_chain.push_back (reason->id); +} + +inline bool Internal::backbone_propagate (int64_t &ticks) { + require_mode (BACKBONE); + CADICAL_assert (!unsat); + START (propagate); + int64_t before = propagated2 = propagated; + for (;;) { + if (propagated2 != trail.size ()) { + const int lit = -trail[propagated2++]; + LOG ("backbone propagating %d over binary clauses", -lit); + Watches &ws = watches (lit); + ticks += + 1 + cache_lines (ws.size (), sizeof (const_watch_iterator *)); + for (const auto &w : ws) { + if (!w.binary ()) + continue; + const signed char b = val (w.blit); + if (b > 0) + continue; + if (b < 0) + conflict = w.clause; // but continue + else { + ticks++; + build_chain_for_units (w.blit, w.clause, 0); + backbone_assign (w.blit, w.clause); + lrat_chain.clear (); + } + } + } else if (!conflict && propagated != trail.size ()) { + const int lit = -trail[propagated++]; + LOG ("backbone propagating %d over large clauses", -lit); + Watches &ws = watches (lit); + const const_watch_iterator eow = ws.end (); + const_watch_iterator i = ws.begin (); + ticks += 1 + cache_lines (ws.size (), sizeof (*i)); + watch_iterator j = ws.begin (); + while (i != eow) { + const Watch w = *j++ = *i++; + if (w.binary ()) + continue; + if (val (w.blit) > 0) + continue; + ticks++; + if (w.clause->garbage) { + j--; + continue; + } + literal_iterator lits = w.clause->begin (); + const int other = lits[0] ^ lits[1] ^ lit; + const signed char u = val (other); + if (u > 0) + j[-1].blit = other; + else { + const int size = w.clause->size; + const const_literal_iterator end = lits + size; + const literal_iterator middle = lits + w.clause->pos; + literal_iterator k = middle; + signed char v = -1; + int r = 0; + while (k != end && (v = val (r = *k)) < 0) + k++; + if (v < 0) { + k = lits + 2; + CADICAL_assert (w.clause->pos <= size); + while (k != middle && (v = val (r = *k)) < 0) + k++; + } + w.clause->pos = k - lits; + CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ()); + if (v > 0) + j[-1].blit = r; + else if (!v) { + LOG (w.clause, "unwatch %d in", r); + lits[0] = other; + lits[1] = r; + *k = lit; + ticks++; + watch_literal (r, lit, w.clause); + j--; + } else if (!u) { + ticks++; + CADICAL_assert (v < 0); + build_chain_for_units (other, w.clause, 0); + backbone_assign_any (other, w.clause); + lrat_chain.clear (); + } else { + if (w.clause == ignore) { + LOG ("ignoring conflict due to clause to vivify"); + continue; + } + CADICAL_assert (u < 0); + CADICAL_assert (v < 0); + conflict = w.clause; + break; + } + } + } + if (j != i) { + while (i != eow) + *j++ = *i++; + ws.resize (j - ws.begin ()); + } + } else + break; + } + int64_t delta = propagated2 - before; + stats.propagations.backbone += delta; + if (conflict) + LOG (conflict, "conflict"); + STOP (propagate); + return !conflict; +} + +inline void Internal::backbone_propagate2 (int64_t &ticks) { + require_mode (BACKBONE); + CADICAL_assert (propagated2 <= trail.size ()); + int64_t before = propagated2; + while (propagated2 != trail.size ()) { + const int lit = -trail[propagated2++]; + LOG ("probe propagating %d over binary clauses", -lit); + Watches &ws = watches (lit); + ticks += 1 + cache_lines (ws.size (), sizeof (const_watch_iterator *)); + for (const auto &w : ws) { + if (!w.binary ()) + break; + const signed char b = val (w.blit); + if (b > 0) + continue; + ticks++; + if (b < 0) { + conflict = w.clause; // no need to continue + break; + } else { + CADICAL_assert (lrat_chain.empty ()); + backbone_lrat_for_units (w.blit, w.clause); + backbone_assign (w.blit, w.clause); + lrat_chain.clear (); + } + } + } + + int64_t delta = propagated2 - before; + stats.propagations.backbone += delta; +} + +void Internal::schedule_backbone_cands (std::vector &candidates) { + + unsigned not_rescheduled = 0; + for (auto v : vars) { + const Flags f = flags (v); + if (!f.active ()) + continue; + if (f.backbone0) { + LOG ("scheduling backbone literal candidate %s", LOGLIT (v)); + candidates.push_back (v); + } else + ++not_rescheduled; + if (f.backbone1) { + LOG ("scheduling backbone literal candidate %s", LOGLIT (-v)); + candidates.push_back (-v); + } else + ++not_rescheduled; + } + + if (not_rescheduled) { + for (auto v : vars) { + const Flags f = flags (v); + if (!f.active ()) + continue; + if (!f.backbone0) { + LOG ("scheduling backbone literal candidate %s", LOGLIT (v)); + candidates.push_back (v); + } + if (!f.backbone1) { + LOG ("scheduling backbone literal candidate %s", LOGLIT (-v)); + candidates.push_back (-v); + } + } + } + CADICAL_assert (candidates.size () <= 2 * (size_t) max_var); + + VERBOSE (3, + "backbone schedule %zu backbone candidates in total %f " + "(rescheduled: %f%%)", + candidates.size (), percent (candidates.size (), 2 * max_var), + percent (not_rescheduled, 2 * max_var)); +} + +int Internal::backbone_analyze (Clause *, int64_t &ticks) { + CADICAL_assert (conflict); + CADICAL_assert (conflict->size == 2); + analyzed.push_back (std::abs (conflict->literals[0])); + flags (conflict->literals[0]).seen = true; + analyzed.push_back (std::abs (conflict->literals[1])); + flags (conflict->literals[1]).seen = true; + LOG (conflict, "analyzing conflict"); + if (lrat) + lrat_chain.push_back (conflict->id); + conflict = nullptr; + + for (auto t = trail.rbegin ();;) { + CADICAL_assert (t < trail.rend ()); + int lit = *t++; + LOG ("analyzing %s", LOGLIT (lit)); + if (!flags (lit).seen) + continue; + Clause *reason = var (lit).reason; + LOG (reason, "resolving with reason of %s", LOGLIT (lit)); + CADICAL_assert (reason), CADICAL_assert (reason != decision_reason); + ++ticks; + const int other = reason->literals[0] ^ reason->literals[1] ^ lit; + Flags &f_o = flags (other); + if (lrat) + lrat_chain.push_back (reason->id); + if (!f_o.seen) { + f_o.seen = true; + analyzed.push_back (other); + } else { + LOG ("backbone UIP %s", LOGLIT (other)); + for (auto lit : analyzed) + flags (lit).seen = false; + analyzed.clear (); + if (lrat) + reverse (begin (lrat_chain), end (lrat_chain)); + return other; + } + } +} + +inline void Internal::backbone_unit_reassign (int lit) { +#ifdef LOGGING + LOG ("reassigning %s to level 0", LOGLIT (lit)); + CADICAL_assert (val (lit) > 0); + CADICAL_assert (val (-lit) < 0); +#else + (void) lit; +#endif + return; +} + +inline void Internal::backbone_unit_assign (int lit) { + LOG ("assigning %s to level 0", LOGLIT (lit)); + require_mode (BACKBONE); + const int idx = vidx (lit); + CADICAL_assert (!vals[idx]); + Var &v = var (idx); + v.level = 0; // required to reuse decisions + v.trail = (int) trail.size (); // used in 'vivify_better_watch' + CADICAL_assert ((int) num_assigned < max_var); + num_assigned++; + v.reason = 0; // for conflict analysis + learn_unit_clause (lit); + lrat_chain.clear (); + const signed char tmp = sign (lit); + vals[idx] = tmp; + vals[-idx] = -tmp; + CADICAL_assert (val (lit) > 0); + CADICAL_assert (val (-lit) < 0); + trail.push_back (lit); + LOG ("backbone assign %d to level 0", lit); +} + +inline void Internal::backbone_assign_any (int lit, Clause *reason) { + require_mode (BACKBONE); + const int idx = vidx (lit); + CADICAL_assert (!vals[idx]); + CADICAL_assert (!flags (idx).eliminated () || !reason); + CADICAL_assert (reason == decision_reason || !reason || reason->size >= 2); + Var &v = var (idx); + v.level = level; // required to reuse decisions + v.trail = (int) trail.size (); // used in 'vivify_better_watch' + CADICAL_assert ((int) num_assigned < max_var); + num_assigned++; + v.reason = level ? reason : 0; // for conflict analysis + if (!level) + learn_unit_clause (lit); + const signed char tmp = sign (lit); + vals[idx] = tmp; + vals[-idx] = -tmp; + CADICAL_assert (val (lit) > 0); + CADICAL_assert (val (-lit) < 0); + trail.push_back (lit); + LOG (reason, "backbone assign %d", lit); +} + +inline void Internal::backbone_assign (int lit, Clause *reason) { + require_mode (BACKBONE); + const int idx = vidx (lit); + CADICAL_assert (!vals[idx]); + CADICAL_assert (!flags (idx).eliminated () || !reason); + CADICAL_assert (reason == decision_reason || !reason || reason->size == 2); + Var &v = var (idx); + v.level = level; // required to reuse decisions + v.trail = (int) trail.size (); // used in 'vivify_better_watch' + CADICAL_assert ((int) num_assigned < max_var); + num_assigned++; + v.reason = level ? reason : 0; // for conflict analysis + if (!level) + learn_unit_clause (lit); + const signed char tmp = sign (lit); + vals[idx] = tmp; + vals[-idx] = -tmp; + CADICAL_assert (val (lit) > 0); + CADICAL_assert (val (-lit) < 0); + trail.push_back (lit); + LOG (reason, "backbone assign %d", lit); +} + +void Internal::backbone_decision (int lit) { + require_mode (BACKBONE); + CADICAL_assert (propagated2 == trail.size ()); + new_trail_level (lit); + notify_decision (); + LOG ("search decide %d", lit); + backbone_assign (lit, decision_reason); +} + +unsigned Internal::compute_backbone_round (std::vector &candidates, + std::vector &units, + const int64_t ticks_limit, + int64_t &ticks, + unsigned inconsistent) { + CADICAL_assert (!conflict); + auto p = begin (candidates); + auto q = p; + const auto end = std::end (candidates); + size_t failed = 0; + ++stats.backbone.rounds; + + LOG (candidates, "candidates: "); + ticks += 1 + cache_lines (candidates.size (), + sizeof (std::vector::iterator *)); + while (p != end) { + CADICAL_assert (p < end); + CADICAL_assert (q <= p); + CADICAL_assert (!conflict); + const int probe = (*q = *p); + ++stats.backbone.probes; + + ++p, ++q; + const signed char v = val (probe); + if (v > 0) { + q--; + LOG ("removing satisfied backbone probe %s", LOGLIT (probe)); + if (probe < 0) + flags (probe).backbone1 = false; + else + flags (probe).backbone0 = false; + continue; + } + + if (v < 0) { + if (var (probe).level) + LOG ("skipping falsified backbone probe %s", LOGLIT (probe)); + else { + LOG ("removing root-level falsified backbone probe %s", + LOGLIT (probe)); + q--; + } + continue; + } + if (ticks >= ticks_limit) + break; + backbone_decision (probe); + backbone_propagate2 (ticks); + if (!conflict) { + LOG (candidates, + "propagating backbone probe %s successful; candidates:", + LOGLIT (probe)); + continue; + } + + ++failed; + ++stats.backbone.units; + int uip = backbone_analyze (conflict, ticks); + backtrack_without_updating_phases (level - 1); + backbone_unit_assign (uip); + ++stats.units; + CADICAL_assert (!conflict); + if (external->learner) + external->export_learned_unit_clause (uip); + + backbone_propagate2 (ticks); + if (conflict) { + LOG ("propagating backbone forced %s failed", LOGLIT (uip)); + inconsistent = uip; + // we have to give up on the current conflict + conflict = nullptr; + break; + } + units.push_back (uip); + } + while (p != end) + *q++ = *p++; + CADICAL_assert (q <= end); + candidates.resize (q - begin (candidates)); + LOG (candidates, "candidates: "); + + if (!inconsistent) { + LOG ("flushing satisfied probe candidates"); + auto p = begin (candidates); + auto q = p; + const auto end = std::end (candidates); + + while (p != end) { + const int probe = (*q++ = *p++); + const signed char v = val (probe); + if (v > 0) { + q--; + LOG ("removing satisfied backbone probe %s", LOGLIT (probe)); + if (probe < 0) + flags (probe).backbone1 = false; + else + flags (probe).backbone0 = false; + continue; + } + + if (v < 0) { + LOG ("keeping falsified probe %s", LOGLIT (probe)); + continue; + } + CADICAL_assert (!v); + LOG ("keeping unassigned probe %s", LOGLIT (probe)); + } + candidates.resize (q - begin (candidates)); + } + LOG (candidates, "candidates after !inconsistent: "); + if (level) + backtrack_without_updating_phases (); + if (!inconsistent && !units.empty ()) { + for (auto l : units) { + backbone_unit_reassign (l); + } + units.clear (); + if (!backbone_propagate (ticks)) { + LOG (conflict, "final repropagation yielded conflict"); + learn_empty_clause (); + } + } else { + if (!backbone_propagate (ticks)) { + learn_empty_clause (); + } + } + LOG (candidates, "candidates end of loop: "); + + LOG (candidates, "candidates end of backbone_round: "); + return failed; +} + +void Internal::keep_backbone_candidates ( + const std::vector &candidates) { + size_t remain = 0; + size_t prioritized = 0; + for (auto v : candidates) { + const Flags &f = flags (v); + if (!f.active ()) + continue; + ++remain; + if (v < 0) + prioritized += f.backbone1; + else + prioritized += f.backbone0; + } + CADICAL_assert (prioritized <= remain); + if (!remain) { +#ifndef CADICAL_NDEBUG + for (auto v : candidates) { + const Flags &f = flags (v); + if (!f.active ()) + continue; + CADICAL_assert (!f.backbone0); + CADICAL_assert (!f.backbone1); + } +#endif + return; + } + if (prioritized == remain) { + LOG ("keeping all remaining backbones"); + } else if (!prioritized) { + for (auto v : candidates) { + Flags &f = flags (v); + if (!f.active ()) + continue; + ++remain; + if (v < 0) { + CADICAL_assert (!f.backbone1); + f.backbone1 = true; + } else { + CADICAL_assert (!f.backbone0); + f.backbone0 = true; + } + } + } +} + +unsigned Internal::compute_backbone () { + size_t failed = 0; + + int64_t ticks = 0; + backbone_propagate2 (ticks); + CADICAL_assert (!conflict); + + std::vector candidates, units; + unsigned inconsistent = 0; + CADICAL_assert (!conflict); + + ++stats.backbone.phases; + schedule_backbone_cands (candidates); + + const size_t max_rounds = opts.backbonemaxrounds; + size_t round_limit = opts.backbonerounds; + round_limit *= stats.backbone.phases; + if (round_limit > max_rounds) + round_limit = max_rounds; + + SET_EFFORT_LIMIT (totalticks, backbone, false); + int64_t ticks_limit = totalticks - stats.ticks.backbone; + PHASE ("backbone", stats.backbone.phases, + "backbone limit of %" PRId64 " ticks", ticks_limit); + size_t rounds = 0; + for (; ++rounds;) { + if (rounds >= round_limit) { + LOG ("backround round limit %zu rounds", rounds); + break; + } + if (ticks >= ticks_limit) { + LOG ("backround round limit %" PRIu64 " ticks", ticks); + break; + } + VERBOSE (3, + "backbone round %zu of %zu with %" PRId64 + " ticks (%f %% done) with %zu failed so far", + rounds, max_rounds, ticks, percent (ticks, ticks_limit), + failed); + size_t new_failed = compute_backbone_round ( + candidates, units, ticks_limit, ticks, inconsistent); + failed += new_failed; + if (inconsistent) + break; + if (candidates.empty ()) + break; + if (unsat) + break; + } + + if (inconsistent && !unsat) { + LOG ("using forced unit %s by repropagating at level 0", + LOGLIT (inconsistent)); + backtrack_without_updating_phases (); + propagate (); + learn_empty_clause (); + } + if (unsat) { + PHASE ("backbone", stats.backbone.phases, + "inconsistent binary clauses"); + } else { + PHASE ("backbone", stats.backbone.phases, + "found %zu backbone literals %zu round in %" PRId64 " ticks", + failed, rounds, ticks); + } + + keep_backbone_candidates (candidates); + if (level) { + backtrack_without_updating_phases (); + if (!backbone_propagate (ticks)) { + learn_empty_clause (); + } + } + stats.ticks.backbone += ticks; + return failed; +} + +void Internal::binary_clauses_backbone () { + if (unsat) + return; + if (!opts.backbone) + return; + if (level) + backtrack_without_updating_phases (); + propagated2 = 0; // TODO: why? + if (!propagate ()) { + LOG ("propagation after connecting watches in inconsistency"); + learn_empty_clause (); + return; + } + + for (auto lit : lits) { + Watches &w = watches (lit); + std::stable_partition (begin (w), end (w), + [] (Watch w) { return w.binary (); }); + } + CADICAL_assert (propagated2 <= trail.size ()); + private_steps = true; + + CADICAL_assert (watching ()); + START_SIMPLIFIER (backbone, BACKBONE); + int failed = compute_backbone (); + CADICAL_assert (!level); + private_steps = false; + + report ('k', !failed); + STOP_SIMPLIFIER (backbone, BACKBONE); +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_backtrack.cpp b/src/sat/cadical/cadical_backtrack.cpp index b3c519ba2..8d182dc81 100644 --- a/src/sat/cadical/cadical_backtrack.cpp +++ b/src/sat/cadical/cadical_backtrack.cpp @@ -49,6 +49,9 @@ inline void Internal::unassign (int lit) { void Internal::update_target_and_best () { + if (opts.rephase == 2 && !stable) + return; + bool reset = (rephased && stats.conflicts > last.rephase.conflicts); if (reset) { @@ -131,7 +134,8 @@ void Internal::backtrack_without_updating_phases (int new_level) { // backtracking. It is possible to just keep out-of-order assigned // literals on the trail without breaking the solver (after some // modifications to 'analyze' - see 'opts.chrono' guarded code there). - CADICAL_assert (opts.chrono || external_prop || did_external_prop); + CADICAL_assert ((in_mode (BACKBONE)) || opts.chrono || external_prop || + did_external_prop); #ifdef LOGGING if (!v.level) LOG ("reassign %d @ 0 unit clause %d", lit, lit); diff --git a/src/sat/cadical/cadical_ccadical.cpp b/src/sat/cadical/cadical_ccadical.cpp index ccc616c80..3b61a0373 100644 --- a/src/sat/cadical/cadical_ccadical.cpp +++ b/src/sat/cadical/cadical_ccadical.cpp @@ -196,12 +196,24 @@ int ccadical_vars (CCaDiCaL *ptr) { return ((Wrapper *) ptr)->solver->vars (); } -int ccadical_reserve_difference (CCaDiCaL *ptr, int number_of_vars) { - return ((Wrapper *) ptr)->solver->reserve_difference (number_of_vars); +int ccadical_declare_more_variables (CCaDiCaL *ptr, int number_of_vars) { + return ((Wrapper *) ptr)->solver->declare_more_variables (number_of_vars); } -void ccadical_reserve(CCaDiCaL *ptr, int min_max_var) { - ((Wrapper *) ptr)->solver->reserve(min_max_var); +int ccadical_declare_one_more_variable (CCaDiCaL *ptr) { + return ((Wrapper *) ptr)->solver->declare_one_more_variable (); +} + +void ccadical_phase (CCaDiCaL *wrapper, int lit) { + ((Wrapper *) wrapper)->solver->phase (lit); +} + +void ccadical_unphase (CCaDiCaL *wrapper, int lit) { + ((Wrapper *) wrapper)->solver->unphase (lit); +} + +void ccadical_resize(CCaDiCaL *ptr, int min_max_var) { + ((Wrapper *) ptr)->solver->resize(min_max_var); } int ccadical_is_inconsistent(CCaDiCaL *ptr) { diff --git a/src/sat/cadical/cadical_checker.cpp b/src/sat/cadical/cadical_checker.cpp index 63a5f2b10..1081f937c 100644 --- a/src/sat/cadical/cadical_checker.cpp +++ b/src/sat/cadical/cadical_checker.cpp @@ -561,7 +561,8 @@ void Checker::add_original_clause (int64_t id, bool, const vector &c, STOP (checking); } -void Checker::add_derived_clause (int64_t id, bool, const vector &c, +void Checker::add_derived_clause (int64_t id, bool, int, + const vector &c, const vector &) { if (inconsistent) return; @@ -627,7 +628,7 @@ void Checker::delete_clause (int64_t id, bool, const vector &c) { void Checker::add_assumption_clause (int64_t id, const vector &c, const vector &chain) { - add_derived_clause (id, true, c, chain); + add_derived_clause (id, true, 0, c, chain); delete_clause (id, true, c); } diff --git a/src/sat/cadical/cadical_clause.cpp b/src/sat/cadical/cadical_clause.cpp index 1c84994a6..481cf4ee6 100644 --- a/src/sat/cadical/cadical_clause.cpp +++ b/src/sat/cadical/cadical_clause.cpp @@ -66,7 +66,7 @@ inline void Internal::mark_added (int lit, int size, bool redundant) { mark_ternary (lit); if (!redundant) mark_block (lit); - if (!redundant || size == 2) + if ((!redundant || size == 2)) mark_factor (lit); } @@ -151,6 +151,7 @@ Clause *Internal::new_clause (bool red, int glue) { void Internal::promote_clause (Clause *c, int new_glue) { CADICAL_assert (c->redundant); + CADICAL_assert (new_glue); const int tier1limit = tier1[false]; const int tier2limit = max (tier1limit, tier2[false]); if (!c->redundant) @@ -160,10 +161,10 @@ void Internal::promote_clause (Clause *c, int new_glue) { int old_glue = c->glue; if (new_glue >= old_glue) return; + c->used = max_used; if (old_glue > tier1limit && new_glue <= tier1limit) { LOG (c, "promoting with new glue %d to tier1", new_glue); stats.promoted1++; - c->used = max_used; } else if (old_glue > tier2limit && new_glue <= tier2limit) { LOG (c, "promoting with new glue %d to tier2", new_glue); stats.promoted2++; @@ -178,6 +179,7 @@ void Internal::promote_clause (Clause *c, int new_glue) { void Internal::promote_clause_glue_only (Clause *c, int new_glue) { CADICAL_assert (c->redundant); + CADICAL_assert (new_glue); if (c->hyper) return; int old_glue = c->glue; @@ -188,7 +190,6 @@ void Internal::promote_clause_glue_only (Clause *c, int new_glue) { if (new_glue <= tier1limit) { LOG (c, "promoting with new glue %d to tier1", new_glue); stats.promoted1++; - c->used = max_used; } else if (old_glue > tier2limit && new_glue <= tier2limit) { LOG (c, "promoting with new glue %d to tier2", new_glue); stats.promoted2++; @@ -582,13 +583,17 @@ Clause *Internal::new_hyper_ternary_resolved_clause (bool red) { return res; } -Clause *Internal::new_factor_clause () { +Clause *Internal::new_factor_clause (int witness) { external->check_learned_clause (); stats.factor_added++; stats.literals_factored += clause.size (); Clause *res = new_clause (false, 0); if (proof) { - proof->add_derived_clause (res, lrat_chain); + if (witness) + proof->add_derived_rat_clause (res, externalize (witness), + lrat_chain); + else + proof->add_derived_clause (res, lrat_chain); } CADICAL_assert (!watching ()); CADICAL_assert (occurring ()); @@ -644,6 +649,48 @@ Clause *Internal::new_resolved_irredundant_clause () { return res; } +void Internal::decay_clauses_upon_incremental_clauses () { + if (!opts.incdecay) + return; + if (!stats.searches) + return; + if (stats.conflicts < lim.incremental_decay) + return; + + PHASE ("decay", stats.incremental_decay, + "decaying clauses with next decaying at conflict %" PRId64 + "(after the next incremental call)", + lim.incremental_decay); + + for (auto c : clauses) { + if (c->garbage) + continue; + if (!c->redundant) + continue; + if (c->id >= last.incremental_decay.last_id) + continue; + switch (opts.incdecay) { + case 1: // my intuition + ++c->glue; + break; + case 2: // Armin's idea + if (c->glue < tier1[false]) + c->used = 1; + break; + case 3: + if (c->glue < tier1[false]) + c->used = 1; + ++c->glue; + break; + default: + break; + } + } + + lim.incremental_decay += stats.conflicts + opts.incdecayint; + ++stats.incremental_decay; + last.incremental_decay.last_id = clause_id; +} } // namespace CaDiCaL ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_compact.cpp b/src/sat/cadical/cadical_compact.cpp index 6747218cb..a46f333c3 100644 --- a/src/sat/cadical/cadical_compact.cpp +++ b/src/sat/cadical/cadical_compact.cpp @@ -354,7 +354,6 @@ void Internal::compact () { mapper.map_vector (phases.target); mapper.map_vector (phases.best); mapper.map_vector (phases.prev); - mapper.map_vector (phases.min); // Special code for 'frozentab'. // diff --git a/src/sat/cadical/cadical_congruence.cpp b/src/sat/cadical/cadical_congruence.cpp index dd5f30b7a..c31a769b5 100644 --- a/src/sat/cadical/cadical_congruence.cpp +++ b/src/sat/cadical/cadical_congruence.cpp @@ -2,6 +2,7 @@ #include "congruence.hpp" #include "internal.hpp" +#include "util.hpp" #include #include #include @@ -19,36 +20,68 @@ Closure::Closure (Internal *i) fresh_id (internal->clause_id) #endif { + dummy_search_gate = new Gate (); + dummy_search_gate->lhs = 0; + dummy_search_gate->garbage = false; } char &Closure::lazy_propagated (int lit) { return lazy_propagated_idx[internal->vidx (lit)]; } +std::string special_gate_str (int8_t f) { + switch (f) { + case NORMAL: + return ""; + case NO_PLUS_ELSE: + return "{no +e}"; + case NO_PLUS_THEN: + return "{no +t}"; + case NO_NEG_ELSE: + return "{no -e}"; + case NO_NEG_THEN: + return "{no -t}"; + case NO_ELSE: + return "{no e}"; + case NO_THEN: + return "{no t}"; + case COND_LHS: + return "{no -t +e}"; + case UCOND_LHS: + return "{no +t -e}"; + case DEGENERATED_AND: + return "{AND/bin}"; + case DEGENERATED_AND_LHS_FALSE: + return "{AND/bin;lhs=-1}"; + default: + CADICAL_assert (false); + return "BROKEN GATE"; + } +} void update_ite_flags (Gate *g) { - int8_t f = g->degenerated_ite; + int8_t f = g->degenerated_gate; const int lhs = g->lhs; - const int cond = g->rhs [0]; + 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; + f |= Special_Gate::NO_NEG_THEN; + f |= Special_Gate::NO_PLUS_ELSE; } if (lhs == -cond) { - f |= Special_ITE_GATE::NO_PLUS_THEN; - f |= Special_ITE_GATE::NO_NEG_ELSE; + f |= Special_Gate::NO_PLUS_THEN; + f |= Special_Gate::NO_NEG_ELSE; } if (lhs == then_lit) { - f |= Special_ITE_GATE::NO_PLUS_THEN; - f |= Special_ITE_GATE::NO_NEG_THEN; + f |= Special_Gate::NO_PLUS_THEN; + f |= Special_Gate::NO_NEG_THEN; } if (lhs == else_lit) { - f |= Special_ITE_GATE::NO_PLUS_ELSE; - f |= Special_ITE_GATE::NO_NEG_ELSE; + f |= Special_Gate::NO_PLUS_ELSE; + f |= Special_Gate::NO_NEG_ELSE; } - g->degenerated_ite = f; + g->degenerated_gate = f; CADICAL_assert (lhs != -then_lit); CADICAL_assert (lhs != -else_lit); CADICAL_assert (cond != then_lit); @@ -59,35 +92,35 @@ void update_ite_flags (Gate *g) { void check_correct_ite_flags (const Gate *const g) { #ifndef CADICAL_NDEBUG - const int8_t f = g->degenerated_ite; + const int8_t f = g->degenerated_gate; const int lhs = g->lhs; - const int cond = g->rhs [0]; + 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)); + CADICAL_assert ((f & Special_Gate::NO_PLUS_THEN)); if (g->pos_lhs_ids[1].clause == nullptr) - CADICAL_assert (f & Special_ITE_GATE::NO_NEG_THEN); + CADICAL_assert (f & Special_Gate::NO_NEG_THEN); if (g->pos_lhs_ids[2].clause == nullptr) - CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_ELSE); + CADICAL_assert (f & Special_Gate::NO_PLUS_ELSE); if (g->pos_lhs_ids[3].clause == nullptr) - CADICAL_assert (f & Special_ITE_GATE::NO_NEG_ELSE); + CADICAL_assert (f & Special_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); + CADICAL_assert (f & Special_Gate::NO_NEG_THEN); + CADICAL_assert (f & Special_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); + CADICAL_assert (f & Special_Gate::NO_PLUS_THEN); + CADICAL_assert (f & Special_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); + CADICAL_assert (f & Special_Gate::NO_PLUS_THEN); + CADICAL_assert (f & Special_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 (f & Special_Gate::NO_PLUS_ELSE); + CADICAL_assert (f & Special_Gate::NO_NEG_ELSE); } CADICAL_assert (lhs != -then_lit); CADICAL_assert (lhs != -else_lit); @@ -96,19 +129,20 @@ void check_correct_ite_flags (const Gate *const g) { CADICAL_assert (cond != -then_lit); CADICAL_assert (cond != -else_lit); #else - (void)g; + (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) { +static inline size_t hash_lits (const std::array &nonces, + const vector &lits, Gate_Type tag) { + uint64_t hash = ((uint64_t) tag << 4) | ((uint64_t) tag >> 50); + const auto end_nonces = std::end (nonces); + const auto begin_nonces = std::begin (nonces); + auto n = begin_nonces + (uint64_t) tag; + CADICAL_assert (n < end_nonces); + for (int lit : lits) { hash += lit; hash *= *n++; hash = (hash << 4) | (hash >> 60); @@ -116,12 +150,11 @@ static size_t hash_lits (std::array &nonces, n = begin_nonces; } hash ^= hash >> 32; - return hash; + return (size_t) hash; } -size_t Hash::operator() (const Gate *const g) const { - CADICAL_assert (hash_lits (nonces, g->rhs) == g->hash); - return g->hash; +inline size_t Hash::operator() (const Gate *const g) const { + return hash_lits (nonces, g->rhs, g->tag); } bool gate_contains (Gate *g, int lit) { @@ -129,19 +162,27 @@ bool gate_contains (Gate *g, int lit) { } /*------------------------------------------------------------------------*/ struct compact_binary_rank { + CaDiCaL::Internal *internal; typedef uint64_t Type; + compact_binary_rank (Internal *i) : internal (i) {} uint64_t operator() (const CompactBinary &a) { - return ((uint64_t) a.lit1 << 32) + a.lit2; + return ((uint64_t) internal->vlit (a.lit1) << 32) + + internal->vlit (a.lit2); }; }; struct compact_binary_order { + CaDiCaL::Internal *internal; + compact_binary_order (Internal *i) : internal (i) {} bool operator() (const CompactBinary &a, const CompactBinary &b) { - return compact_binary_rank () (a) < compact_binary_rank () (b); + return compact_binary_rank (internal) (a) < + compact_binary_rank (internal) (b); }; }; bool Closure::find_binary (int lit, int other) const { + if (internal->vlit (lit) > internal->vlit (other)) + swap (lit, other); const auto offsize = offsetsize[internal->vlit (lit)]; // in C++17: [offset, size] = const auto offset = offsize.first; @@ -150,11 +191,12 @@ bool Closure::find_binary (int lit, int other) const { 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 ()); + auto it = std::lower_bound (begin, end, target, + compact_binary_order (internal)); // 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); + LOG ("found binary [%" PRId64 "] %d %d", it->id, lit, other); if (internal->lrat) lrat_chain.push_back (it->id); } @@ -187,7 +229,7 @@ void Closure::extract_binaries () { } MSORT (internal->opts.radixsortlim, begin (binaries), end (binaries), - compact_binary_rank (), compact_binary_order ()); + compact_binary_rank (internal), compact_binary_order (internal)); { const size_t size = binaries.size (); @@ -207,6 +249,7 @@ void Closure::extract_binaries () { } size_t extracted = 0, already_present = 0, duplicated = 0; + (void) extracted, (void) already_present, (void) duplicated; const size_t size = internal->clauses.size (); for (size_t i = 0; i < size; ++i) { @@ -221,7 +264,8 @@ void Closure::extract_binaries () { 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 + 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)) @@ -256,7 +300,7 @@ void Closure::extract_binaries () { // 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 ()); + compact_binary_rank (internal), compact_binary_order (internal)); const size_t new_size = binaries.size (); { size_t i = 0; @@ -277,9 +321,13 @@ void Closure::extract_binaries () { } binaries.clear (); STOP (extractbinaries); - LOG ("extracted %zu binaries (plus %zu already present and %zu " - "duplicates)", - extracted, already_present, duplicated); + VERBOSE (2, + "[congruence-%" PRId64 + "] extracted %zu binaries (plus %zu already " + "present and %zu " + "duplicates)", + internal->stats.congruence.rounds, extracted, already_present, + duplicated); } /*------------------------------------------------------------------------*/ @@ -299,7 +347,7 @@ void Closure::unmark_all () { void Closure::set_mu1_reason (int lit, Clause *c) { CADICAL_assert (marked (lit) & 1); - LOG (c, "mu1 %d -> %zd", lit, c->id); + LOG (c, "mu1 %d -> %" PRId64, lit, c->id); mu1_ids[internal->vlit (lit)] = LitClausePair (lit, c); } @@ -307,7 +355,7 @@ 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); + LOG (c, "mu2 %d -> %" PRId64, lit, c->id); mu2_ids[internal->vlit (lit)] = LitClausePair (lit, c); } @@ -315,7 +363,7 @@ 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); + LOG (c, "mu4 %d -> %" PRId64, lit, c->id); mu4_ids[internal->vlit (lit)] = LitClausePair (lit, c); } @@ -448,6 +496,29 @@ int Closure::find_representative (int lit) { return res; } +int Closure::find_representative_and_compress_no_proofs (int lit) { + int res = lit; + int nxt = lit; + do { + res = nxt; + nxt = representative (nxt); + } while (nxt != res); + + representative (lit) = res; + representative (-lit) = -res; + + return res; +} + +int Closure::find_representative_already_compressed (int lit) { + int res = lit; + res = representative (lit); + + CADICAL_assert (representative (res) == res); + + return res; +} + int Closure::find_representative_and_compress (int lit, bool update_eager) { LOG ("finding representative of %d", lit); int res = lit; @@ -684,17 +755,21 @@ 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; + CADICAL_assert (table.find (g) != table.end ()); } void Closure::produce_rewritten_clause_lrat_and_clean ( - std::vector &litIds, int except_lhs, bool remove_units) { + std::vector &litIds, int except_lhs, bool remove_units, + bool allow_tautology_in_input) { CADICAL_assert (internal->lrat_chain.empty ()); for (auto &litId : litIds) { - CADICAL_assert (litId.clause); + CADICAL_assert (litId.clause || allow_tautology_in_input); + (void) allow_tautology_in_input; + 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); @@ -705,6 +780,20 @@ void Closure::produce_rewritten_clause_lrat_and_clean ( end (litIds)); } +void Closure::produce_rewritten_clause_lrat_and_clean ( + my_dummy_optional &litIds, int except_lhs, bool remove_units) { + CADICAL_assert (internal->lrat_chain.empty ()); + if (litIds ()) { + auto &litId = litIds.content; + 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); + if (!litId.clause) + litIds.reset (); + } +} + 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) { @@ -713,6 +802,14 @@ void Closure::produce_rewritten_clause_lrat_and_clean ( size_t j = 0; for (size_t i = 0; i < litIds.size (); ++i) { CADICAL_assert (j <= i); + if (!litIds[i].clause) { + LOG ("empty clause %zd", i); + if (i == old_position1) + old_position1 = -1; + if (i == old_position2) + old_position2 = -1; + continue; + } litIds[j].clause = produce_rewritten_clause_lrat ( litIds[i].clause, except_lhs, remove_units); litIds[j].current_lit = @@ -757,15 +854,11 @@ void Closure::compute_rewritten_clause_lrat_simple (Clause *c, int except) { 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"); @@ -820,7 +913,11 @@ Clause *Closure::new_tmp_clause (std::vector &clause) { LOG (clause, "learn new tmp clause"); CADICAL_assert (clause.size () >= 2); - internal->external->check_learned_clause (); + if (internal->external->solution) { + std::swap (clause, internal->clause); + internal->external->check_learned_clause (); + std::swap (clause, internal->clause); + } CADICAL_assert (internal->clause.size () <= (size_t) INT_MAX); const int size = (int) clause.size (); @@ -912,7 +1009,8 @@ void Closure::produce_rewritten_clause_lrat ( } Clause *Closure::produce_rewritten_clause_lrat (Clause *c, int except_lhs, - bool remove_units, bool fail_on_unit) { + 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)); @@ -1012,7 +1110,7 @@ Clause *Closure::produce_rewritten_clause_lrat (Clause *c, int except_lhs, void Closure::push_id_on_chain (std::vector &chain, Rewrite rewrite, int lit) { - LOG ("adding reason %zd for rewriting %d marked", + LOG ("adding reason %" PRId64 " for rewriting %d marked", lit == rewrite.src ? rewrite.id1 : rewrite.id2, lit); chain.push_back (lit == rewrite.src ? rewrite.id1 : rewrite.id2); } @@ -1132,11 +1230,19 @@ void Closure::push_id_on_chain (std::vector &chain, Clause *c) { LOG (lrat_chain, "chain"); } +void Closure::push_id_on_chain (std::vector &chain, + const my_dummy_optional &c) { + CADICAL_assert (c ()); + auto litId = c.content; + chain.push_back (litId.clause->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); + LOG (litId.clause, "found lrat in gate %d from %" PRId64, + litId.current_lit, litId.clause->id); push_id_on_chain (chain, litId.clause); } LOG (lrat_chain, "chain from %zd reasons", reasons.size ()); @@ -1155,9 +1261,19 @@ void Closure::learn_congruence_unit_when_lhs_set (Gate *g, int src, 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); + if (g->neg_lhs_ids ()) + push_id_and_rewriting_lrat_unit (g->neg_lhs_ids.content.clause, + Rewrite (src, dst, id1, id2), + lrat_chain); + else { + CADICAL_assert (g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == + Special_Gate::DEGENERATED_AND_LHS_FALSE); + CADICAL_assert (g->pos_lhs_ids.size () == 1); + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[0].clause, + Rewrite (src, dst, id1, id2), + lrat_chain); + } LOG (lrat_chain, "lrat"); break; default: @@ -1167,10 +1283,15 @@ void Closure::learn_congruence_unit_when_lhs_set (Gate *g, int src, // Something very important here: as we are producing a unit, we cannot // simplify or rewrite the clauses as this will produce units. +// +// For special cases, you have to be careful as the reasons are not +// eagerly reduced. Therefore, we need to filter to keep only the reasons we +// are interested (usually the ones where we have a clash). 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 (unit); CADICAL_assert (!g->pos_lhs_ids.empty ()); CADICAL_assert (internal->analyzed.empty ()); CADICAL_assert (lrat_chain.empty ()); @@ -1185,14 +1306,19 @@ void Closure::learn_congruence_unit_falsifies_lrat_chain ( if (clashing == -g->lhs) { for (auto litId : g->pos_lhs_ids) { LOG (litId.clause, - "found lrat in gate %d from %zd (looking for %d)", + "found lrat in gate %d from %" PRId64 " (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); + g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == + Special_Gate::DEGENERATED_AND_LHS_FALSE + ? 0 + : -g->lhs); } } + CADICAL_assert (!proof_chain.empty ()); } else { // Example: 3 = (-1&2) and 2=1 // The proof consists in taking the binary clause with the rewrites @@ -1201,7 +1327,9 @@ void Closure::learn_congruence_unit_falsifies_lrat_chain ( // 9: -2v1 // 6: 3v1 // The chain cannot start by 9 - if (g->degenerated_and_neg || g->degenerated_and_pos) { + if (g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == + Special_Gate::DEGENERATED_AND_LHS_FALSE) { LOG ("%d %d %d", src, dst, g->lhs); if (src == g->lhs || dst == g->lhs) { LOG ("degenerated AND gate with dst=lhs"); @@ -1215,24 +1343,36 @@ void Closure::learn_congruence_unit_falsifies_lrat_chain ( LOG (proof_chain, "produced lrat chain so far"); } } - CADICAL_assert (!proof_chain.empty ()); + CADICAL_assert (!proof_chain.empty ()); } else { - LOG ("degenerated AND gate with conflict without LHS"); + LOG ("degenerated AND gate with conflict without LHS for %s", + LOGLIT (unit)); for (const auto &litId : g->pos_lhs_ids) { LOG (litId.clause, "definition clause %d ->", litId.current_lit); + const bool insert_after = + std::find (begin (*litId.clause), end (*litId.clause), + unit) == end (*litId.clause); push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), - proof_chain, false, - Rewrite (), -g->lhs); + proof_chain, insert_after, + 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); + const bool insert_after = + std::find (begin (*litId.clause), end (*litId.clause), + unit) == end (*litId.clause); + push_id_and_rewriting_lrat_unit ( + litId.clause, Rewrite (), proof_chain, insert_after, + Rewrite (), + g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == + Special_Gate::DEGENERATED_AND_LHS_FALSE + ? 0 + : -g->lhs); LOG (proof_chain, "produced lrat chain so far"); } } @@ -1245,7 +1385,7 @@ void Closure::learn_congruence_unit_falsifies_lrat_chain ( // rewriting) for (auto litId : g->pos_lhs_ids) { LOG (litId.clause, - "found lrat in gate %d from %zd (looking for %d)", + "found lrat in gate %d from %" PRId64 " (looking for %d)", litId.current_lit, litId.clause->id, falsified); if (litId.current_lit == falsified || (litId.current_lit == src && dst == falsified)) { @@ -1257,9 +1397,9 @@ void Closure::learn_congruence_unit_falsifies_lrat_chain ( } 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); + if (g->neg_lhs_ids ()) { + push_id_and_rewriting_lrat_unit (g->neg_lhs_ids.content.clause, + Rewrite (), proof_chain); } LOG (proof_chain, "produced lrat chain"); break; @@ -1288,16 +1428,18 @@ bool Closure::fully_propagate () { return false; } -bool Closure::learn_congruence_unit (int lit, bool delay_propagation, bool force_propagation) { +bool Closure::learn_congruence_unit (int lit, bool delay_propagation, + bool force_propagation) { if (internal->unsat) return false; + LOG (lrat_chain, "assigning due to LRAT chain"); 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 fully_propagate (); return true; } LOG ("adding unit %s", LOGLIT (lit)); @@ -1307,6 +1449,7 @@ bool Closure::learn_congruence_unit (int lit, bool delay_propagation, bool force if (internal->lrat) { CADICAL_assert (internal->lrat_chain.empty ()); LRAT_ID id = internal->unit_id (-lit); + LOG ("for literal %s adding unit %" PRId64, LOGLIT (-lit), id); internal->lrat_chain.push_back (id); for (auto id : lrat_chain) internal->lrat_chain.push_back (id); @@ -1317,19 +1460,21 @@ bool Closure::learn_congruence_unit (int lit, bool delay_propagation, bool force } LOG (lrat_chain, "assigning due to LRAT chain"); + CADICAL_assert (internal->lrat_chain.empty ()); 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 (); + 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 ( +bool Closure::merge_literals ( Gate *g, Gate *h, int lit, int other, const std::vector &extra_reasons_lit, const std::vector &extra_reasons_ulit) { @@ -1337,9 +1482,7 @@ bool Closure::merge_literals_lrat ( 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! + LOG ("merging literals %s and %s", LOGLIT (lit), LOGLIT (other)); 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); @@ -1365,6 +1508,48 @@ bool Closure::merge_literals_lrat ( } } + // This is a special case where we actually cannot produce an LRAT proof + // 2 = (3 & 4) + // false = (3 & 4) + if (g->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE || + h->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE) { + LOG ("merging special AND case with both side < 0"); + CADICAL_assert (internal->val (g->lhs) < 0 || internal->val (h->lhs) < 0); + CADICAL_assert (internal->val (g->lhs) != internal->val (h->lhs)); + lrat_chain = + extra_reasons_lit.empty () ? extra_reasons_ulit : extra_reasons_lit; + const int unass = internal->val (g->lhs) < 0 ? h->lhs : g->lhs; + learn_congruence_unit (-unass); + return false; + } + + LOG ("special case %d %d %d %d", + g->degenerated_gate == Special_Gate::DEGENERATED_AND, + internal->val (h->lhs) < 0, + h->degenerated_gate == Special_Gate::DEGENERATED_AND, + internal->val (g->lhs) < 0); + if ((g->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE && + internal->val (h->lhs) < 0) || + (h->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE && + internal->val (g->lhs) < 0)) { + LOG ("merging special AND case"); + CADICAL_assert (internal->val (g->lhs) < 0 || internal->val (h->lhs) < 0); + CADICAL_assert (internal->val (g->lhs) != internal->val (h->lhs)); + lrat_chain = + extra_reasons_lit.empty () ? extra_reasons_ulit : extra_reasons_lit; + const int unass = internal->val (g->lhs) < 0 ? h->lhs : g->lhs; + learn_congruence_unit (-unass); + return false; + } + + return really_merge_literals (lit, other, repr_lit, repr_other, + extra_reasons_lit, extra_reasons_ulit); +} + +bool Closure::really_merge_literals ( + int lit, int other, int repr_lit, int repr_other, + const std::vector &extra_reasons_lit, + const std::vector &extra_reasons_ulit) { // For LRAT we need to distinguish more cases for a more regular // reconstruction. // @@ -1383,23 +1568,39 @@ bool Closure::merge_literals_lrat ( // (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. + const int val_lit = internal->val (lit); + const int val_other = internal->val (other); + int smaller_repr = repr_lit; int larger_repr = repr_other; int smaller = lit; int larger = other; + int val_smaller = val_lit; + int val_larger = val_other; const std::vector *smaller_chain = &extra_reasons_ulit; const std::vector *larger_chain = &extra_reasons_lit; + if (val_lit && 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 (repr_lit == repr_other) { + LOG ("already merged %s and %s", LOGLIT (lit), LOGLIT (other)); + return false; + } 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) { - CADICAL_assert (chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); LOG ("merging clashing %d and %d", lit, other); if (internal->proof) { if (internal->lrat) { @@ -1421,9 +1622,10 @@ bool Closure::merge_literals_lrat ( return false; } - LOG ("merging %d and %d first and then the equivalences of %d and %d " + LOG ("merging %s and %s first and then the equivalences of %s and %s " "with LRAT", - lit, other, repr_lit, repr_other); + LOGLIT (lit), LOGLIT (other), LOGLIT (repr_lit), + LOGLIT (repr_other)); Clause *eq1_tmp = nullptr; if (internal->lrat) { lrat_chain = *smaller_chain; @@ -1447,7 +1649,12 @@ bool Closure::merge_literals_lrat ( if (repr_lit == -repr_other) { // now derive empty clause Rewrite rew1, rew2; + Clause *eq1 = eq1_tmp; // == -larger, smaller + Clause *eq2 = eq2_tmp; if (internal->lrat) { + if (val_larger < 0) + swap (eq1, eq2); + // 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, @@ -1456,21 +1663,26 @@ bool Closure::merge_literals_lrat ( 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); + push_id_and_rewriting_lrat_unit (eq1, rew1, lrat_chain, true, rew2); swap (lrat_chain, internal->lrat_chain); } - internal->assign_unit (-larger_repr); - if (internal->lrat) { - internal->lrat_chain.clear (); + 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); - 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); + 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, 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) @@ -1480,22 +1692,37 @@ bool Closure::merge_literals_lrat ( if (val_lit) { if (val_lit == val_other) { - LOG ("not merging lits %d and %d assigned to same value", lit, other); + LOG ("not merging lits %s and %s assigned to same value", + LOGLIT (lit), LOGLIT (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); + LOG ("merging lits %s and %s assigned to inconsistent value", + LOGLIT (lit), LOGLIT (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; + Clause *c; + // eq1 == -larger, smaller + // eq2 == larger, -smaller + 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; + } + int pos = val_lit < 0 ? other : lit; + int neg = val_lit < 0 ? -lit : -other; push_lrat_unit (pos); push_lrat_unit (neg); push_id_on_chain (lrat_chain, c); + swap (internal->lrat_chain, lrat_chain); } internal->learn_empty_clause (); if (internal->lrat) @@ -1504,7 +1731,8 @@ bool Closure::merge_literals_lrat ( } CADICAL_assert (!val_other); - LOG ("merging assigned %d and unassigned %d", lit, other); + LOG ("merging assigned %s and unassigned %s", LOGLIT (lit), + LOGLIT (other)); CADICAL_assert (lrat_chain.empty ()); const int unit = (val_lit < 0) ? -other : other; if (internal->lrat) { @@ -1530,21 +1758,24 @@ bool Closure::merge_literals_lrat ( } if (!val_lit && val_other) { - LOG ("merging assigned %d and unassigned %d", lit, other); + LOG ("merging unassigned %s and assigned %s", LOGLIT (lit), + LOGLIT (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; + // eq1 == -larger, smaller + // eq2 == larger, -smaller + if (lit == smaller) { // opposite of case above + if (val_other < 0) + c = eq2_tmp; // other == larger ==> eq2 == other , unit else - c = eq2_tmp; + c = eq1_tmp; // other == larger ==> eq1 == -other , unit } else { - if (val_lit < 0) - c = eq2_tmp; + if (val_other < 0) + c = eq1_tmp; // lit == larger ==> eq1 == unit, other else - c = eq1_tmp; + c = eq2_tmp; // lit == larger ==> eq2 == unit, -other } push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, Rewrite (), lit, unit); @@ -1633,7 +1864,7 @@ bool Closure::merge_literals_lrat ( // 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 ( +bool Closure::merge_literals ( int lit, int other, const std::vector &extra_reasons_lit, const std::vector &extra_reasons_ulit) { CADICAL_assert (!internal->unsat); @@ -1650,320 +1881,11 @@ bool Closure::merge_literals_lrat ( 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; + return really_merge_literals (lit, other, repr_lit, repr_other, + extra_reasons_lit, extra_reasons_ulit); } inline void Closure::promote_clause (Clause *c) { @@ -2238,8 +2160,8 @@ void Closure::init_closure () { } units = internal->propagated; Random rand (internal->stats.congruence.rounds); - for (auto &n : nonces) { - n = 1 | rand.next (); + for (uint64_t &n : nonces) { + n = static_cast (1) | rand.next (); } #ifdef LOGGING fresh_id = internal->clause_id; @@ -2270,8 +2192,21 @@ void Closure::init_and_gate_extraction () { void Closure::check_and_gate_implied (Gate *g) { CADICAL_assert (internal->clause.empty ()); CADICAL_assert (g->tag == Gate_Type::And_Gate); - if (internal->lrat) + if (!internal->opts.check) return; + if (internal->lrat) { + CADICAL_assert (g->degenerated_gate != Special_Gate::NORMAL || + g->neg_lhs_ids ()); +#ifndef CADICAL_NDEBUG + for (auto c : g->pos_lhs_ids) { + CADICAL_assert (std::find_if ( + begin (*c.clause), end (*c.clause), [this, c] (int lit) { + return find_eager_representative (lit) == c.current_lit; + }) != end (*c.clause)); + } +#endif + return; + } LOG (g, "checking implied"); const int lhs = g->lhs; const int not_lhs = -lhs; @@ -2351,13 +2286,11 @@ 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; @@ -2368,23 +2301,30 @@ void Closure::update_and_gate_unit_build_lrat_chain ( 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->lhs == g->rhs[0]) // do not try to produce LRAT chains, we will not + // need them anyway + return; - if (g->degenerated_and_neg || g->degenerated_and_pos) { + if (!g->neg_lhs_ids ()) { + + if (g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE) { // 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 ()); + 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 + 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->neg_lhs_ids ()); CADICAL_assert (!g->pos_lhs_ids.empty ()); const int repr_lit = find_representative (g->lhs); @@ -2394,9 +2334,9 @@ void Closure::update_and_gate_unit_build_lrat_chain ( return; } - push_id_and_rewriting_lrat_unit (g->neg_lhs_ids[0].clause, Rewrite (), - extra_reasons_ulit, true, Rewrite (), - g->lhs, -dst); + push_id_and_rewriting_lrat_unit (g->neg_lhs_ids.content.clause, + Rewrite (), extra_reasons_ulit, true, + Rewrite (), g->lhs, -dst); LOG (extra_reasons_ulit, "lrat chain for negative side"); lrat_chain.clear (); @@ -2419,10 +2359,12 @@ void Closure::update_and_gate_build_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)) + 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 @@ -2443,7 +2385,7 @@ void Closure::update_and_gate_build_lrat_chain ( for (auto &litId : h->pos_lhs_ids) { if (litId.current_lit == g->lhs) { CADICAL_assert (extra_reasons_ulit.empty ()); - CADICAL_assert (litId.clause); + 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); @@ -2455,6 +2397,54 @@ void Closure::update_and_gate_build_lrat_chain ( CADICAL_assert (extra_reasons_ulit.size () == 1); return; } + + const bool produce_unit_due_to_both_degenerated = + (g_tautology && internal->val (h->lhs) < 0 && + h->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE) || + (h_tautology && internal->val (g->lhs) < 0 && + g->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE); + + if (produce_unit_due_to_both_degenerated) { + LOG ("one gate is a tautology and the other is a unit: producing " + "reason for unit"); + Gate *tauto = (g_tautology ? g : h); + Gate *other = (g_tautology ? h : g); + CADICAL_assert (tauto->degenerated_gate & Special_Gate::DEGENERATED_AND); + LOG (tauto, "starting lrat from tauto gate"); + for (auto &litId : tauto->pos_lhs_ids) { + litId.clause = produce_rewritten_clause_lrat ( + litId.clause, tauto->lhs, remove_units); + if (litId.clause) + extra_reasons_lit.push_back (litId.clause->id); + } + LOG (tauto, "now creating lrat with other gate"); + // if tauto can also be a tautology: neg_lhs_ids + // otherwise the clause is in pos_lhs_ids + for (auto &litId : other->pos_lhs_ids) { + 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, 0, remove_units); + if (litId.clause) + extra_reasons_lit.push_back (litId.clause->id); + } + if (other->neg_lhs_ids ()) { + auto &litId = other->neg_lhs_ids.content; + if (litId.current_lit == tauto->lhs || + litId.current_lit == -tauto->lhs || true) { + 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, 0, remove_units); + if (litId.clause) + extra_reasons_lit.push_back (litId.clause->id); + } + } + 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 @@ -2473,7 +2463,7 @@ void Closure::update_and_gate_build_lrat_chain ( // 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 (litId.clause); CADICAL_assert (extra_reasons_tauto.empty ()); LOG (litId.clause, "binary clause to push into the reason"); litId.clause = produce_rewritten_clause_lrat ( @@ -2493,32 +2483,69 @@ void Closure::update_and_gate_build_lrat_chain ( 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); + 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); + 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)); + 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 (other->neg_lhs_ids ()); + push_id_on_chain (extra_reasons_other, + other->neg_lhs_ids.content.clause); CADICAL_assert (!extra_reasons_tauto.empty ()); CADICAL_assert (!extra_reasons_other.empty ()); return; } + + const bool g_degenerated = + (g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE); + const bool h_degenerated = + (h->degenerated_gate == Special_Gate::DEGENERATED_AND || + h->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE); + + CADICAL_assert (!(g_degenerated && h_degenerated)); + if (g_degenerated || h_degenerated) { + Gate *tauto = (g_degenerated ? g : h); + Gate *other = (g_degenerated ? h : g); + LOG (tauto, "one gate is degenerated"); + CADICAL_assert (tauto != other); + CADICAL_assert (tauto == h || tauto == g); + + auto &extra_reasons_tauto = + (!g_tautology ? extra_reasons_lit : extra_reasons_ulit); + + // start with the other clauses for LRAT + produce_rewritten_clause_lrat_and_clean (other->pos_lhs_ids, + other->lhs); + for (auto &litId : other->pos_lhs_ids) { + LOG (litId.clause, "pushing clause from other"); + push_id_on_chain (extra_reasons_tauto, litId.clause); + } + // one direction: the binary clause already exists + produce_rewritten_clause_lrat_and_clean (tauto->pos_lhs_ids, + other->lhs); + for (auto &litId : tauto->pos_lhs_ids) { + LOG (litId.clause, "pushing clause from tauto"); + push_id_on_chain (extra_reasons_tauto, litId.clause); + } + LOG (extra_reasons_tauto, "lrat chain for positive side"); + 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 ()); @@ -2533,7 +2560,7 @@ void Closure::update_and_gate_build_lrat_chain ( 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); + push_id_on_chain (extra_reasons_ulit, g->neg_lhs_ids); lrat_chain.clear (); LOG (extra_reasons_ulit, "lrat chain for negative side"); @@ -2549,18 +2576,36 @@ void Closure::update_and_gate_build_lrat_chain ( 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 ()); + LOG (g, "update and gate of arity %zu", g->arity ()); CADICAL_assert (lrat_chain.empty ()); bool garbage = true; + if (g->arity () == 1 && internal->val (g->lhs) && + internal->val (g->lhs) == internal->val (g->rhs[0])) { + g->garbage = true; + return; + } 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; + if (internal->lrat) + learn_congruence_unit_falsifies_lrat_chain (g, src, dst, clashing, + falsifies, unit); + int other = -unit; + if (g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE) { + other = find_eager_representative_and_compress (-unit); + if (internal->lrat && -unit != other) { + CADICAL_assert (internal->lrat_chain.empty ()); + internal->lrat_chain.push_back (eager_representative_id (-unit)); + for (auto id : lrat_chain) + internal->lrat_chain.push_back (id); + lrat_chain.clear (); + swap (internal->lrat_chain, lrat_chain); + } + } learn_congruence_unit (unit); if (internal->lrat) lrat_chain.clear (); @@ -2586,8 +2631,8 @@ void Closure::update_and_gate (Gate *g, GatesTable::iterator it, int src, 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)) { + if (merge_literals (g, g, g->lhs, g->rhs[0], extra_reasons_lit, + extra_reasons_ulit)) { ++internal->stats.congruence.unaries; ++internal->stats.congruence.unary_and; } @@ -2604,15 +2649,15 @@ void Closure::update_and_gate (Gate *g, GatesTable::iterator it, int src, 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)) + if (merge_literals (g, h, g->lhs, h->lhs, extra_reasons_lit2, + extra_reasons_ulit2)) ++internal->stats.congruence.ands; } else { if (g->indexed) { + CADICAL_assert (it != table.end ()); 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); @@ -2632,7 +2677,6 @@ void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { 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) { @@ -2653,7 +2697,7 @@ void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { for (auto id : lrat_chain) internal->lrat_chain.push_back (id); lrat_chain.clear (); - internal->learn_empty_clause(); + internal->learn_empty_clause (); } else learn_congruence_unit (-g->lhs); @@ -2681,9 +2725,8 @@ void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { 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 + } else if (merge_literals (g->lhs, g->rhs[0], reasons_implication, + reasons_back)) { ++internal->stats.congruence.unaries; ++internal->stats.congruence.unary_and; } @@ -2695,15 +2738,15 @@ void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { 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)) + if (merge_literals (g, h, 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; @@ -2726,6 +2769,16 @@ void Closure::simplify_and_gate (Gate *g) { int falsifies = 0; std::vector::iterator it = begin (g->rhs); bool ulhs_in_rhs = false; + if (g->degenerated_gate != Special_Gate::NORMAL) { + const int old_lhs = g->lhs; + g->lhs = find_eager_representative (g->lhs); + for (auto &c : g->pos_lhs_ids) { + if (c.current_lit == old_lhs) + c.current_lit = g->lhs; + if (c.current_lit == -old_lhs) + c.current_lit = -g->lhs; + } + } for (auto lit : g->rhs) { const signed char v = internal->val (lit); if (v > 0) { @@ -2739,24 +2792,24 @@ void Closure::simplify_and_gate (Gate *g) { ulhs_in_rhs = true; *it++ = lit; if (lit == g->lhs) - g->degenerated_and_pos = true; + g->degenerated_gate = DEGENERATED_AND; if (lit == -g->lhs) - g->degenerated_and_neg = true; + g->degenerated_gate = DEGENERATED_AND; } 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); + LOG ("looking at %d [%zu %zu]", g->pos_lhs_ids[j].current_lit, i, j); g->pos_lhs_ids[i] = g->pos_lhs_ids[j]; - if (!g->degenerated_and_pos && + if (!g->degenerated_gate && 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); + LOG ("keeping %d [%zu %zu]", g->pos_lhs_ids[i].current_lit, i, j); ++i; } - LOG ("resizing to %ld", i); + LOG ("resizing to %zu", i); g->pos_lhs_ids.resize (i); } @@ -2767,7 +2820,6 @@ void Closure::simplify_and_gate (Gate *g) { 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); @@ -2823,7 +2875,7 @@ bool Closure::simplify_gates (int lit) { /*------------------------------------------------------------------------*/ // AND gates -Gate *Closure::find_and_lits (const vector &rhs, Gate *except) { +Gate *Closure::find_and_lits (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); @@ -2832,42 +2884,46 @@ Gate *Closure::find_and_lits (const vector &rhs, 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 *Closure::find_gate_lits (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; + dummy_search_gate->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; + dummy_search_gate->tag = typ; + std::swap (dummy_search_gate->rhs, rhs); + CADICAL_assert (dummy_search_gate->lhs == 0); + CADICAL_assert (dummy_search_gate->garbage == false); + + if ((!except || !except->indexed)) { + auto git = table.find (dummy_search_gate); + if (git != std::end (table)) + h = *git; + CADICAL_assert (!except || h != except); + } else { + const auto &its = table.equal_range (dummy_search_gate); + for (auto it = its.first; it != its.second; ++it) { + LOG ((*it), "checking gate in the table"); + if (*it == except) + continue; + if ((*it)->tag != typ) + continue; + if ((*it)->rhs != dummy_search_gate->rhs) + continue; + h = *it; + break; + } } + std::swap (dummy_search_gate->rhs, rhs); if (h) { - LOG (g, "searching"); + LOG (dummy_search_gate, "searching"); LOG (h, "already existing"); - delete g; return h; } else { - LOG (g->rhs, "gate not found in table"); - delete g; + LOG (dummy_search_gate->rhs, "gate not found in table"); return nullptr; } } @@ -2891,7 +2947,7 @@ Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { g->lhs = lhs; g->tag = Gate_Type::And_Gate; if (internal->lrat) { - g->neg_lhs_ids.push_back (LitClausePair (lhs, base_clause)); + g->neg_lhs_ids = LitClausePair (lhs, base_clause); for (auto i : lrat_chain_and_gate) g->pos_lhs_ids.push_back (i); #ifdef LOGGING @@ -2901,9 +2957,10 @@ Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { [] (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; }); + if (g->neg_lhs_ids ()) { + auto c = g->neg_lhs_ids.content; + result.push_back (c.clause->id); + } LOG (result, "lrat chain negative (%d):", lhs); #endif } @@ -2916,8 +2973,8 @@ Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { 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)) { + if (merge_literals (g, h, lhs, h->lhs, reasons_lrat_src, + reasons_lrat_usrc)) { LOG ("found merged literals"); ++internal->stats.congruence.ands; } @@ -2930,7 +2987,6 @@ Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { g->garbage = false; g->indexed = true; g->shrunken = false; - g->hash = hash_lits (nonces, g->rhs); table.insert (g); ++internal->stats.congruence.gates; @@ -2942,6 +2998,7 @@ Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { connect_goccs (g, lit); } } + ++internal->stats.congruence.and_gates; return g; } @@ -3072,7 +3129,7 @@ void Closure::check_not_tmp_binary_clause (Clause *c) { #else (void) c; #endif -}; +} Clause *Closure::maybe_promote_tmp_binary_clause (Clause *c) { CADICAL_assert (internal->lrat); @@ -3089,7 +3146,7 @@ Clause *Closure::maybe_promote_tmp_binary_clause (Clause *c) { 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 ()); @@ -3127,9 +3184,9 @@ Gate *Closure::find_remaining_and_gate (Clause *base_clause, int 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)); + LOG ("trying to find AND gate with remaining LHS %s", LOGLIT (lhs)); + LOG ("negated LHS %s occurs times in %" PRId64 " binary clauses", + LOGLIT (not_lhs), internal->noccs (-lhs)); const size_t arity = lits.size () - 1; size_t matched = 0; @@ -3150,7 +3207,7 @@ Gate *Closure::find_remaining_and_gate (Clause *base_clause, int lhs) { ++matched; if (!(mark & 2)) { lrat_chain_and_gate.push_back (LitClausePair (other, w.clause)); - LOG ("pushing %d -> %zd", other, w.clause->id); + LOG ("pushing %d -> %" PRId64, other, w.clause->id); continue; } LOG ("marking %d mu4", other); @@ -3279,7 +3336,7 @@ void Closure::extract_and_gates_with_base_clause (Clause *c) { const size_t arity = size - 1; if (max_negbincount < arity) { LOG (c, - "all literals have less than %lu negated occurrences" + "all literals have less than %zu negated occurrences" "thus skipping", arity); if (internal->lrat) @@ -3305,7 +3362,7 @@ void Closure::extract_and_gates_with_base_clause (Clause *c) { } const size_t reduced_size = clause_size - reduced; CADICAL_assert (reduced_size); - LOG (c, "trying as base arity %lu AND gate", arity); + LOG (c, "trying as base arity %zu AND gate", arity); CADICAL_assert (begin (lits) + reduced_size <= end (lits)); MSORT (internal->opts.radixsortlim, begin (lits), begin (lits) + reduced_size, @@ -3321,8 +3378,9 @@ void Closure::extract_and_gates_with_base_clause (Clause *c) { if (c->garbage) break; const int lhs = lits[i]; - LOG ("trying LHS candidate literal %d with %ld negated occurrences", - (lhs), internal->noccs (-lhs)); + LOG ("trying LHS candidate literal %s with %" PRId64 + " negated occurrences", + LOGLIT (lhs), internal->noccs (-lhs)); if (first) { first = false; @@ -3347,7 +3405,7 @@ void Closure::extract_and_gates_with_base_clause (Clause *c) { } lrat_chain_and_gate.clear (); if (extracted) - LOG (c, "extracted %u with arity %lu AND base", extracted, arity); + LOG (c, "extracted %u with arity %zu AND base", extracted, arity); } void Closure::reset_and_gate_extraction () { @@ -3360,12 +3418,16 @@ void Closure::extract_and_gates () { if (!internal->opts.congruenceand) return; START (extractands); + marks.resize (internal->max_var * 2 + 3); init_and_gate_extraction (); - +#ifndef CADICAL_QUIET + const int64_t gates_before = internal->stats.congruence.and_gates; +#endif 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 + ++i) { + // we can learn new binary clauses, but so no for loop CADICAL_assert (lrat_chain.empty ()); Clause *c = internal->clauses[i]; if (c->garbage) @@ -3380,6 +3442,11 @@ void Closure::extract_and_gates () { CADICAL_assert (lrat_chain.empty ()); } + VERBOSE (2, + "[congruence-%" PRId64 "] " + "found %" PRIu64 " AND gates", + internal->stats.congruence.rounds, + internal->stats.congruence.and_gates - gates_before); reset_and_gate_extraction (); STOP (extractands); } @@ -3415,6 +3482,8 @@ void inc_lits (vector &lits) { void Closure::check_ternary (int a, int b, int c) { CADICAL_assert (internal->clause.empty ()); + if (!internal->opts.check) + return; if (internal->lrat) return; auto &clause = internal->clause; @@ -3424,7 +3493,7 @@ void Closure::check_ternary (int a, int b, int c) { clause.push_back (c); internal->external->check_learned_clause (); if (internal->proof) { - const LRAT_ID id = internal->clause_id++; + const LRAT_ID id = ++internal->clause_id; internal->proof->add_derived_clause (id, false, clause, {}); internal->proof->delete_clause (id, false, clause); } @@ -3434,6 +3503,8 @@ void Closure::check_ternary (int a, int b, int c) { void Closure::check_binary_implied (int a, int b) { CADICAL_assert (internal->clause.empty ()); + if (!internal->opts.check) + return; if (internal->lrat) return; auto &clause = internal->clause; @@ -3445,6 +3516,8 @@ void Closure::check_binary_implied (int a, int b) { } void Closure::check_implied () { + if (!internal->opts.check) + return; if (internal->lrat) return; internal->external->check_learned_clause (); @@ -3481,8 +3554,6 @@ void Closure::add_xor_shrinking_proof_chain (Gate *g, int pivot) { 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); @@ -3524,6 +3595,8 @@ void Closure::check_xor_gate_implied (Gate const *const g) { if (internal->lrat) { return; } + if (!internal->opts.check) + return; const int lhs = g->lhs; LOG (g, "checking implied"); auto &clause = internal->clause; @@ -3542,7 +3615,7 @@ void Closure::check_xor_gate_implied (Gate const *const g) { inc_lits (clause); internal->external->check_learned_clause (); if (internal->proof) { - internal->proof->add_derived_clause (internal->clause_id, false, + internal->proof->add_derived_clause (++internal->clause_id, false, clause, {}); internal->proof->delete_clause (internal->clause_id, false, clause); } @@ -3551,7 +3624,7 @@ void Closure::check_xor_gate_implied (Gate const *const g) { clause.clear (); } -Gate *Closure::find_xor_lits (const vector &rhs) { +Gate *Closure::find_xor_lits (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); @@ -3579,19 +3652,16 @@ bool Closure::normalize_ite_lits_gate (Gate *g) { 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 flag = g->degenerated_gate; 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); + g->degenerated_gate = + (plus_then ? Special_Gate::NO_PLUS_ELSE : Special_Gate::NORMAL) | + (neg_then ? Special_Gate::NO_NEG_ELSE : Special_Gate::NORMAL) | + (plus_else ? Special_Gate::NO_PLUS_THEN : Special_Gate::NORMAL) | + (neg_else ? Special_Gate::NO_NEG_THEN : Special_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) @@ -3609,19 +3679,16 @@ bool Closure::normalize_ite_lits_gate (Gate *g) { 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 flag = g->degenerated_gate; 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); + g->degenerated_gate = + (plus_then ? Special_Gate::NO_NEG_THEN : Special_Gate::NORMAL) | + (neg_then ? Special_Gate::NO_PLUS_THEN : Special_Gate::NORMAL) | + (plus_else ? Special_Gate::NO_NEG_ELSE : Special_Gate::NORMAL) | + (neg_else ? Special_Gate::NO_PLUS_ELSE : Special_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! @@ -3635,7 +3702,7 @@ bool Closure::normalize_ite_lits_gate (Gate *g) { #ifndef CADICAL_NDEBUG bool is_tautological_ite_gate (Gate *g) { CADICAL_assert (g->tag == Gate_Type::ITE_Gate); - CADICAL_assert (g->rhs.size () == 3); + CADICAL_assert (g->arity () == 3); const int cond_lit = g->rhs[0]; const int then_lit = g->rhs[1]; const int else_lit = g->rhs[2]; @@ -3713,6 +3780,7 @@ LRAT_ID Closure::simplify_and_add_to_proof_chain (vector &unsimplified, } } else { id = check_and_add_to_proof_chain (clause); + CADICAL_assert (id > 0); add_clause_to_chain (clause, id); } } else { @@ -3765,12 +3833,14 @@ void Closure::simplify_and_sort_xor_lrat_clauses ( 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; + if (lhs1 == lhs2) + return; CADICAL_assert (unsimplified.empty ()); - unsimplified = g->rhs; + unsimplified.reserve (g->arity ()); + for (auto lit : g->rhs) + unsimplified.push_back (lit); vector first; vector second; if (internal->lrat) { @@ -3785,7 +3855,7 @@ void Closure::add_xor_matching_proof_chain ( for (auto pair : first) { bool first = pair.current_lit & 1; int rest = pair.current_lit >> 1; - rest &= ~(1 << (g->rhs.size () - 1)); + rest &= ~(1 << (g->arity () - 1)); if (first == (lhs1 > 0)) { first_ids.push_back (LitIdPair (rest, pair.clause->id)); } else { @@ -3796,7 +3866,7 @@ void Closure::add_xor_matching_proof_chain ( for (auto pair : second) { bool first = pair.current_lit & 1; int rest = pair.current_lit >> 1; - rest &= ~(1 << (g->rhs.size () - 1)); + rest &= ~(1 << (g->arity () - 1)); if (first == (lhs2 < 0)) { first_ids.push_back (LitIdPair (rest, pair.clause->id)); } else { @@ -3804,7 +3874,6 @@ void Closure::add_xor_matching_proof_chain ( } LOG (pair.clause, "key %d", pair.current_lit); } - // TODO Florian: resort and ids after every round do { vector first_tmp; vector second_tmp; @@ -3873,7 +3942,7 @@ void Closure::add_xor_matching_proof_chain ( Gate *Closure::new_xor_gate (const vector &glauses, int lhs) { rhs.clear (); - + rhs.reserve (lits.size ()); for (auto lit : lits) { if (lhs != lit && -lhs != lit) { CADICAL_assert (lit > 0); @@ -3888,8 +3957,7 @@ Gate *Closure::new_xor_gate (const vector &glauses, 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)) { + if (merge_literals (g->lhs, lhs, reasons_implication, reasons_back)) { ++internal->stats.congruence.xors; } delete_proof_chain (); @@ -3902,9 +3970,9 @@ Gate *Closure::new_xor_gate (const vector &glauses, 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); + if (internal->lrat) + for (auto pair : glauses) + g->pos_lhs_ids.push_back (pair); table.insert (g); ++internal->stats.congruence.gates; #ifdef LOGGING @@ -3916,8 +3984,10 @@ Gate *Closure::new_xor_gate (const vector &glauses, connect_goccs (g, lit); } } + ++internal->stats.congruence.xor_gates; return g; } + uint32_t Closure::number_from_xor_reason_reversed (const std::vector &rhs) { uint32_t n = 0; @@ -4026,7 +4096,7 @@ void Closure::init_xor_gate_extraction (std::vector &candidates) { CONTINUE_COUNTING_NEXT_CLAUSE:; } - LOG ("considering %zd out of %zd", candidates.size (), + LOG ("considering %zu out of %" PRId64, candidates.size (), internal->irredundant ()); const unsigned rounds = internal->opts.congruencexorcounts; #ifdef LOGGING @@ -4072,7 +4142,7 @@ void Closure::init_xor_gate_extraction (std::vector &candidates) { if (!removed) break; - LOG ("after round %d, %zd (%ld %%) remain", round, candidates.size (), + LOG ("after round %d, %zd (%zu %%) remain", round, candidates.size (), candidates.size () / (1 + original_size) * 100); } @@ -4080,6 +4150,10 @@ void Closure::init_xor_gate_extraction (std::vector &candidates) { for (auto lit : *c) internal->occs (lit).push_back (c); } + + VERBOSE ( + 2, "connected %zu large clauses %.0f%%", candidates.size (), + percent (candidates.size (), internal->stats.current.irredundant)); } Clause *Closure::find_large_xor_side_clause (std::vector &lits) { @@ -4107,7 +4181,7 @@ Clause *Closure::find_large_xor_side_clause (std::vector &lits) { 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); + LOG ("searching for size %zu", size_lits); for (auto c : internal->occs (least_occurring_literal)) { LOG (c, "checking"); CADICAL_assert (c->size != 2); // TODO kissat has break @@ -4252,6 +4326,7 @@ void Closure::extract_xor_gates_with_base_clause (Clause *c) { inc_lits (lits); LOG (lits, "found all needed %u matching clauses:", found); CADICAL_assert (found == 1u << arity); + (void) found; if (negated) { auto p = begin (lits); int lit; @@ -4279,6 +4354,9 @@ void Closure::extract_xor_gates () { if (!internal->opts.congruencexor) return; START (extractxors); +#ifndef CADICAL_QUIET + const int64_t gates_before = internal->stats.congruence.xor_gates; +#endif LOG ("starting extracting XOR"); std::vector candidates = {}; init_xor_gate_extraction (candidates); @@ -4289,6 +4367,9 @@ void Closure::extract_xor_gates () { continue; extract_xor_gates_with_base_clause (c); } + VERBOSE (2, "[congruence-%" PRId64 "] found %" PRId64 " XOR gates", + internal->stats.congruence.rounds, + internal->stats.congruence.xor_gates - gates_before); reset_xor_gate_extraction (); STOP (extractxors); } @@ -4300,11 +4381,11 @@ void Closure::find_units () { RESTART: if (!internal->flags (v).active ()) continue; - for (int sgn = -1; sgn < 1; sgn += 2) { + 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 + break; const int other = w.blit; if (marked (-other)) { LOG (w.clause, "binary clause %d %d and %d %d give unit %d", lit, @@ -4332,6 +4413,7 @@ void Closure::find_units () { CADICAL_assert (internal->analyzed.empty ()); } LOG ("found %zd units", units); + (void) units; } void Closure::find_equivalences () { @@ -4394,7 +4476,6 @@ void Closure::find_equivalences () { lit, other, internal->lrat ? marked_mu1 (-other).clause : nullptr, w.clause)) { - ++internal->stats.congruence.congruent; } unmark_all (); if (internal->unsat) @@ -4407,7 +4488,11 @@ void Closure::find_equivalences () { unmark_all (); } CADICAL_assert (internal->analyzed.empty ()); - LOG ("found %zd equivalences", schedule.size ()); + // force removal of memory + mu1_ids.clear (); + shrink_vector (mu1_ids); + VERBOSE (2, "[congruence-%" PRId64 "] found %zd equivalences", + internal->stats.congruence.rounds, schedule.size ()); } /*------------------------------------------------------------------------*/ @@ -4428,22 +4513,29 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, 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); + GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); + CADICAL_assert (!g->indexed || git != table.end ()); int clashing = 0, falsifies = 0; unsigned dst_count = 0, not_dst_count = 0; auto q = begin (g->rhs); + if (g->degenerated_gate != Special_Gate::NORMAL) { + g->lhs = find_eager_representative (g->lhs); + LOG (g, "updating LHS to"); + } for (int &lit : g->rhs) { - if (lit == src) - lit = dst; - if (lit == -g->lhs) { + if (lit == -g->lhs || (lit == src && dst == -g->lhs)) { LOG ("found negated LHS literal %d", lit); clashing = lit; - g->degenerated_and_neg = true; + g->degenerated_gate = DEGENERATED_AND; break; } + if (lit == src) + lit = dst; if (lit == g->lhs) - g->degenerated_and_pos = true; + g->degenerated_gate = Special_Gate::DEGENERATED_AND; + if (-lit == g->lhs) + g->degenerated_gate = Special_Gate::DEGENERATED_AND; const signed char val = internal->val (lit); if (val > 0) { continue; @@ -4456,7 +4548,7 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, if (lit == dst) { if (not_dst_count) { LOG ("clashing literals %d and %d", (-dst), (dst)); - clashing = -dst; + clashing = dst; break; } if (dst_count++) @@ -4476,6 +4568,11 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, } LOG (lrat_chain, "lrat chain after rewriting"); + if (q != end (g->rhs)) { + g->rhs.resize (q - begin (g->rhs)); + g->shrunken = true; + } + if (internal->lrat) { // updating reasons in the chain. #ifdef LOGGING for (auto litId : g->pos_lhs_ids) { @@ -4484,30 +4581,40 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, #endif // We remove all assigned literals except the falsified literal such // that we can produce an LRAT chain + // + // Obviously there are exceptions that make everything more complicated, + // in particular when we do not need the gate anymore, like in: 3 := And + // 1@0+=1 3 Then CADICAL_assertion will fail (like the `CADICAL_assert (i)`) but it does + // not matter. 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); + 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); + // We do not need all the clauses. Therefore, we keep only the + // ones required by 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) + int &curr = g->pos_lhs_ids[i].current_lit; + // if the gate is not normal, we rewrite the LHS. Therefore, we + // also have to update the literal here. + if (g->degenerated_gate != Special_Gate::NORMAL && + find_eager_representative (curr) == g->lhs) + curr = find_eager_representative (curr); + if (keep_clashing && curr != orig_clashing && + curr != -orig_clashing && curr != keep_clashing && + curr != -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) + if (internal->val (curr) && curr != src && curr != orig_falsifies) continue; - if (g->pos_lhs_ids[i].current_lit == dst) { + if (curr == dst) { if (!found) found = true; else @@ -4526,21 +4633,18 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, ++i; } LOG ("resizing to %zd", i); - CADICAL_assert (i); + CADICAL_assert (i || (g->arity () == 1 && g->rhs[0] == g->lhs)); 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 ()); + CADICAL_assert (!internal->lrat || !g->pos_lhs_ids.empty () || + (g->arity () == 1 && g->rhs[0] == g->lhs)); // check_and_gate_implied (g); update_and_gate (g, git, src, dst, id1, id2, falsifies, clashing); ++internal->stats.congruence.rewritten_ands; @@ -4609,7 +4713,7 @@ void Closure::rewrite_xor_gate (Gate *g, int dst, int src) { 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); + dst = internal->vidx (dst); unsigned negate = original_dst_negated; const size_t size = g->rhs.size (); for (size_t i = 0; i < size; ++i) { @@ -4646,14 +4750,10 @@ void Closure::rewrite_xor_gate (Gate *g, int dst, int src) { 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); @@ -4674,7 +4774,7 @@ void Closure::rewrite_xor_gate (Gate *g, int dst, int src) { } check_xor_gate_implied (g); - // TODO stats + ++internal->stats.congruence.rewritten_xors; } // update to produce proofs @@ -4685,7 +4785,7 @@ void Closure::simplify_xor_gate (Gate *g) { 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 (); + const size_t size = g->arity (); size_t j = 0; for (size_t i = 0; i < size; ++i) { int lit = g->rhs[i]; @@ -4707,9 +4807,6 @@ void Closure::simplify_xor_gate (Gate *g) { 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); @@ -4797,10 +4894,6 @@ size_t Closure::propagate_units_and_equivalences () { 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); @@ -4857,7 +4950,7 @@ void Closure::reset_closure () { for (auto c : extra_clauses) { CADICAL_assert (!c->garbage); internal->proof->delete_clause (c); - delete c; + delete[] c; } extra_clauses.clear (); } else { @@ -4869,40 +4962,12 @@ void Closure::reset_extraction () { full_watching = true; if (!internal->unsat && !internal->propagate ()) { internal->learn_empty_clause (); + return; } -#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 () { @@ -4910,34 +4975,42 @@ void Closure::forward_subsume_matching_clauses () { reset_closure (); std::vector matchable; matchable.resize (internal->max_var + 1); +#ifndef CADICAL_QUIET size_t count_matchable = 0; - +#endif for (auto idx : internal->vars) { - if (!internal->flags (idx).active ()) + if (!internal->flags (idx).active ()) { + (void) find_representative_and_compress_no_proofs (idx); continue; + } const int lit = idx; - const int repr = find_representative (lit); + const int repr = find_representative_and_compress_no_proofs (lit); if (lit == repr) continue; - const int repr_idx = abs (repr); + const int repr_idx = internal->vidx (repr); if (!matchable[idx]) { LOG ("matchable %d", idx); matchable[idx] = true; - count_matchable++; +#ifndef CADICAL_QUIET + ++count_matchable; +#endif } if (!matchable[repr_idx]) { LOG ("matchable %d", repr_idx); matchable[repr_idx] = true; - count_matchable++; +#ifndef CADICAL_QUIET + ++count_matchable; +#endif } } - LOG ("found %.0f%%", - (double) count_matchable / - (double) (internal->max_var ? internal->max_var : 1)); - std::vector candidates; + VERBOSE (3, "congruence found %zu matchable variables %.0f%%", + count_matchable, percent (count_matchable, internal->max_var)); + std::vector candidates; auto &analyzed = internal->analyzed; + size_t potential = 0; + (void) potential; for (auto *c : internal->clauses) { if (c->garbage) @@ -4946,6 +5019,7 @@ void Closure::forward_subsume_matching_clauses () { continue; if (c->size == 2) continue; + ++potential; CADICAL_assert (analyzed.empty ()); bool contains_matchable = false; for (auto lit : *c) { @@ -4963,7 +5037,7 @@ void Closure::forward_subsume_matching_clauses () { contains_matchable = true; } - const int repr = find_representative (lit); + const int repr = find_representative_and_compress_no_proofs (lit); CADICAL_assert (!internal->val (repr)); if (marked (repr)) continue; @@ -4990,19 +5064,30 @@ void Closure::forward_subsume_matching_clauses () { candidates.push_back (c); } + VERBOSE (3, + "[congruence-%" PRId64 "] considering %zu " + "matchable subsumption candidates out of %zu %.0f%%", + internal->stats.congruence.rounds, candidates.size (), potential, + percent (candidates.size (), potential)); + rsort (begin (candidates), end (candidates), smaller_clause_size_rank ()); size_t tried = 0, subsumed = 0; + (void) tried, (void) subsumed; internal->init_occs (); for (auto c : candidates) { - CADICAL_assert (c->size != 2); - // TODO if terminated + CADICAL_assert (c.size != 2); + if (internal->terminated_asynchronously ()) + break; ++tried; - if (find_subsuming_clause (c)) { + if (find_subsuming_clause (c.clause)) { ++subsumed; } } - LOG ("[congruence] subsumed %.0f%%", - (double) subsumed / (double) (tried ? tried : 1)); + VERBOSE (3, + "[congruence-%" PRId64 "] subsumed %zu " + "clauses out of %zu tried %.0f%% clauses", + internal->stats.congruence.rounds, subsumed, tried, + percent (subsumed, tried)); STOP (congruencematching); } @@ -5046,7 +5131,7 @@ bool Closure::find_subsuming_clause (Clause *subsumed) { Clause *subsuming = nullptr; for (auto lit : *subsumed) { CADICAL_assert (internal->val (lit) <= 0); - const int repr_lit = find_representative (lit); + const int repr_lit = find_representative_already_compressed (lit); const signed char repr_val = internal->val (repr_lit); CADICAL_assert (repr_val <= 0); if (repr_val < 0) @@ -5061,14 +5146,14 @@ bool Closure::find_subsuming_clause (Clause *subsumed) { 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 (); + const int repr_lit = find_representative_already_compressed (lit); + const size_t count = internal->occs (repr_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)) { + for (auto d : internal->occs (repr_lit)) { CADICAL_assert (!d->garbage); CADICAL_assert (subsumed != d); if (!subsumed->redundant && d->redundant) @@ -5078,7 +5163,8 @@ bool Closure::find_subsuming_clause (Clause *subsumed) { if (v < 0) continue; CADICAL_assert (!v); - const int repr_other = find_representative (other); + const int repr_other = + find_representative_already_compressed (other); if (!marked (repr_other)) goto CONTINUE_WITH_NEXT_CLAUSE; LOG ("subsuming due to %d -> %d", other, repr_other); @@ -5093,7 +5179,7 @@ bool Closure::find_subsuming_clause (Clause *subsumed) { FOUND_SUBSUMING: for (auto lit : *subsumed) { - const int repr_lit = find_representative (lit); + const int repr_lit = find_representative_already_compressed (lit); const signed char v = internal->val (lit); if (!v) marked (repr_lit) = 0; @@ -5126,28 +5212,30 @@ void Closure::produce_ite_merge_then_else_reasons ( 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]) + 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); + check_ite_lrat_reasons (g); + CADICAL_assert (g->arity () == 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; + const int8_t flag = g->degenerated_gate; 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); + LOG (g->pos_lhs_ids[1].clause, "1:"); + LOG (g->pos_lhs_ids[2].clause, "2:"); + reasons_back.push_back (g->pos_lhs_ids[1].clause->id); + reasons_implication.push_back (g->pos_lhs_ids[2].clause->id); return; } if (ite_flags_cond_lhs (flag)) { @@ -5170,21 +5258,28 @@ void Closure::rewrite_ite_gate_update_lrat_reasons (Gate *g, int src, return; LOG (g, "updating lrat from"); for (auto &litId : g->pos_lhs_ids) { - CADICAL_assert (litId.clause); + CADICAL_assert (litId.clause || g->degenerated_gate != Special_Gate::NORMAL); if (litId.current_lit == src) litId.current_lit = dst; if (litId.current_lit == -src) litId.current_lit = -dst; } - check_ite_lrat_reasons (g, false); + check_ite_lrat_reasons (g); } +// Transforms an ITE gate to an AND gate +// +// In essence the transformation is simple for LRAT: two long clause need to +// be resolved to get a binary and one long clause stays the long ternary +// clause for the resulting gate. There are two things to be careful: we +// have to rewrite the LHS too, because if it appears on the RHS too, it +// will not get rewritten and the proof will not work. 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"); + LOG (g, "rewriting to proper AND gate, namely"); if (internal->val (g->lhs) > 0) { { const int lit = g->rhs[0]; @@ -5234,12 +5329,59 @@ bool Closure::rewrite_ite_gate_to_and ( } return true; } + if (src == g->lhs) + g->lhs = dst; + else if (src == -g->lhs) + g->lhs = -dst; 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); + LOG (g, "updating flags for"); + + if (g->rhs[1] == -g->lhs || g->rhs[0] == -g->lhs) + g->degenerated_gate = Special_Gate::DEGENERATED_AND; + + if (g->degenerated_gate == Special_Gate::DEGENERATED_AND || + g->degenerated_gate == Special_Gate::DEGENERATED_AND_LHS_FALSE) { + LOG ("degenerated AND gate"); + CADICAL_assert (g->rhs[1] == -g->lhs || g->rhs[0] == -g->lhs); + if (g->rhs[1] == -g->lhs) { + if (g->rhs[0] == -g->lhs) { + // -2 := ITE 1 2 2: we need + // we need 2 3 + LitClausePair &p1 = g->pos_lhs_ids[2]; + LitClausePair &p2 = g->pos_lhs_ids[3]; + p1.clause = produce_rewritten_clause_lrat (p1.clause); + p2.clause = produce_rewritten_clause_lrat (p2.clause); + CADICAL_assert (p1.clause); + CADICAL_assert (p2.clause); + lrat_chain.push_back (p1.clause->id); + lrat_chain.push_back (p2.clause->id); + // push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[0].clause, + // Rewrite (), lrat_chain); + } else { + // -1 := ITE -4 1 -1 we need 3 + // -2 := ITE 1 2 2 we need 2 + LOG ("g->rhs[0] != -g->lhs"); + const int idx = (g->lhs == g->rhs[2]) ? 3 : 2; + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[idx].clause, + Rewrite (), lrat_chain); + } + learn_congruence_unit (-g->lhs); + return true; + } + if (g->rhs[0] == -g->lhs) { + LOG ("g->rhs[0] == -g->lhs"); + Clause *c = + g->lhs > 0 ? g->pos_lhs_ids[1].clause : g->pos_lhs_ids[0].clause; + push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain); + learn_congruence_unit (-g->lhs); + return true; + } + } + if (g->rhs[0] == g->lhs || g->rhs[1] == g->lhs) + g->degenerated_gate = Special_Gate::DEGENERATED_AND; + + CADICAL_assert (g->arity () == 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 ()); @@ -5286,7 +5428,7 @@ bool Closure::rewrite_ite_gate_to_and ( [] (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->neg_lhs_ids = *long_clause; g->pos_lhs_ids.erase (long_clause); CADICAL_assert (g->pos_lhs_ids.size () == 1); @@ -5318,6 +5460,8 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( std::vector &reasons_back, std::vector &reasons_unit, bool rewritting_then, bool &learn_units) { + if (!internal->proof) + return; const size_t idx1 = rewritting_then ? 0 : 2; const size_t idx2 = idx1 + 1; const size_t other_idx1 = rewritting_then ? 2 : 0; @@ -5328,10 +5472,7 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( 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; - + const int repr_lhs = find_eager_representative (g->lhs); LOG ("cond: %d, merging %d and rewriting to %d", cond_lit, lit_to_merge, other_lit); @@ -5342,7 +5483,8 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( 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 + 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"); @@ -5460,7 +5602,6 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( } 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 ( @@ -5531,7 +5672,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { return; LOG (g, "rewriting %d by %d in", src, dst); CADICAL_assert (!g->shrunken); - CADICAL_assert (g->rhs.size () == 3); + CADICAL_assert (g->arity () == 3); CADICAL_assert (!internal->lrat || g->pos_lhs_ids.size () == 4); auto &rhs = g->rhs; const int lhs = g->lhs; @@ -5579,15 +5720,15 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { // !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); + rhs[0] = then_lit; + rhs[1] = else_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); + rhs[0] = then_lit; + rhs[1] = else_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 @@ -5627,11 +5768,13 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { 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 (); + 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)) { + if (merge_literals (g->lhs, else_lit, reasons_implication, + reasons_back)) { ++internal->stats.congruence.unaries; ++internal->stats.congruence.unary_ites; } @@ -5669,8 +5812,8 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { 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)) { + if (merge_literals (lhs, else_lit, reasons_implication, + reasons_back)) { ++internal->stats.congruence.unaries; ++internal->stats.congruence.unary_ites; } @@ -5691,7 +5834,8 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { learn_congruence_unit (-else_lit); garbage = true; } else { - LOG ("changing to xor"); + LOG ("changing to xor %s = %s ^ %s", LOGLIT (g->lhs), LOGLIT (cond), + LOGLIT (else_lit)); new_tag = Gate_Type::XOr_Gate; CADICAL_assert (rhs[0] == cond); rhs[1] = else_lit; @@ -5703,7 +5847,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { } #endif produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs, - true); + !true, true); #ifdef LOGGING for (auto litId : g->pos_lhs_ids) { LOG (litId.clause, "%d ->", litId.current_lit); @@ -5730,14 +5874,17 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { lrat_chain = reasons_unit; learn_congruence_unit (cond, true); if (then_lit != lhs) { - LOG ("special case, learning %d",cond == -lhs ? -then_lit : then_lit); + 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 (); + 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)) { + if (merge_literals (lhs, then_lit, reasons_implication, + reasons_back)) { ++internal->stats.congruence.unaries; ++internal->stats.congruence.unary_ites; } @@ -5775,8 +5922,8 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { 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)) { + if (merge_literals (lhs, then_lit, reasons_implication, + reasons_back)) { ++internal->stats.congruence.unaries; ++internal->stats.congruence.unary_ites; } @@ -5785,10 +5932,15 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { // cond ? then_lit : !then_lit // cond & then_lit | !cond & !then_lit // !(cond ^ then_lit) + // + // For the clause 11 = 11 ? 9 : 8, we have empty clauses. The flag is + // only useful for debugging + const bool allow_empty_lrat_clause = + g->degenerated_gate != Special_Gate::NORMAL; if (lhs == cond) { - produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, - false); if (internal->lrat) { + produce_rewritten_clause_lrat_and_clean ( + g->pos_lhs_ids, not_lhs, false, allow_empty_lrat_clause); 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); @@ -5796,9 +5948,9 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { 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) { + produce_rewritten_clause_lrat_and_clean ( + g->pos_lhs_ids, not_lhs, false, allow_empty_lrat_clause); 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); @@ -5806,9 +5958,9 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { 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) { + produce_rewritten_clause_lrat_and_clean ( + g->pos_lhs_ids, not_lhs, false, allow_empty_lrat_clause); 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); @@ -5848,9 +6000,8 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { 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 + // unlike kissat, we need to do it after negating + if (internal->vlit (rhs[0]) > internal->vlit (rhs[1])) { std::swap (rhs[0], rhs[1]); CADICAL_assert (new_tag != Gate_Type::ITE_Gate); } @@ -5916,26 +6067,35 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { 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]); + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, + g->lhs); + CADICAL_assert ( + !internal->lrat || g->pos_lhs_ids.size () == 2 || + (g->arity () == 1 && find_representative (g->lhs) == + find_representative (g->rhs[0]))); + if (g->arity () == 1) // can happen when there are units in the + // gate that are not simplified yet + garbage = true; + else { + 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; } - merge_literals_equivalence (g->lhs, g->rhs[0], c1, c2); - garbage = true; } } if (!garbage) { @@ -5947,7 +6107,6 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { } if (!garbage) { - g->hash = hash_lits (nonces, g->rhs); LOG (g, "rewritten"); if (internal->lrat) { @@ -5971,8 +6130,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { #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->neg_lhs_ids () || gate_contains (g, g->lhs)); CADICAL_assert (g->arity () == 2); #ifndef CADICAL_NDEBUG std::for_each ( @@ -6004,8 +6162,8 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { 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)) + if (merge_literals (g->lhs, h->lhs, reasons_implication, + reasons_back)) ++internal->stats.congruence.xors; } else { add_ite_turned_and_binary_clauses (g); @@ -6013,8 +6171,8 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { 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)) + if (merge_literals (g, h, g->lhs, h->lhs, reasons_implication, + reasons_back)) ++internal->stats.congruence.ands; } delete_proof_chain (); @@ -6036,7 +6194,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { } else { LOG (g, "rewritten"); if (internal->lrat) - update_ite_flags (g), check_correct_ite_flags(g); + 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]); @@ -6044,7 +6202,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { CADICAL_assert (rhs[0] != -(rhs[2])); CADICAL_assert (rhs[1] != -(rhs[2])); check_ite_gate_implied (g); - check_ite_lrat_reasons (g, false); + check_ite_lrat_reasons (g); bool negate_lhs; Gate *h = find_ite_gate (g, negate_lhs); CADICAL_assert (lhs == g->lhs); @@ -6053,20 +6211,23 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { g->lhs = -lhs; check_ite_lrat_reasons (g); if (internal->lrat) - check_correct_ite_flags (g); + check_correct_ite_flags (g); if (h) { garbage = true; check_ite_gate_implied (g); - check_ite_lrat_reasons (g, false); + check_ite_lrat_reasons (g); check_ite_gate_implied (h); - check_ite_lrat_reasons (h, false); + check_ite_lrat_reasons (h); 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)) + // LHS can change for degenerated gates + if (g->lhs != normalized_lhs) + normalized_lhs = find_eager_representative (normalized_lhs); + if (merge_literals (g, h, normalized_lhs, h->lhs, extra_reasons_lit, + extra_reasons_ulit)) ++internal->stats.congruence.ites; delete_proof_chain (); CADICAL_assert (internal->unsat || chain.empty ()); @@ -6075,7 +6236,6 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { 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) @@ -6089,6 +6249,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { mark_garbage (g); CADICAL_assert (chain.empty ()); + ++internal->stats.congruence.rewritten_ites; } void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, @@ -6096,7 +6257,6 @@ void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, size_t idx2) { if (!internal->lrat) return; - // TODO if (internal->val (lit) > 0) return; CADICAL_assert (internal->lrat); @@ -6110,9 +6270,11 @@ void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, 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"); + 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); + 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, @@ -6121,9 +6283,11 @@ void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, return; } if (g->lhs == g->rhs[0]) { - LOG ("special case of LHS=cond where only one clause in LRAT is needed is needed"); + 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); + 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, @@ -6153,7 +6317,6 @@ void Closure::merge_and_gate_lrat_produce_lrat ( 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); } @@ -6162,7 +6325,12 @@ void Closure::merge_and_gate_lrat_produce_lrat ( 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); + CADICAL_assert (g->arity () == 3); + LOG ("transforming to gate[%" PRIu64 + "] %s = %s & %s with idx1=%zd, idx2=%zd", + g->id, LOGLIT (g->lhs), LOGLIT (g->rhs[0]), LOGLIT (g->rhs[1]), idx1, + idx2); + const int8_t orig_flag = g->degenerated_gate; #ifdef LOGGING for (auto litId : g->pos_lhs_ids) { LOG (litId.clause, "%d ->", litId.current_lit); @@ -6179,9 +6347,25 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, } 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); + if (g->rhs[1] == -g->lhs || g->rhs[0] == -g->lhs) + g->degenerated_gate = Special_Gate::DEGENERATED_AND; + else if (g->rhs[0] == g->lhs || g->rhs[1] == g->lhs) + g->degenerated_gate = Special_Gate::DEGENERATED_AND; + else + g->degenerated_gate = Special_Gate::NORMAL; + + if (g->lhs == -removed_lit && internal->val (-removed_lit)) { + // 3 = 5 ? 1 : 3 where 3@0 = -1 + g->degenerated_gate = Special_Gate::DEGENERATED_AND_LHS_FALSE; + size_t new_idx1 = idx1; + size_t new_idx2 = idx2; + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, 0, new_idx1, + new_idx2, true); + CADICAL_assert (g->pos_lhs_ids.size () == 1); + CADICAL_assert (g->pos_lhs_ids[0].clause->size == 2); + return false; + } CADICAL_assert (g->pos_lhs_ids.size () == 4); CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); @@ -6193,7 +6377,9 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t 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]); + CADICAL_assert (g->degenerated_gate != Special_Gate::NORMAL); + 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); @@ -6203,21 +6389,53 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, 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 (*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; } + LOG ("normal AND gate with clauses %zd (was: %zd) %zd (was: %zd)", + new_idx1, idx1, new_idx2, idx2); + Clause *c = nullptr; + if ((orig_flag == Special_Gate::NO_ELSE || + orig_flag == Special_Gate::NO_THEN) && + (find_eager_representative (g->lhs) == + find_eager_representative (g->rhs[0]) || + find_eager_representative (g->lhs) == + find_eager_representative (g->rhs[1]))) { + g->degenerated_gate = Special_Gate::DEGENERATED_AND; + LOG (g, "marking as degenerated"); + CADICAL_assert (find_eager_representative (g->lhs) == g->rhs[0] || + find_eager_representative (g->lhs) == g->rhs[1]); + Clause *d = g->pos_lhs_ids[new_idx1].clause; + // TODO there should be a better way than dereferencing the clause even + // if it is cheap as we have just written the proof out + CADICAL_assert (-g->lhs == d->literals[0] || -g->lhs == d->literals[1]); + int lit = d->literals[0] ^ d->literals[1] ^ -g->lhs; + LOG ("%d -> %d", g->lhs, find_eager_representative (g->lhs)); + LOG ("%d -> %d", d->literals[0], + find_eager_representative (d->literals[0])); + LOG ("%d -> %d", d->literals[1], + find_eager_representative (d->literals[1])); + LOG (d, "with reference %s at position %zd", LOGLIT (lit), new_idx1); + CADICAL_assert (d->size == 2); + g->pos_lhs_ids.clear (); + g->pos_lhs_ids.push_back ({lit, d}); + g->neg_lhs_ids.reset (); + // TODO should be before lrat test + g->lhs = find_eager_representative (g->lhs); + 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; + c = g->pos_lhs_ids[new_idx1].clause; CADICAL_assert (c->size == 2); + CADICAL_assert (c); + CADICAL_assert (new_idx2 < g->pos_lhs_ids.size ()); 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), @@ -6225,11 +6443,13 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, return p.clause == d; }), end (g->pos_lhs_ids)); - CADICAL_assert (g->pos_lhs_ids.size () == 2); + CADICAL_assert (g->pos_lhs_ids.size () == 2 || + (g->degenerated_gate == Special_Gate::DEGENERATED_AND)); CADICAL_assert (lit); CADICAL_assert (other); CADICAL_assert (lit != other); - lrat_chain.push_back (c->id); + if (c) + lrat_chain.push_back (c->id); lrat_chain.push_back (d->id); Clause *e = add_tmp_binary_clause (lit, -other); @@ -6238,7 +6458,7 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, [] (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->neg_lhs_ids = *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}); @@ -6268,12 +6488,13 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, 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)); } + CADICAL_assert (g->pos_lhs_ids.size () == 2 || + g->degenerated_gate != Special_Gate::NORMAL); return false; } @@ -6334,10 +6555,17 @@ void Closure::simplify_ite_gate_condition_set ( 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); + if (c) + push_id_and_rewriting_lrat_unit (c, Rewrite (), reasons_lrat, true, + Rewrite (), -g->lhs); + else + CADICAL_assert (g->degenerated_gate != Special_Gate::NORMAL); + + if (d) + push_id_and_rewriting_lrat_unit (d, Rewrite (), reasons_back_lrat, true, + Rewrite (), g->lhs); + else + CADICAL_assert (g->degenerated_gate != Special_Gate::NORMAL); } void Closure::simplify_ite_gate (Gate *g) { @@ -6363,8 +6591,7 @@ void Closure::simplify_ite_gate (Gate *g) { 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)) { + if (merge_literals (lhs, then_lit, reasons_lrat, reasons_back_lrat)) { ++internal->stats.congruence.unary_ites; ++internal->stats.congruence.unaries; } @@ -6372,8 +6599,7 @@ void Closure::simplify_ite_gate (Gate *g) { 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)) { + if (merge_literals (lhs, else_lit, reasons_lrat, reasons_back_lrat)) { ++internal->stats.congruence.unary_ites; ++internal->stats.congruence.unaries; } @@ -6388,20 +6614,24 @@ void Closure::simplify_ite_gate (Gate *g) { 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) + // if the gate is a = a ? true : false, there is nothing to do and the + // lrat generation fails + if (internal->lrat && find_eager_representative (lhs) != + find_eager_representative (cond)) 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)) { + if (merge_literals (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) + // if the gate is a = -a ? false : true, there is nothing to do and + // the lrat generation fails + if (internal->lrat && find_eager_representative (lhs) != + find_eager_representative (-cond)) 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)) { + if (merge_literals (lhs, -cond, extra_reasons_back, extra_reasons)) { ++internal->stats.congruence.unary_ites; ++internal->stats.congruence.unaries; } @@ -6438,7 +6668,6 @@ void Closure::simplify_ite_gate (Gate *g) { 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) { @@ -6446,16 +6675,15 @@ void Closure::simplify_ite_gate (Gate *g) { 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)) { + reasons_lrat_back, true); + if (merge_literals (g, h, 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); @@ -6477,23 +6705,34 @@ void Closure::simplify_ite_gate (Gate *g) { void Closure::add_ite_matching_proof_chain ( Gate *g, Gate *h, int lhs1, int lhs2, std::vector &reasons1, std::vector &reasons2) { + if (!internal->proof) + return; check_ite_lrat_reasons (g); - check_ite_lrat_reasons (h, false); + check_ite_lrat_reasons (h); 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"); + if (g->degenerated_gate != Special_Gate::NORMAL) { + g->lhs = find_eager_representative (g->lhs); + lhs1 = find_eager_representative (lhs1); + LOG (g, "rewritten LHS of g"); + } + LOG (h, "matching ITE proof chain with"); + if (h->degenerated_gate != Special_Gate::NORMAL) { + h->lhs = find_eager_representative (h->lhs); + lhs2 = find_eager_representative (lhs2); + LOG (g, "rewritten LHS of h"); + } + LOG ("producing ITE gates %d %d", lhs1, lhs2); 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 int8_t flags_g = g->degenerated_gate; + const int8_t flags_h = h->degenerated_gate; 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; @@ -6528,25 +6767,33 @@ void Closure::add_ite_matching_proof_chain ( 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; + 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; + 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; + 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; + 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; @@ -6554,28 +6801,35 @@ void Closure::add_ite_matching_proof_chain ( 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; + 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; + 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; + 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; + 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) { @@ -6584,8 +6838,10 @@ void Closure::add_ite_matching_proof_chain ( unsimplified.push_back (lhs2); LRAT_ID id1 = 0; if (internal->lrat) { + CADICAL_assert (g_then_id); lrat_chain.push_back (g_then_id); - lrat_chain.push_back (h_neg_then_id); + if (h_neg_then_id) + lrat_chain.push_back (h_neg_then_id); } id1 = simplify_and_add_to_proof_chain (unsimplified); @@ -6595,7 +6851,8 @@ void Closure::add_ite_matching_proof_chain ( LRAT_ID id2 = 0; if (internal->lrat) { lrat_chain.push_back (g_neg_else_id); - lrat_chain.push_back (h_else_id); + if (h_else_id) + lrat_chain.push_back (h_else_id); } id2 = simplify_and_add_to_proof_chain (unsimplified); @@ -6614,7 +6871,8 @@ void Closure::add_ite_matching_proof_chain ( LRAT_ID id1 = 0; if (internal->lrat) { lrat_chain.push_back (h_then_id); - lrat_chain.push_back (g_neg_then_id); + if (g_neg_then_id) + lrat_chain.push_back (g_neg_then_id); } id1 = simplify_and_add_to_proof_chain (unsimplified); @@ -6624,7 +6882,8 @@ void Closure::add_ite_matching_proof_chain ( LRAT_ID id2 = 0; if (internal->lrat) { lrat_chain.push_back (h_neg_else_id); - lrat_chain.push_back (g_else_id); + if (g_else_id) + lrat_chain.push_back (g_else_id); } id2 = simplify_and_add_to_proof_chain (unsimplified); @@ -6642,9 +6901,10 @@ void Closure::add_ite_matching_proof_chain ( 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); + CADICAL_assert (g_neg_then_id); lrat_chain.push_back (g_neg_then_id); - lrat_chain.push_back (h_then_id); + if (h_then_id) + lrat_chain.push_back (h_then_id); } id1 = simplify_and_add_to_proof_chain (unsimplified); @@ -6654,8 +6914,10 @@ void Closure::add_ite_matching_proof_chain ( LRAT_ID id2 = -1; if (internal->lrat) { + CADICAL_assert (g_else_id); lrat_chain.push_back (g_else_id); - lrat_chain.push_back (h_neg_else_id); + if (h_neg_else_id) + lrat_chain.push_back (h_neg_else_id); } id2 = simplify_and_add_to_proof_chain (unsimplified); CADICAL_assert (!internal->lrat || id1); @@ -6668,34 +6930,37 @@ void Closure::add_ite_matching_proof_chain ( if (degenerated_h_not_cond) { LOG ("special case: cond = -lhs, h degenerated"); - unsimplified.push_back (lhs1); - unsimplified.push_back (-lhs2); + 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); + CADICAL_assert (h_neg_then_id); lrat_chain.push_back (h_neg_then_id); - lrat_chain.push_back (g_then_id); + if (g_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); + unsimplified.push_back (lhs1); + unsimplified.push_back (-lhs2); LRAT_ID id2 = -1; if (internal->lrat) { + CADICAL_assert (h_else_id); lrat_chain.push_back (h_else_id); - lrat_chain.push_back (g_neg_else_id); + if (g_neg_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); + reasons1.push_back (id1); + reasons2.push_back (id2); unsimplified.clear (); return; } - + LOG ("normal path"); CADICAL_assert (!internal->lrat || degenerated_g_then || (g_then_id && g_neg_then_id)); @@ -6796,15 +7061,15 @@ Gate *Closure::new_ite_gate (int lhs, int cond, int then_lit, int else_lit, std::vector reasons_implication, reasons_back; if (internal->lrat) { merge_ite_gate_same_then_else_lrat (clauses, reasons_implication, - reasons_back); + reasons_back); } - if (merge_literals_lrat (lhs, then_lit, reasons_implication, - reasons_back)) + if (merge_literals (lhs, then_lit, reasons_implication, reasons_back)) ++internal->stats.congruence.trivial_ite; return 0; } rhs.clear (); + rhs.reserve (3); rhs.push_back (cond); rhs.push_back (then_lit); rhs.push_back (else_lit); @@ -6825,15 +7090,15 @@ Gate *Closure::new_ite_gate (int lhs, int cond, int then_lit, int else_lit, g->lhs = lhs; check_ite_gate_implied (g); check_ite_lrat_reasons ( - g, false); // due to merges done before during AND gate detection! + g); // 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)) { + if (merge_literals (h, g, h->lhs, g->lhs, extra_reasons_lit, + extra_reasons_ulit)) { ++internal->stats.congruence.ites; LOG ("found merged literals"); } @@ -6846,7 +7111,6 @@ Gate *Closure::new_ite_gate (int lhs, int cond, int then_lit, int else_lit, 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 @@ -6861,6 +7125,7 @@ Gate *Closure::new_ite_gate (int lhs, int cond, int then_lit, int else_lit, } } check_ite_lrat_reasons (g); + ++internal->stats.congruence.ite_gates; return g; } @@ -6915,17 +7180,17 @@ void Closure::check_contained_module_rewriting (Clause *c, int lit, #endif } -void Closure::check_ite_lrat_reasons (Gate *g, bool normalized) { +void Closure::check_ite_lrat_reasons (Gate *g) { #ifndef CADICAL_NDEBUG CADICAL_assert (g->tag == Gate_Type::ITE_Gate); if (!internal->lrat) return; - CADICAL_assert (g->rhs.size () == 3); + CADICAL_assert (g->arity () == 3); CADICAL_assert (is_tautological_ite_gate (g) || g->pos_lhs_ids.size () == 4); - CADICAL_assert (g->neg_lhs_ids.empty ()); + CADICAL_assert (!g->neg_lhs_ids ()); CADICAL_assert (g->pos_lhs_ids.size () == 4); #else - (void) g, (void) normalized; + (void) g; #endif } @@ -6975,6 +7240,15 @@ void Closure::init_ite_gate_extraction ( CONTINUE_COUNTING_NEXT_CLAUSE:; } + VERBOSE (4, + "counted %zu ternary ITE gates" + "(%.0f%% of %" PRIu64 " irredundant clauses)", + ternary.size (), + percent (ternary.size (), internal->stats.current.irredundant), + internal->stats.current.irredundant); +#ifndef CADICAL_QUIET + size_t connected = 0; +#endif for (auto c : ternary) { CADICAL_assert (!c->garbage); CADICAL_assert (!c->redundant); @@ -6997,6 +7271,9 @@ void Closure::init_ite_gate_extraction ( if (twice < 2) goto CONTINUE_WITH_NEXT_TERNARY_CLAUSE; CADICAL_assert (c->size != 2); +#ifndef CADICAL_QUIET + connected++; +#endif for (auto lit : *c) internal->occs (lit).push_back (c); if (positive && negative) @@ -7005,6 +7282,19 @@ void Closure::init_ite_gate_extraction ( } ternary.clear (); + + VERBOSE ( + 4, "connected %zu large clauses %.0f%%", candidates.size (), + percent (candidates.size (), internal->stats.current.irredundant)); + +#ifndef CADICAL_QUIET + size_t size_candidates = candidates.size (); + VERBOSE (4, + "%zu candidates ITE base clauses " + "(%.0f%% of %zu connected)", + size_candidates, percent (size_candidates, connected), + connected); +#endif } void Closure::reset_ite_gate_extraction () { @@ -7032,6 +7322,11 @@ void Closure::copy_conditional_equivalences (int lit, else { CADICAL_assert (!second); second = other; +#ifdef CADICAL_NDEBUG + // all other literals are true, so we can stop here + // otherwise, one clause does not have length 3 (ignoring unit) + break; +#endif } } CADICAL_assert (first), CADICAL_assert (second); @@ -7078,8 +7373,30 @@ struct litpair_smaller { 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); + const uint64_t s = litpair_rank (internal) (a); + const uint64_t t = litpair_rank (internal) (b); + return s < t; + } +}; + +struct litequivalence_rank { + CaDiCaL::Internal *internal; + litequivalence_rank (Internal *i) : internal (i) {} + typedef uint64_t Type; + Type operator() (const lit_equivalence &a) const { + uint64_t lita = internal->vlit (a.first); + uint64_t litb = internal->vlit (a.second); + return (lita << 32) + litb; + } +}; + +struct litequivalence_smaller { + CaDiCaL::Internal *internal; + litequivalence_smaller (Internal *i) : internal (i) {} + bool operator() (const lit_equivalence &a, + const lit_equivalence &b) const { + const uint64_t s = litequivalence_rank (internal) (a); + const uint64_t t = litequivalence_rank (internal) (b); return s < t; } }; @@ -7088,19 +7405,16 @@ 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; + [this] (const lit_implication &a, const lit_implication &b) { + return internal->vlit (a.second) < internal->vlit (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; + [this] (const lit_implication &a, const lit_implication &b) { + return internal->vlit (a.second) < internal->vlit (b.second); }); #endif @@ -7178,11 +7492,6 @@ void Closure::extract_condeq_pairs (int lit, lit_implications &condbin, 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) @@ -7249,8 +7558,8 @@ void Closure::extract_condeq_pairs (int lit, lit_implications &condbin, 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)); + "with positive %s in %zu conditional binary clauses with %s", + pos_size, LOGLIT (pos_lit), neg_size, LOGLIT (neg_lit)); search_condeq (lit, pos_lit, pos_begin, pos_end, neg_lit, neg_begin, neg_end, condeq); } else { @@ -7289,8 +7598,9 @@ void Closure::find_conditional_equivalences (int lit, 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)); + MSORT (internal->opts.radixsortlim, begin (condeq), end (condeq), + litequivalence_rank (this->internal), + litequivalence_smaller (this->internal)); #ifdef LOGGING for (auto pair : condeq) @@ -7309,15 +7619,20 @@ void Closure::merge_condeq (int cond, lit_equivalences &condeq, for (auto p : condeq) { const int lhs = p.first; const int then_lit = p.second; +#ifndef CADICAL_NDEBUG + for (auto q2 = begin (not_condeq); q2 != q; ++q2) + CADICAL_assert (q2->first < lhs); if (internal->lrat) p.check_invariant (); +#endif CADICAL_assert (lhs > 0); while (q != end_not_condeq && q->first < lhs) ++q; - while (q != end_not_condeq && q->first == lhs) { + auto q2 = q; + while (q2 != end_not_condeq && q2->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 lit_equivalence not_cond_pair = *q2++; const int else_lit = not_cond_pair.second; std::vector clauses; if (internal->lrat) { @@ -7335,8 +7650,7 @@ void Closure::merge_condeq (int cond, lit_equivalences &condeq, 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 (); + not_cond_pair.check_invariant (); } new_ite_gate (lhs, cond, then_lit, else_lit, std::move (clauses)); if (internal->unsat) @@ -7387,7 +7701,9 @@ void Closure::extract_ite_gates () { return; START (extractites); std::vector candidates; - +#ifndef CADICAL_QUIET + const int64_t gates_before = internal->stats.congruence.ite_gates; +#endif init_ite_gate_extraction (candidates); for (auto idx : internal->vars) { @@ -7398,6 +7714,9 @@ void Closure::extract_ite_gates () { } } // Kissat has an alternative version MERGE_CONDITIONAL_EQUIVALENCES + VERBOSE (2, "[congruence-%" PRId64 "] found %" PRId64 " ITE clauses", + internal->stats.congruence.rounds, + internal->stats.congruence.ite_gates - gates_before); reset_ite_gate_extraction (); STOP (extractites); } @@ -7412,6 +7731,17 @@ void Closure::extract_gates () { STOP (extract); return; } + + if (internal->lrat) { // save some memory + mu2_ids.clear (); + shrink_vector (mu2_ids); + mu4_ids.clear (); + shrink_vector (mu4_ids); + } else { + CADICAL_assert (mu2_ids.empty ()); + CADICAL_assert (mu4_ids.empty ()); + } + extract_xor_gates (); CADICAL_assert (internal->unsat || lrat_chain.empty ()); CADICAL_assert (internal->unsat || chain.empty ()); @@ -7426,7 +7756,7 @@ void Closure::extract_gates () { /*------------------------------------------------------------------------*/ // top level function to extract gate -bool Internal::extract_gates () { +bool Internal::extract_gates (bool remove_units_before_run) { if (unsat) return false; if (!opts.congruence) @@ -7437,27 +7767,35 @@ bool Internal::extract_gates () { learn_empty_clause (); return false; } - if (congruence_delay.bumpreasons.limit) { + if (congruence_delay.bumpreasons.delay ()) { 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) { + if (remove_units_before_run) { + clear_watches (); + mark_satisfied_clauses_as_garbage (); // breaks watch lists + connect_watches (); + } const bool dedup = opts.deduplicate; opts.deduplicate = true; mark_duplicated_binary_clauses_as_garbage (); opts.deduplicate = dedup; + } else if (remove_units_before_run) { + // to remove false literals from clauses + // It makes the technique stronger as long clauses + // can become binary / ternary + + // In the SC2024 there are benchmarks where 16% of variables are units + // at the beginning, so we remove units during preprocessing. Later GC + // and other techniques are doing it anyway, so we try to avoid going + // over all clauses once more. + mark_satisfied_clauses_as_garbage (); // breaks watch lists but we + // unwatch anyway afterwards + report ('.'); } ++stats.congruence.rounds; clear_watches (); @@ -7470,13 +7808,14 @@ bool Internal::extract_gates () { CADICAL_assert (unsat || closure.chain.empty ()); CADICAL_assert (unsat || lrat_chain.empty ()); closure.extract_binaries (); + const int64_t old_merged = + stats.congruence + .congruent; // the binary stuff is covered by other techniques 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) { @@ -7502,7 +7841,6 @@ bool Internal::extract_gates () { internal->connect_watches (); if (!internal->unsat) { propagated2 = propagated = 0; - propagate (); } CADICAL_assert (closure.new_unwatched_binary_clauses.empty ()); internal->reset_occs (); @@ -7512,23 +7850,22 @@ bool Internal::extract_gates () { 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; + PHASE ("congruence-phase", stats.congruence.rounds, + "merged %" PRId64 " literals", new_merged - old_merged); + if (!unsat && !internal->propagate ()) { + learn_empty_clause (); + } STOP_SIMPLIFIER (congruence, CONGRUENCE); - report ('c', !opts.reportall && !(stats.congruence.congruent - old)); + report ('c', + !opts.reportall && !(stats.congruence.congruent - old_merged)); #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); + CADICAL_assert (!w.binary () || !w.clause->garbage); if (w.clause->garbage) continue; ++watched; @@ -7549,16 +7886,11 @@ bool Internal::extract_gates () { CADICAL_assert (!internal->occurring ()); if (new_merged == old_merged) { - congruence_delay.bumpreasons.interval++; + congruence_delay.bumpreasons.bump_delay (); } else { - congruence_delay.bumpreasons.interval /= 2; + congruence_delay.bumpreasons.reduce_delay (); } - LOG ("delay congruence internal %" PRId64, - congruence_delay.bumpreasons.interval); - congruence_delay.bumpreasons.limit = - congruence_delay.bumpreasons.interval; - return new_merged != old_merged; } diff --git a/src/sat/cadical/cadical_decide.cpp b/src/sat/cadical/cadical_decide.cpp index 0874cffd7..2a8d772a2 100644 --- a/src/sat/cadical/cadical_decide.cpp +++ b/src/sat/cadical/cadical_decide.cpp @@ -41,7 +41,83 @@ int Internal::next_decision_variable_with_best_score () { return res; } +void Internal::start_random_sequence () { + if (!opts.randec) + return; + + CADICAL_assert (!stable || opts.randecstable); + CADICAL_assert (stable || opts.randecfocused); + CADICAL_assert (!randomized_deciding); + + const uint64_t count = ++stats.randec.random_decision_phases; + const unsigned length = opts.randeclength * log (count + 10); + VERBOSE (3, + "starting random decision sequence " + "at %" PRId64 " conflicts for %u conflicts", + stats.conflicts, length); + randomized_deciding = length; + + const double delta = stats.randec.random_decision_phases * + log (stats.randec.random_decision_phases); + lim.random_decision = stats.conflicts + delta * opts.randecint; + VERBOSE (3, + "next random decision sequence " + "at %" PRId64 " conflicts current conflict: %" PRId64 + " conflicts", + lim.random_decision, stats.conflicts); +} + +int Internal::next_random_decision () { + CADICAL_assert (max_var); + if (!opts.randec) + return 0; + if (stable && !opts.randecstable) + return 0; + if (!stable && !opts.randecfocused) + return 0; + if (stats.conflicts < lim.random_decision) + return 0; + if (satisfied ()) + return 0; + + if (!randomized_deciding) { + if (level > (int) assumptions.size () + !!constraint.size ()) { + LOG ("random decision delayed because too deep"); + return 0; + } + start_random_sequence (); + } + LOG ("searching for random decision"); + Random random (internal->opts.seed); + random += stats.decisions; + ++stats.randec.random_decisions; + for (;;) { + int idx = 1 + (random.next () % max_var); + LOG ("trying lit %s", LOGLIT (idx)); + /* + // Kissat filters out active literals but we cannot do that because + // eliminated variables are not actively removed. + if (!flags (idx).active()) + continue; + */ + if (val (idx)) + continue; + return idx; + } + CADICAL_assert (false); +#if defined(WIN32) && !defined(__MINGW32__) + __assume(false); +#else + __builtin_unreachable (); +#endif +} + int Internal::next_decision_variable () { + int res = next_random_decision (); + if (res) { + LOG ("randomized decision %s", LOGLIT (res)); + return res; + } if (use_scores ()) return next_decision_variable_with_best_score (); else @@ -57,16 +133,39 @@ int Internal::next_decision_variable () { int Internal::decide_phase (int idx, bool target) { const int initial_phase = opts.phase ? 1 : -1; int phase = 0; - if (force_saved_phase) + if (force_saved_phase) { phase = phases.saved[idx]; - if (!phase) + LOG ("trying force_saved_phase, i.e., %d", phase); + } + CADICAL_assert (force_saved_phase || !phase); + if (!phase) { phase = phases.forced[idx]; // swapped with opts.forcephase case! - if (!phase && opts.forcephase) + LOG ("trying forced phase, i.e., %d", phase); + } + if (!phase && opts.forcephase) { phase = initial_phase; - if (!phase && target) + LOG ("trying initial phase, i.e., %d", phase); + } + if (!phase && target) { phase = phases.target[idx]; - if (!phase) - phase = phases.saved[idx]; + } + if (!phase) { + // ported from kissat where it does not seem very useful + if (opts.stubbornIOfocused && opts.rephase == 2) + switch ((stats.rephased.total >> 1) & 7) { + case 1: + phase = initial_phase; + break; + case 5: // kissat has 3 but 5 looks better + phase = -initial_phase; + break; + default: + phase = phases.saved[idx]; + break; + } + else + phase = phases.saved[idx]; + } // The following should not be necessary and in some version we had even // a hard 'COVER' CADICAL_assertion here to check for this. Unfortunately it diff --git a/src/sat/cadical/cadical_decompose.cpp b/src/sat/cadical/cadical_decompose.cpp index 9081321b4..b925da138 100644 --- a/src/sat/cadical/cadical_decompose.cpp +++ b/src/sat/cadical/cadical_decompose.cpp @@ -473,6 +473,11 @@ bool Internal::decompose_round () { } external->push_binary_clause_on_extension_stack (id2, idx, -other); decompose_ids[vlit (idx)] = id2; + for (auto &tracer : tracers) { + const int eidx = externalize (idx); + const int eother = externalize (other); + tracer->notify_equivalence (eidx, eother); + } clause.clear (); lrat_chain.clear (); diff --git a/src/sat/cadical/cadical_drattracer.cpp b/src/sat/cadical/cadical_drattracer.cpp index eb73b4c95..badfe8e67 100644 --- a/src/sat/cadical/cadical_drattracer.cpp +++ b/src/sat/cadical/cadical_drattracer.cpp @@ -87,7 +87,7 @@ void DratTracer::drat_delete_clause (const vector &clause) { /*------------------------------------------------------------------------*/ -void DratTracer::add_derived_clause (int64_t, bool, +void DratTracer::add_derived_clause (int64_t, bool, int, const vector &clause, const vector &) { if (file->closed ()) diff --git a/src/sat/cadical/cadical_elim.cpp b/src/sat/cadical/cadical_elim.cpp index 951dfee74..60315d47e 100644 --- a/src/sat/cadical/cadical_elim.cpp +++ b/src/sat/cadical/cadical_elim.cpp @@ -24,9 +24,8 @@ namespace CaDiCaL { inline double Internal::compute_elim_score (unsigned lit) { CADICAL_assert (1 <= lit), CADICAL_assert (lit <= (unsigned) max_var); - const unsigned uidx = 2 * lit; - const double pos = internal->ntab[uidx]; - const double neg = internal->ntab[uidx + 1]; + const double pos = noccs (lit); + const double neg = noccs (-lit); if (!pos) return -neg; if (!neg) @@ -1043,7 +1042,7 @@ void Internal::elim (bool update_limits) { #endif // Make sure there was a complete subsumption phase since last - // elimination including vivification etc. + // elimination // if (last.elim.subsumephases == stats.subsumephases) subsume (); diff --git a/src/sat/cadical/cadical_elimfast.cpp b/src/sat/cadical/cadical_elimfast.cpp index 90dacf8c0..95ad070b4 100644 --- a/src/sat/cadical/cadical_elimfast.cpp +++ b/src/sat/cadical/cadical_elimfast.cpp @@ -196,17 +196,18 @@ void Internal::try_to_fasteliminate_variable (Eliminator &eliminator, // First flush garbage clauses and check limits. + const int64_t occ_bound = opts.fastelimocclim; int64_t bound = opts.fastelimbound; int64_t pos = flush_elimfast_occs (pivot); - if (pos > bound) { + int64_t neg = flush_elimfast_occs (-pivot); + if (neg && pos > occ_bound) { LOG ("too many occurrences thus not eliminated %d", pivot); CADICAL_assert (!eliminator.schedule.contains (abs (pivot))); return; } - int64_t neg = flush_elimfast_occs (-pivot); - if (neg > bound) { + if (pos && neg > occ_bound) { LOG ("too many occurrences thus not eliminated %d", -pivot); CADICAL_assert (!eliminator.schedule.contains (abs (pivot))); return; @@ -286,7 +287,6 @@ int Internal::elimfast_round (bool &completed, delta = opts.elimmineff; if (delta > opts.elimmaxeff) delta = opts.elimmaxeff; - delta = max (delta, (int64_t) 2l * active ()); PHASE ("fastelim-round", stats.elimfastrounds, "limit of %" PRId64 " resolutions", delta); diff --git a/src/sat/cadical/cadical_extend.cpp b/src/sat/cadical/cadical_extend.cpp index c549e90fa..fab83ee3c 100644 --- a/src/sat/cadical/cadical_extend.cpp +++ b/src/sat/cadical/cadical_extend.cpp @@ -22,6 +22,14 @@ void External::push_id_on_extension_stack (int64_t id) { void External::push_clause_literal_on_extension_stack (int ilit) { CADICAL_assert (ilit); const int elit = internal->externalize (ilit); + const int eidx = abs (elit); + const bool is_extension_var = ervars[eidx]; + Flags &f = internal->flags (ilit); + if (is_extension_var) { + f.factored_but_on_reconstruction_stack = true; + LOG ("marking lit %s as tainted", LOGLIT (ilit)); + } + CADICAL_assert (elit); extension.push_back (elit); LOG ("pushing clause literal %d on extension stack (internal %d)", elit, diff --git a/src/sat/cadical/cadical_external.cpp b/src/sat/cadical/cadical_external.cpp index 67d1f918d..f6d7d652f 100644 --- a/src/sat/cadical/cadical_external.cpp +++ b/src/sat/cadical/cadical_external.cpp @@ -1,6 +1,8 @@ #include "global.h" #include "internal.hpp" +#include "util.hpp" + #include ABC_NAMESPACE_IMPL_START @@ -43,6 +45,11 @@ void External::init (int new_max_var, bool extension) { if ((size_t) new_max_var >= vsize) enlarge (new_max_var); LOG ("initialized %d external variables", new_vars); + reserve_at_least (ext_units, 2 * new_max_var + 2); + reserve_at_least (e2i, new_max_var + 1); + reserve_at_least (ervars, new_max_var + 1); + reserve_at_least (ext_flags, new_max_var + 1); + reserve_at_least (internal->i2e, new_max_var + 1); if (!max_var) { CADICAL_assert (e2i.empty ()); e2i.push_back (0); @@ -74,8 +81,6 @@ void External::init (int new_max_var, bool extension) { internal->stats.variables_extension += new_vars; else internal->stats.variables_original += new_vars; - if (new_max_var >= (int64_t) is_observed.size ()) - is_observed.resize (1 + (size_t) new_max_var, false); if (internal->opts.checkfrozen) if (new_max_var >= (int64_t) moltentab.size ()) moltentab.resize (1 + (size_t) new_max_var, false); @@ -415,6 +420,8 @@ void External::remove_observed_var (int elit) { if (eidx > max_var) return; + if ((size_t) eidx <= is_observed.size ()) + return; if (is_observed[eidx]) { // Follow opposite order of add_observed_var, first remove internal // is_observed @@ -438,11 +445,11 @@ void External::reset_observed_vars () { if (!is_observed.size ()) return; - CADICAL_assert (!max_var || (size_t) max_var + 1 == is_observed.size ()); - for (auto elit : vars) { int eidx = abs (elit); CADICAL_assert (eidx <= max_var); + if ((size_t) eidx >= is_observed.size ()) + break; if (is_observed[eidx]) { int ilit = internalize (elit); internal->remove_observed_var (ilit); @@ -514,7 +521,7 @@ void External::implied (std::vector &trailed) { // (Internal does not see these marks, so no earlier filter is // possible.) - trailed.clear(); + trailed.clear (); for (const auto &ilit : ilit_implicants) { CADICAL_assert (ilit); diff --git a/src/sat/cadical/cadical_external_propagate.cpp b/src/sat/cadical/cadical_external_propagate.cpp index 215170c8b..43224d4c7 100644 --- a/src/sat/cadical/cadical_external_propagate.cpp +++ b/src/sat/cadical/cadical_external_propagate.cpp @@ -2,6 +2,8 @@ #include "internal.hpp" +#include + ABC_NAMESPACE_IMPL_START namespace CaDiCaL { @@ -99,6 +101,7 @@ void Internal::set_tainted_literal () { } void Internal::renotify_trail_after_ilb () { + CADICAL_assert (opts.ilb); if (!external_prop || external_prop_is_lazy || !trail.size () || !opts.ilb) { return; @@ -124,9 +127,57 @@ void Internal::renotify_trail_after_local_search () { renotify_full_trail (); } +void Internal::renotify_full_trail_between_trail_pos ( + int start_level, int end_level, int propagator_level, + std::vector &assigned, bool start_new_level) { + CADICAL_assert (assigned.empty ()); + int j = start_level; +#ifdef LOGGING + LOG ("starting notification of level %d from trail %d .. %d", + propagator_level, start_level, end_level); +#else + (void) propagator_level; +#endif + if (start_new_level) { + if (assigned.size ()) + external->propagator->notify_assignment (assigned); + assigned.clear (); + external->propagator->notify_new_decision_level (); + } + for (; j < end_level; ++j) { + int ilit = trail[j]; + // In theory, 0 ilit can happen due to pseudo-decision levels + if (!ilit) + continue; + + if (!observed (ilit)) + continue; + + int elit = externalize (ilit); // TODO: double-check tainting + + LOG ("notifying elit %d @ %d aka %s", propagator_level, elit, + LOGLIT (ilit)); + CADICAL_assert (elit); + // Fixed variables might get mapped (during compact) to another + // non-observed but fixed variable. + // This happens on root level, so notification about their assignment + // is already done. + CADICAL_assert (external->observed (elit) || fixed (ilit)); + if (!external->ervars[abs (elit)]) + assigned.push_back (elit); + } + + if (assigned.size ()) + external->propagator->notify_assignment (assigned); + assigned.clear (); +} + // It repeats ALL assignments of the trail, so the already notified // root-level assignments will be notified multiple times. - +// +// As CaDiCaL is missing some '0' seperators, it is important to go +// over slices from the control stack instead of going over the trail +// directly. void Internal::renotify_full_trail () { const size_t end_of_trail = trail.size (); if (level) { @@ -136,56 +187,38 @@ void Internal::renotify_full_trail () { } std::vector assigned; - int prev_max_level = 0; - int current_level = 0; int propagator_level = 0; - while (notified < end_of_trail) { - int ilit = trail[notified++]; - // In theory, 0 ilit can happen due to pseudo-decision levels - if (!ilit) - current_level = prev_max_level + 1; - else - current_level = var (ilit).level; - - if (current_level > propagator_level) { - if (assigned.size ()) - external->propagator->notify_assignment (assigned); - while (current_level > propagator_level) { - external->propagator->notify_new_decision_level (); - propagator_level++; - } - assigned.clear (); - } - // Current level can be smaller than prev_max_level due to chrono - if (current_level > prev_max_level) - prev_max_level = current_level; - - if (!observed (ilit)) - continue; - - int elit = externalize (ilit); // TODO: double-check tainting - CADICAL_assert (elit); - // Fixed variables might get mapped (during compact) to another - // non-observed but fixed variable. - // This happens on root level, so notification about their assignment is - // already done. - CADICAL_assert (external->observed (elit) || fixed (ilit)); - if (!external->ervars[abs (elit)]) - assigned.push_back (elit); + const int c_size = control.size (); + { // first all root-level literals + const int start_level = 0; + const int end_level = + (control.size () > 1 ? control[1].trail : end_of_trail); + renotify_full_trail_between_trail_pos ( + start_level, end_level, propagator_level, assigned, false); } - if (assigned.size ()) - external->propagator->notify_assignment (assigned); - assigned.clear (); - // In case there are some left over empty levels on the top of the trail, - // the external propagtor must be notified about them so the levels are - // synced - while (level > propagator_level) { - external->propagator->notify_new_decision_level (); + // notify all intermediate levels + for (int i = 2; i < c_size; ++i) { + const int start_level = control[i - 1].trail; + const int end_level = control[i].trail; propagator_level++; + LOG ("notification of %d", propagator_level); + + renotify_full_trail_between_trail_pos ( + start_level, end_level, propagator_level, assigned, true); } + // and the current level if there is non-root level one + if (level) { + const int start_level = control.back ().trail; + propagator_level++; + renotify_full_trail_between_trail_pos ( + start_level, end_of_trail, propagator_level, assigned, true); + } + CADICAL_assert (propagator_level == level); + notified = trail.size (); + return; } @@ -849,7 +882,10 @@ bool Internal::external_check_solution () { // Here the variables must be filtered by external->is_observed, // because fixed variables are internally not necessarily observed // anymore. - for (int idx = 1; idx <= external->max_var; idx++) { + for (int idx = 1; + idx <= std::min ((int) external->is_observed.size () - 1, + external->max_var); + idx++) { if (!external->is_observed[idx]) continue; const int lit = external->ival (idx); diff --git a/src/sat/cadical/cadical_factor.cpp b/src/sat/cadical/cadical_factor.cpp index 3fee40685..a87ad237d 100644 --- a/src/sat/cadical/cadical_factor.cpp +++ b/src/sat/cadical/cadical_factor.cpp @@ -540,7 +540,7 @@ void Internal::add_self_subsuming_factor (Quotient *q, Quotient *p) { CADICAL_assert (lrat_chain.size () == 2); } if (clause.size () > 1) { - new_factor_clause (); + new_factor_clause (0); } else { const int unit = clause[0]; const signed char tmp = val (unit); @@ -599,9 +599,9 @@ bool Internal::self_subsuming_factor (Quotient *q) { void Internal::add_factored_divider (Quotient *q, int fresh) { const int factor = q->factor; LOG ("factored %d divider %d", factor, fresh); - clause.push_back (factor); clause.push_back (fresh); - new_factor_clause (); + clause.push_back (factor); + new_factor_clause (fresh); clause.clear (); if (lrat) mini_chain.push_back (-clause_id); @@ -616,11 +616,12 @@ void Internal::blocked_clause (Quotient *q, int not_fresh) { int64_t new_id = ++clause_id; q->bid = new_id; CADICAL_assert (clause.empty ()); + clause.push_back (not_fresh); for (Quotient *p = q; p; p = p->prev) clause.push_back (-p->factor); - clause.push_back (not_fresh); CADICAL_assert (!lrat || mini_chain.size ()); - proof->add_derived_clause (new_id, true, clause, mini_chain); + proof->add_derived_rat_clause (new_id, true, externalize (not_fresh), + clause, mini_chain); mini_chain.clear (); clause.clear (); } @@ -654,15 +655,15 @@ void Internal::add_factored_quotient (Quotient *q, int not_fresh) { lrat_chain.push_back (q->bid); } clause.push_back (not_fresh); - new_factor_clause (); + new_factor_clause (0); clause.clear (); lrat_chain.clear (); } if (proof) { + clause.push_back (not_fresh); for (Quotient *p = q; p; p = p->prev) { clause.push_back (-p->factor); } - clause.push_back (not_fresh); proof->delete_clause (q->bid, true, clause); clause.clear (); } @@ -769,6 +770,79 @@ void Internal::schedule_factorization (Factoring &factoring) { #endif } +void Internal::adjust_scores_and_phases_of_fresh_variables ( + Factoring &factoring) { + if (!opts.factorunbump) { + factoring.fresh.clear (); + return; + } + if (factoring.fresh.empty ()) + return; + +#if 0 // the scores are very low anyway + for (auto lit : factoring.fresh) { + CADICAL_assert (lit > 0 && internal->max_var); + const double old_score = internal->stab[lit]; + // make the scores a little different from each other with the newest having the highest score + const double new_score = 1.0 / (double)(internal->max_var - lit); + if (old_score == new_score) + continue; + if (!scores.contains (lit)) + continue; + LOG ("unbumping %s", LOGLIT(lit)); + internal->stab[lit] = new_score; + scores.update (lit); + } +#endif + + for (auto lit : factoring.fresh) { + LOG ("dequeuing %s", LOGLIT (lit)); + queue.dequeue (links, lit); + } + + for (auto lit : factoring.fresh) { + LOG ("dequeuing %s", LOGLIT (lit)); + queue.bury (links, lit); + } + + // fix the scores with negative numbers + int lit = queue.first; + queue.bumped = 0; + while (lit) { + btab[lit] = ++queue.bumped; + lit = links[lit].next; + } + stats.bumped = queue.bumped; + update_queue_unassigned (queue.last); + +#ifndef CADICAL_NDEBUG + for (auto v : vars) + CADICAL_assert (val (v) || scores.contains (v)); + lit = queue.first; + int next_lit = links[lit].next; + while (next_lit) { + CADICAL_assert (btab[lit] < btab[next_lit]); + const int tmp = links[next_lit].next; + CADICAL_assert (!tmp || links[tmp].prev == next_lit); + lit = next_lit; + next_lit = tmp; + } + + lit = queue.last; + next_lit = links[lit].prev; + while (next_lit) { + CADICAL_assert (btab[lit] > btab[next_lit]); + const int tmp = links[next_lit].prev; + CADICAL_assert (!tmp || links[tmp].next == next_lit); + lit = next_lit; + next_lit = tmp; + } + CADICAL_assert (queue.first); + CADICAL_assert (queue.last); +#endif + factoring.fresh.clear (); +} + bool Internal::run_factorization (int64_t limit) { Factoring factoring = Factoring (this, limit); schedule_factorization (factoring); @@ -834,8 +908,7 @@ bool Internal::run_factorization (int64_t limit) { const unsigned idx = factoring.schedule.front (); completed = occs (u2i (idx)).empty (); } - // kissat initializes scores for new variables at this point, however - // this is actually done already during resize of internal + adjust_scores_and_phases_of_fresh_variables (factoring); #ifndef CADICAL_QUIET report ('f', !factored); #endif @@ -863,6 +936,19 @@ bool Internal::factor () { return false; if (!opts.factor) return false; + + int v_active = active (); + size_t log_active = log10 (v_active); + size_t eliminations = stats.elimrounds; + size_t delay = opts.factordelay; + size_t delay_limit = eliminations + delay; + if (log_active > delay_limit) { + VERBOSE (3, + "factorization delayed as %zu = log10 (%u)" + "> eliminations + delay = %zu + %zu = %zu", + log_active, v_active, eliminations, delay, delay_limit); + return false; + } // The following CADICAL_assertion fails if there are *only* user propagator // clauses (which are redundant). // CADICAL_assert (stats.mark.factor || clauses.empty ()); @@ -879,6 +965,8 @@ bool Internal::factor () { if (!stats.factor) limit += opts.factoriniticks * 1e6; + mark_duplicated_binary_clauses_as_garbage (); + START_SIMPLIFIER (factor, FACTOR); stats.factor++; diff --git a/src/sat/cadical/cadical_flags.cpp b/src/sat/cadical/cadical_flags.cpp index 5c06a33b4..7b2b650cf 100644 --- a/src/sat/cadical/cadical_flags.cpp +++ b/src/sat/cadical/cadical_flags.cpp @@ -31,7 +31,7 @@ void Internal::mark_fixed (int lit) { // to know about it. // But at that point it is not guaranteed to be already on the trail, so // notification will happen only later. - CADICAL_assert (!level); + CADICAL_assert (!level || in_mode (BACKBONE)); } } diff --git a/src/sat/cadical/cadical_frattracer.cpp b/src/sat/cadical/cadical_frattracer.cpp index d35a18271..90f3e1a42 100644 --- a/src/sat/cadical/cadical_frattracer.cpp +++ b/src/sat/cadical/cadical_frattracer.cpp @@ -196,7 +196,7 @@ void FratTracer::add_original_clause (int64_t id, bool, frat_add_original_clause (id, clause); } -void FratTracer::add_derived_clause (int64_t id, bool, +void FratTracer::add_derived_clause (int64_t id, bool, int, const vector &clause, const vector &chain) { if (file->closed ()) diff --git a/src/sat/cadical/cadical_idruptracer.cpp b/src/sat/cadical/cadical_idruptracer.cpp index ec6467163..3028122f7 100644 --- a/src/sat/cadical/cadical_idruptracer.cpp +++ b/src/sat/cadical/cadical_idruptracer.cpp @@ -391,7 +391,7 @@ void IdrupTracer::idrup_solve_query () { /*------------------------------------------------------------------------*/ -void IdrupTracer::add_derived_clause (int64_t, bool, +void IdrupTracer::add_derived_clause (int64_t, bool, int, const vector &clause, const vector &) { if (file->closed ()) diff --git a/src/sat/cadical/cadical_internal.cpp b/src/sat/cadical/cadical_internal.cpp index abe18c59c..40ad514ff 100644 --- a/src/sat/cadical/cadical_internal.cpp +++ b/src/sat/cadical/cadical_internal.cpp @@ -27,8 +27,9 @@ Internal::Internal () tainted_literal (0), notified (0), probe_reason (0), propagated (0), propagated2 (0), propergated (0), best_assigned (0), target_assigned (0), no_conflict_until (0), unsat_constraint (false), - marked_failed (true), sweep_incomplete (false), citten (0), - num_assigned (0), proof (0), opts (this), + marked_failed (true), sweep_incomplete (false), + randomized_deciding (false), citten (0), num_assigned (0), proof (0), + opts (this), #ifndef CADICAL_QUIET profiles (this), force_phase_messages (false), #endif @@ -38,8 +39,10 @@ Internal::Internal () control.push_back (Level (0, 0)); // The 'dummy_binary' is used in 'try_to_subsume_clause' to fake a real - // clause, which then can be used to subsume or strengthen the given - // clause in one routine for both binary and non binary clauses. This + // clause (which then can be used to subsume or strengthen the given + // clause in one routine for both binary and non binary clauses) and + // in walk (which is only used as a placeholder in the watch lists + // when logging is off, since the clause is not accessed). This // fake binary clause is always kept non-redundant (and not-moved etc.) // due to the following 'memset'. Only literals will be changed. @@ -51,6 +54,8 @@ Internal::Internal () dummy_binary = (Clause *) new char[bytes]; memset (dummy_binary, 0, bytes); dummy_binary->size = 2; + + /*with C++17: static_*/ CADICAL_assert (max_used == (1 << USED_SIZE) - 1); } Internal::~Internal () { @@ -147,7 +152,6 @@ void Internal::enlarge (int new_max_var) { enlarge_zero (phases.target, new_vsize); enlarge_zero (phases.best, new_vsize); enlarge_zero (phases.prev, new_vsize); - enlarge_zero (phases.min, new_vsize); enlarge_zero (marks, new_vsize); } @@ -348,8 +352,8 @@ int Internal::propagate_assumptions () { if (proof) proof->solve_query (); if (opts.ilb) { - if (opts.ilbassumptions) - sort_and_reuse_assumptions (); + sort_and_reuse_assumptions (); + CADICAL_assert (opts.ilb == 2 || (size_t) level <= assumptions.size ()); stats.ilbtriggers++; stats.ilbsuccess += (level > 0); stats.levelsreused += level; @@ -419,9 +423,9 @@ void Internal::implied (std::vector &entrailed) { int last_assumption_level = assumptions.size (); if (constraint.size ()) last_assumption_level++; - - size_t trail_limit = trail.size(); - if (level > last_assumption_level) + + size_t trail_limit = trail.size (); + if (level > last_assumption_level) trail_limit = control[last_assumption_level + 1].trail; for (size_t i = 0; i < trail_limit; i++) @@ -559,6 +563,7 @@ void Internal::init_search_limits () { lim.rephase = stats.conflicts + opts.rephaseint; lim.rephased[0] = lim.rephased[1] = 0; + last.stabilize.rephased = 0; LOG ("new rephase limit %" PRId64 " after %" PRId64 " conflicts", lim.rephase, lim.rephase - stats.conflicts); @@ -589,17 +594,18 @@ void Internal::init_search_limits () { } else LOG ("keeping non-stable phase"); - if (!incremental) { - inc.stabilize = 0; - lim.stabilize = stats.conflicts + opts.stabilizeinit; - LOG ("initial stabilize limit %" PRId64 " after %d conflicts", - lim.stabilize, (int) opts.stabilizeinit); - } + inc.stabilize = 0; + last.stabilize.conflicts = stats.conflicts; + lim.stabilize = stats.conflicts + opts.stabilizeinit; + last.stabilize.ticks = stats.ticks.search[0]; + stats.stabphases = 0; + LOG ("new ticks-based stabilize limit %" PRId64 " after %d conflicts", + lim.stabilize, (int) opts.stabilizeinit); - if (opts.stabilize && opts.reluctant) { + if (opts.stabilize && opts.reluctant && opts.reluctantint) { LOG ("new restart reluctant doubling sequence period %d", opts.reluctant); - reluctant.enable (opts.reluctant, opts.reluctantmax); + reluctant.enable (opts.reluctantint, opts.reluctantmax); } else reluctant.disable (); @@ -622,11 +628,20 @@ void Internal::init_search_limits () { LOG ("no limit on decisions"); } else { lim.decisions = stats.decisions + inc.decisions; - LOG ("conflict limit after %" PRId64 " decisions at %" PRId64 + LOG ("decision limit after %" PRId64 " decisions at %" PRId64 " decisions", inc.decisions, lim.decisions); } + if (inc.ticks < 0) { + lim.ticks = -1; + LOG ("no limit on ticks"); + } else { + lim.ticks = stats.ticks.search[0] + stats.ticks.search[1] + inc.ticks; + LOG ("ticks limit after %" PRId64 " ticks at %" PRId64 " ticks", + inc.ticks, lim.ticks); + } + /*----------------------------------------------------------------------*/ // Initial preprocessing rounds. @@ -639,6 +654,41 @@ void Internal::init_search_limits () { LOG ("limiting to %" PRId64 " local search rounds", lim.localsearch); } + /*----------------------------------------------------------------------*/ + // tier 1 and tier 2 limits + if (incremental && opts.recomputetier) { + for (auto m : {true, false}) + for (auto &u : stats.used[m]) + u = 0; + stats.bump_used = {0, 0}; + for (auto u : {true, false}) { + tier1[u] = max (tier1[u], opts.tier1minglue ? opts.tier1minglue : 2); + tier2[u] = max (tier2[u], opts.tier2minglue ? opts.tier2minglue : 6); + } + stats.tierecomputed = 0; + } + + /*----------------------------------------------------------------------*/ + // clause decaying + if (incremental) + last.incremental_decay.last_id = 0; + else { + lim.incremental_decay = opts.incdecayint; + } + + /*----------------------------------------------------------------------*/ + + if (incremental) + mode = "keeping"; + else { + lim.random_decision = stats.conflicts + opts.randecinit; + mode = "initial"; + } + (void) mode; + LOG ("%s randomize decision limit %" PRId64 " after %" PRId64 + " conflicts", + mode, lim.random_decision, lim.random_decision - stats.conflicts); + /*----------------------------------------------------------------------*/ lim.initialized = true; @@ -693,28 +743,34 @@ bool Internal::preprocess_round (int round) { } // for now counts as one of the preprocessing rounds TODO: change this? -void Internal::preprocess_quickly () { +void Internal::preprocess_quickly (bool always) { if (unsat) return; if (!max_var) return; if (!opts.preprocesslight) return; + if (!always && stats.searches > 1) + return; START (preprocess); +#ifndef CADICAL_QUIET struct { int64_t vars, clauses; } before, after; before.vars = active (); before.clauses = stats.current.irredundant; +#endif // stats.preprocessings++; CADICAL_assert (!preprocessing); preprocessing = true; + report ('('); PHASE ("preprocessing", stats.preprocessings, "starting with %" PRId64 " variables and %" PRId64 " clauses", before.vars, before.clauses); - if (extract_gates ()) + if (extract_gates (true)) decompose (); + binary_clauses_backbone (); if (sweep ()) decompose (); @@ -724,10 +780,12 @@ void Internal::preprocess_quickly () { if (opts.fastelim) elimfast (); - // if (opts.condition) - // condition (false); + // if (opts.condition) + // condition (false); +#ifndef CADICAL_QUIET after.vars = active (); after.clauses = stats.current.irredundant; +#endif CADICAL_assert (preprocessing); preprocessing = false; PHASE ("preprocessing", stats.preprocessings, @@ -737,11 +795,17 @@ void Internal::preprocess_quickly () { report ('P'); } -int Internal::preprocess () { - preprocess_quickly (); +int Internal::preprocess (bool always) { + int res = 0; + if (!level && !unsat && opts.luckyearly) + res = lucky_phases (); + if (res) + return res; + preprocess_quickly (always); for (int i = 0; i < lim.preprocessing; i++) if (!preprocess_round (i)) break; + report (')'); if (unsat) return 20; return 0; @@ -836,7 +900,11 @@ int Internal::local_search_round (int round) { else limit = LONG_MAX; - int res = walk_round (limit, true); + int res; + if (opts.walkfullocc) + res = walk_full_occs_round (limit, true); + else + res = walk_round (limit, true); CADICAL_assert (localsearching); localsearching = false; @@ -883,12 +951,13 @@ int Internal::local_search () { // int Internal::solve (bool preprocess_only) { CADICAL_assert (clause.empty ()); + stats.searches++; START (solve); if (proof) proof->solve_query (); if (opts.ilb) { - if (opts.ilbassumptions) - sort_and_reuse_assumptions (); + sort_and_reuse_assumptions (); + CADICAL_assert (opts.ilb || (size_t) level <= assumptions.size ()); stats.ilbtriggers++; stats.ilbsuccess += (level > 0); stats.levelsreused += level; @@ -919,12 +988,14 @@ int Internal::solve (bool preprocess_only) { res = local_search (); } if (!res && !level) - res = preprocess (); + res = preprocess (preprocess_only); if (!preprocess_only) { + if (!res && !level && opts.luckylate) + res = lucky_phases (); if (!res && !level) res = local_search (); - if (!res && !level) - res = lucky_phases (); + if (!res) + decay_clauses_upon_incremental_clauses (); if (!res || (res == 10 && external_prop)) { if (res == 10 && external_prop && level) backtrack (); diff --git a/src/sat/cadical/cadical_kitten.c b/src/sat/cadical/cadical_kitten.c index da3738980..031c21b01 100644 --- a/src/sat/cadical/cadical_kitten.c +++ b/src/sat/cadical/cadical_kitten.c @@ -497,11 +497,12 @@ static void clear_cadical_kitten (cadical_kitten *cadical_kitten) { void *OLD_PTR = (P); \ CALLOC (T, (P), new_size / 2); \ const size_t BYTES = old_vars * sizeof *(P); \ - memcpy ((P), OLD_PTR, BYTES); \ + if ((P) && OLD_PTR) /* nullptr not allowed */ \ + memcpy ((P), OLD_PTR, BYTES); \ void *NEW_PTR = (P); \ (P) = (T*)OLD_PTR; \ DEALLOC ((P), old_size / 2); \ - (P) = (T*)NEW_PTR; \ + (P) = (T*)NEW_PTR; \ } while (0) #define RESIZE2(T, P) \ @@ -509,7 +510,8 @@ static void clear_cadical_kitten (cadical_kitten *cadical_kitten) { void *OLD_PTR = (P); \ CALLOC (T, (P), new_size); \ const size_t BYTES = old_lits * sizeof *(P); \ - memcpy ((P), OLD_PTR, BYTES); \ + if ((P) && OLD_PTR) /* nullptr not allowed */ \ + memcpy ((P), OLD_PTR, BYTES); \ void *NEW_PTR = (P); \ (P) = (T*)OLD_PTR; \ DEALLOC ((P), old_size); \ @@ -839,7 +841,8 @@ static void enlarge_external (cadical_kitten *cadical_kitten, size_t eidx) { unsigned *old_import = cadical_kitten->import; CALLOC (unsigned, cadical_kitten->import, new_size); const size_t bytes = old_evars * sizeof *cadical_kitten->import; - memcpy (cadical_kitten->import, old_import, bytes); + if (cadical_kitten->import && old_import) + memcpy (cadical_kitten->import, old_import, bytes); DEALLOC (old_import, old_size); cadical_kitten->esize = new_size; } @@ -927,10 +930,14 @@ void cadical_kitten_clear (cadical_kitten *cadical_kitten) { CADICAL_assert (!cadical_kitten->marks[i]); #endif - memset (cadical_kitten->phases, 0, vars); - memset (cadical_kitten->values, 0, lits); - memset (cadical_kitten->failed, 0, lits); - memset (cadical_kitten->vars, 0, vars); + if (cadical_kitten->phases) + memset (cadical_kitten->phases, 0, vars); + if (cadical_kitten->values) + memset (cadical_kitten->values, 0, lits); + if (cadical_kitten->failed) + memset (cadical_kitten->failed, 0, lits); + if (cadical_kitten->vars) + memset (cadical_kitten->vars, 0, vars); clear_cadical_kitten (cadical_kitten); } @@ -2441,14 +2448,6 @@ int cadical_kitten_compute_prime_implicant (cadical_kitten *cadical_kitten, void return res; } -static bool contains_blit (cadical_kitten *cadical_kitten, klause *c, const unsigned blit) { - for (all_literals_in_klause (lit, c)) { - if (lit == blit) - return true; - } - return false; -} - static bool prime_propagate_blit (cadical_kitten *cadical_kitten, const unsigned idx, const unsigned blit) { unsigned lit = 2 * idx; @@ -2518,7 +2517,9 @@ static bool prime_propagate_blit (cadical_kitten *cadical_kitten, const unsigned static int compute_prime_implicant_for (cadical_kitten *cadical_kitten, unsigned blit) { value *values = cadical_kitten->values; +#ifndef CADICAL_NDEBUG kar *vars = cadical_kitten->vars; +#endif unsigneds unassigned; INIT_STACK (unassigned); bool limit_hit = false; @@ -2532,7 +2533,7 @@ static int compute_prime_implicant_for (cadical_kitten *cadical_kitten, unsigned values[blit] = 0; values[blit ^ 1] = 0; PUSH_STACK (unassigned, tmp > 0 ? blit : blit ^ 1); - PUSH_STACK (cadical_kitten->prime[i], block); // will be negated! + PUSH_STACK (cadical_kitten->prime[ignoring], block); // will be negated! } else CADICAL_assert (false); for (all_stack (unsigned, lit, cadical_kitten->trail)) { @@ -2546,7 +2547,6 @@ static int compute_prime_implicant_for (cadical_kitten *cadical_kitten, unsigned continue; CADICAL_assert (values[lit]); // not true when flipping is involved const unsigned idx = lit / 2; - const unsigned ref = vars[idx].reason; CADICAL_assert (vars[idx].level); LOG ("non-prime candidate var %d", idx); if (prime_propagate_blit (cadical_kitten, idx, block)) { diff --git a/src/sat/cadical/cadical_lidruptracer.cpp b/src/sat/cadical/cadical_lidruptracer.cpp index db07e36e6..79adfea3a 100644 --- a/src/sat/cadical/cadical_lidruptracer.cpp +++ b/src/sat/cadical/cadical_lidruptracer.cpp @@ -462,7 +462,7 @@ void LidrupTracer::lidrup_solve_query () { /*------------------------------------------------------------------------*/ -void LidrupTracer::add_derived_clause (int64_t id, bool, +void LidrupTracer::add_derived_clause (int64_t id, bool, int, const vector &clause, const vector &chain) { if (file->closed ()) diff --git a/src/sat/cadical/cadical_limit.cpp b/src/sat/cadical/cadical_limit.cpp index 69417a473..96f979834 100644 --- a/src/sat/cadical/cadical_limit.cpp +++ b/src/sat/cadical/cadical_limit.cpp @@ -27,7 +27,7 @@ Last::Last () { memset (this, 0, sizeof *this); } Inc::Inc () { memset (this, 0, sizeof *this); - decisions = conflicts = -1; // unlimited + ticks = decisions = conflicts = -1; // unlimited } void Internal::limit_terminate (int l) { @@ -66,6 +66,18 @@ void Internal::limit_decisions (int l) { } } +void Internal::limit_ticks (int64_t l) { + if (l < 0 && inc.ticks < 0) { + LOG ("keeping unbounded ticks limit"); + } else if (l < 0) { + LOG ("reset ticks limit to be unbounded"); + inc.ticks = -1; + } else { + inc.ticks = l; + LOG ("new ticks limit of %" PRId64 " ticks", l); + } +} + void Internal::limit_preprocessing (int l) { if (l < 0) { LOG ("ignoring invalid preprocessing limit %d", l); @@ -101,6 +113,8 @@ bool Internal::is_valid_limit (const char *name) { return true; if (!strcmp (name, "localsearch")) return true; + if (!strcmp (name, "ticks")) + return true; return false; } @@ -116,6 +130,8 @@ bool Internal::limit (const char *name, int l) { limit_preprocessing (l); else if (!strcmp (name, "localsearch")) limit_local_search (l); + else if (!strcmp (name, "ticks")) + limit_ticks (l); else res = false; return res; @@ -128,6 +144,7 @@ void Internal::reset_limits () { limit_decisions (-1); limit_preprocessing (0); limit_local_search (0); + limit_ticks (-1); } } // namespace CaDiCaL diff --git a/src/sat/cadical/cadical_logging.cpp b/src/sat/cadical/cadical_logging.cpp index 589bf097e..9799d6f01 100644 --- a/src/sat/cadical/cadical_logging.cpp +++ b/src/sat/cadical/cadical_logging.cpp @@ -92,9 +92,8 @@ void Logger::log (Internal *internal, const Gate *g, const char *fmt, ...) { vprintf (fmt, ap); va_end (ap); if (g) { - printf ("%s%s%s gate[%" PRIu64 "] (arity: %ld) %s := %s", - g->degenerated_and_pos ? " deg+" : "", - g->degenerated_and_neg ? " deg-" : "", + printf ("%s%s gate[%" PRIu64 "] (arity: %zu) %s := %s", + special_gate_str (g->degenerated_gate).c_str (), g->garbage ? " garbage" : "", g->id, g->arity (), loglit (internal, g->lhs).c_str (), string_of_gate (g->tag).c_str ()); diff --git a/src/sat/cadical/cadical_lratchecker.cpp b/src/sat/cadical/cadical_lratchecker.cpp index 5f342a370..d2854c6d6 100644 --- a/src/sat/cadical/cadical_lratchecker.cpp +++ b/src/sat/cadical/cadical_lratchecker.cpp @@ -478,16 +478,20 @@ void LratChecker::add_original_clause (int64_t id, bool, STOP (checking); } -void LratChecker::add_derived_clause (int64_t id, bool, +void LratChecker::add_derived_clause (int64_t id, bool, int w, const vector &c, const vector &proof_chain) { START (checking); - LOG (c, "LRAT CHECKER addition of derived clause[%" PRId64 "]", id); + LOG (c, "LRAT CHECKER addition of derived %d clause[%" PRId64 "]", w, id); + CADICAL_assert (!w || c[0] == w); + if (w) + stats.rat++; stats.added++; stats.derived++; import_clause (c); last_id = id; CADICAL_assert (id == current_id + 1); + CADICAL_assert (!w || w == c[0]); current_id = id; if (size_clauses) { LratCheckerClause **p = find (id), *d = *p; @@ -541,7 +545,7 @@ void LratChecker::add_assumption_clause (int64_t id, const vector &c, stderr); fatal_message_end (); } - add_derived_clause (id, true, c, chain); + add_derived_clause (id, true, 0, c, chain); delete_clause (id, true, c); assumption_clauses.push_back (id); } diff --git a/src/sat/cadical/cadical_lrattracer.cpp b/src/sat/cadical/cadical_lrattracer.cpp index 4da728e18..165cfacfc 100644 --- a/src/sat/cadical/cadical_lrattracer.cpp +++ b/src/sat/cadical/cadical_lrattracer.cpp @@ -127,7 +127,7 @@ void LratTracer::lrat_delete_clause (int64_t id) { /*------------------------------------------------------------------------*/ -void LratTracer::add_derived_clause (int64_t id, bool, +void LratTracer::add_derived_clause (int64_t id, bool, int, const vector &clause, const vector &chain) { if (file->closed ()) diff --git a/src/sat/cadical/cadical_lucky.cpp b/src/sat/cadical/cadical_lucky.cpp index d67782db4..fee3894ee 100644 --- a/src/sat/cadical/cadical_lucky.cpp +++ b/src/sat/cadical/cadical_lucky.cpp @@ -25,7 +25,7 @@ namespace CaDiCaL { int Internal::unlucky (int res) { if (level > 0) - backtrack (); + backtrack_without_updating_phases (); if (conflict) conflict = 0; return res; @@ -34,7 +34,9 @@ int Internal::unlucky (int res) { int Internal::trivially_false_satisfiable () { LOG ("checking that all clauses contain a negative literal"); CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); + int res = lucky_decide_assumptions (); + if (res) + return res; for (const auto &c : clauses) { if (terminated_asynchronously (100)) return unlucky (-1); @@ -81,7 +83,9 @@ int Internal::trivially_false_satisfiable () { int Internal::trivially_true_satisfiable () { LOG ("checking that all clauses contain a positive literal"); CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); + int res = lucky_decide_assumptions (); + if (res) + return res; for (const auto &c : clauses) { if (terminated_asynchronously (100)) return unlucky (-1); @@ -132,7 +136,8 @@ inline bool Internal::lucky_propagate_discrepency (int dec) { if (no_conflict) return false; if (level > 1) { - backtrack (level - 1); + conflict = nullptr; + backtrack_without_updating_phases (level - 1); search_assume_decision (-dec); no_conflict = propagate (); if (no_conflict) @@ -155,7 +160,9 @@ int Internal::forward_false_satisfiable () { LOG ("checking increasing variable index false assignment"); CADICAL_assert (!unsat); CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); + int res = lucky_decide_assumptions (); + if (res) + return res; for (auto idx : vars) { START: if (terminated_asynchronously (100)) @@ -180,7 +187,9 @@ int Internal::forward_true_satisfiable () { LOG ("checking increasing variable index true assignment"); CADICAL_assert (!unsat); CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); + int res = lucky_decide_assumptions (); + if (res) + return res; for (auto idx : vars) { START: if (terminated_asynchronously (10)) @@ -207,8 +216,11 @@ int Internal::backward_false_satisfiable () { LOG ("checking decreasing variable index false assignment"); CADICAL_assert (!unsat); CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); - for (int idx = max_var; idx > 0; idx--) { + int res = lucky_decide_assumptions (); + if (res) + return res; + for (auto it = vars.rbegin (); it != vars.rend (); ++it) { + int idx = *it; START: if (terminated_asynchronously (10)) return unlucky (-1); @@ -232,8 +244,11 @@ int Internal::backward_true_satisfiable () { LOG ("checking decreasing variable index true assignment"); CADICAL_assert (!unsat); CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); - for (int idx = max_var; idx > 0; idx--) { + int res = lucky_decide_assumptions (); + if (res) + return res; + for (auto it = vars.rbegin (); it != vars.rend (); ++it) { + int idx = *it; START: if (terminated_asynchronously (10)) return unlucky (-1); @@ -264,7 +279,9 @@ int Internal::backward_true_satisfiable () { int Internal::positive_horn_satisfiable () { LOG ("checking that all clauses are positive horn satisfiable"); CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); + int res = lucky_decide_assumptions (); + if (res) + return res; for (const auto &c : clauses) { if (terminated_asynchronously (10)) return unlucky (-1); @@ -320,10 +337,50 @@ int Internal::positive_horn_satisfiable () { return 10; } -int Internal::negative_horn_satisfiable () { - LOG ("checking that all clauses are negative horn satisfiable"); +int Internal::lucky_decide_assumptions () { CADICAL_assert (!level); - CADICAL_assert (assumptions.empty ()); + CADICAL_assert (!constraint.size ()); + int res = 0; + while ((size_t) level < assumptions.size ()) { + res = decide (); + if (res == 20) { + marked_failed = false; + return 20; + } + if (!propagate ()) { + break; + } + } + + if (conflict) { + // analyze and learn from the conflict. + LOG (conflict, "setting assumption lead to conflict"); + analyze_wrapper (); + backtrack (0); + CADICAL_assert (!conflict); + int res = 0; + while (!res) { + CADICAL_assert ((size_t) level <= assumptions.size ()); + if (unsat) + res = 20; + else if (!propagate ()) { + analyze_wrapper (); + } else { + res = decide_wrapper (); + } + } + CADICAL_assert (res == 20); + return 20; + } + return 0; +} + +int Internal::negative_horn_satisfiable () { + CADICAL_assert (!level); + LOG ("checking that all clauses are negative horn satisfiable"); + int res = lucky_decide_assumptions (); + if (res) + return res; for (const auto &c : clauses) { if (terminated_asynchronously (10)) return unlucky (-1); @@ -350,7 +407,7 @@ int Internal::negative_horn_satisfiable () { continue; if (!negative_literal) { if (level > 0) - backtrack (); + backtrack_without_updating_phases (); LOG (c, "no negative unassigned literal in"); return unlucky (0); } @@ -389,15 +446,21 @@ int Internal::lucky_phases () { if (!opts.lucky) return 0; - // TODO: Some of the lucky assignments can also be found if there are - // assumptions, but this is not completely implemented nor tested yet. - // Nothing done for constraint either. - // External propagator assumes a CDCL loop, so lucky is not tried here. - if (!assumptions.empty () || !constraint.empty () || external_prop) + if (!opts.luckyassumptions && !assumptions.empty ()) return 0; + // TODO: Some of the lucky assignments can also be found if there are + // constraint. + // External propagator assumes a CDCL loop, so lucky is not tried here. + if (!constraint.empty () || external_prop) + return 0; + if (!propagate ()) { + learn_empty_clause (); + return 20; + } START (search); START (lucky); + LOG ("starting lucky"); CADICAL_assert (!searching_lucky_phases); searching_lucky_phases = true; stats.lucky.tried++; @@ -405,18 +468,18 @@ int Internal::lucky_phases () { int res = trivially_false_satisfiable (); if (!res) res = trivially_true_satisfiable (); - if (!res) - res = forward_true_satisfiable (); if (!res) res = forward_false_satisfiable (); + if (!res) + res = forward_true_satisfiable (); if (!res) res = backward_false_satisfiable (); if (!res) res = backward_true_satisfiable (); - if (!res) - res = positive_horn_satisfiable (); if (!res) res = negative_horn_satisfiable (); + if (!res) + res = positive_horn_satisfiable (); if (res < 0) CADICAL_assert (termination_forced), res = 0; if (res == 10) @@ -424,11 +487,20 @@ int Internal::lucky_phases () { report ('l', !res); CADICAL_assert (searching_lucky_phases); + CADICAL_assert (res || !level); + if (res != 20) { + if (!propagate ()) { + LOG ("propagating units after elimination results in empty clause"); + learn_empty_clause (); + } + } + const int64_t units = active_before - stats.active; if (!res && units) - LOG ("lucky %zd units", units); + LOG ("lucky %" PRId64 " units", units); searching_lucky_phases = false; + STOP (lucky); STOP (search); diff --git a/src/sat/cadical/cadical_message.cpp b/src/sat/cadical/cadical_message.cpp index 42d8c0e48..fbc44bee3 100644 --- a/src/sat/cadical/cadical_message.cpp +++ b/src/sat/cadical/cadical_message.cpp @@ -25,6 +25,12 @@ void Internal::vmessage (const char *fmt, va_list &ap) { } void Internal::message (const char *fmt, ...) { +#ifdef LOGGING + if (!opts.log) +#endif + if (opts.quiet) + return; + va_list ap; va_start (ap, fmt); vmessage (fmt, ap); diff --git a/src/sat/cadical/cadical_parse.cpp b/src/sat/cadical/cadical_parse.cpp index 0668aca7d..e2901fdf5 100644 --- a/src/sat/cadical/cadical_parse.cpp +++ b/src/sat/cadical/cadical_parse.cpp @@ -52,6 +52,18 @@ inline const char *Parser::parse_positive_int (int &ch, int &res, return 0; } +inline const char *Parser::parse_positive_uint64_t (int &ch, uint64_t &res, + const char *name) { + CADICAL_assert (isdigit (ch)); + res = ch - '0'; + while (isdigit (ch = parse_char ())) { + int digit = ch - '0'; + if (UINT64_MAX / 10 < res || UINT64_MAX - digit < 10 * res) + PER ("too large '%s' in header", name); + res = 10 * res + digit; + } + return 0; +} static const char *cube_token = "unexpected 'a' in CNF"; inline const char *Parser::parse_lit (int &ch, int &lit, int &vars, @@ -99,7 +111,8 @@ const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { #endif bool found_inccnf_header = false; - int ch, clauses = 0; + int ch = 0; + uint64_t clauses = 0; vars = 0; // First read comments before header with possibly embedded options. @@ -162,11 +175,12 @@ const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { PER ("expected ' ' after 'p cnf %d'", vars); if (!isdigit (ch = parse_char ())) PER ("expected digit after 'p cnf %d '", vars); - err = parse_positive_int (ch, clauses, ""); + err = parse_positive_uint64_t (ch, clauses, ""); if (err) return err; if (ch != '\n') - PER ("expected new-line after 'p cnf %d %d'", vars, clauses); + PER ("expected new-line after 'p cnf %d %" PRIu64 "'", vars, + clauses); } else { if (parse_char () != 'n') PER ("expected 'n' after 'p c'"); @@ -190,21 +204,22 @@ const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { while (isspace (ch)); if (!isdigit (ch)) PER ("expected digit after 'p cnf %d '", vars); - err = parse_positive_int (ch, clauses, ""); + err = parse_positive_uint64_t (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); + PER ("expected new-line after 'p cnf %d %" PRIu64 "'", vars, + clauses); ch = parse_char (); } } - MSG ("found %s'p cnf %d %d'%s header", tout.green_code (), vars, - clauses, tout.normal_code ()); + MSG ("found %s'p cnf %d %" PRIu64 "'%s header", tout.green_code (), + vars, clauses, tout.normal_code ()); if (strict != FORCED) - solver->reserve (vars); + solver->resize (vars); internal->reserve_ids (clauses); } else if (!parse_inccnf_too) PER ("expected 'c' after 'p '"); @@ -237,7 +252,8 @@ const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { // Now read body of DIMACS part. // - int lit = 0, parsed = 0; + int lit = 0; + uint64_t parsed = 0; while ((ch = parse_char ()) != EOF) { if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r') continue; @@ -272,8 +288,8 @@ const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { #ifndef CADICAL_QUIET double end = internal->time (); - MSG ("parsed %d clauses in %.2f seconds %s time", parsed, end - start, - internal->opts.realtime ? "real" : "process"); + MSG ("parsed %" PRIu64 " clauses in %.2f seconds %s time", parsed, + end - start, internal->opts.realtime ? "real" : "process"); #endif #ifndef CADICAL_QUIET diff --git a/src/sat/cadical/cadical_phases.cpp b/src/sat/cadical/cadical_phases.cpp index f7e4eaffd..ffb9398fd 100644 --- a/src/sat/cadical/cadical_phases.cpp +++ b/src/sat/cadical/cadical_phases.cpp @@ -8,8 +8,20 @@ namespace CaDiCaL { void Internal::copy_phases (vector &dst) { START (copy); - for (auto i : vars) - dst[i] = phases.saved[i]; + for (auto i : vars) { + const signed char tmp = phases.saved[i]; + if (tmp) + dst[i] = tmp; + } + STOP (copy); +} + +void Internal::save_assigned_phases (vector &dst) { + START (copy); + for (auto i : vars) { + if (vals[i]) + dst[i] = vals[i]; + } STOP (copy); } diff --git a/src/sat/cadical/cadical_probe.cpp b/src/sat/cadical/cadical_probe.cpp index 19ff0961f..6414dffaa 100644 --- a/src/sat/cadical/cadical_probe.cpp +++ b/src/sat/cadical/cadical_probe.cpp @@ -426,8 +426,8 @@ bool Internal::probe_propagate () { const int lit = -trail[propagated++]; LOG ("probe propagating %d over large clauses", -lit); Watches &ws = watches (lit); - ticks += 1 + cache_lines (ws.size (), - sizeof (sizeof (const_watch_iterator *))); + ticks += + 1 + cache_lines (ws.size (), sizeof (const_watch_iterator *)); size_t i = 0, j = 0; while (i != ws.size ()) { const Watch w = ws[j++] = ws[i++]; @@ -838,7 +838,7 @@ bool Internal::probe () { LOG ("probing %d", probe); probe_assign_decision (probe); if (probe_propagate ()) - backtrack (); + backtrack_without_updating_phases (); else failed_literal (probe); clean_probehbr_lrat (); @@ -944,13 +944,16 @@ void CaDiCaL::Internal::inprobe (bool update_limits) { if (probe ()) decompose (); - if (extract_gates ()) + if (extract_gates (preprocessing)) decompose (); + binary_clauses_backbone (); + mark_duplicated_binary_clauses_as_garbage (); if (sweep ()) // full occurrence list decompose (); // ... and (ELS) afterwards. (void) vivify (); // resets watches transred (); // builds big. - factor (); // resets watches, partial occurrence list + binary_clauses_backbone (); + factor (); // resets watches, partial occurrence list } if (external_prop) { diff --git a/src/sat/cadical/cadical_proof.cpp b/src/sat/cadical/cadical_proof.cpp index 13c9a1a5d..4cb548dc8 100644 --- a/src/sat/cadical/cadical_proof.cpp +++ b/src/sat/cadical/cadical_proof.cpp @@ -199,7 +199,9 @@ void Internal::flush_trace (bool print) { /*------------------------------------------------------------------------*/ -Proof::Proof (Internal *s) : internal (s) { LOG ("PROOF new"); } +Proof::Proof (Internal *s) : internal (s), witness (0) { + LOG ("PROOF new"); +} Proof::~Proof () { LOG ("PROOF delete"); } @@ -292,6 +294,20 @@ void Proof::add_derived_clause (Clause *c, const vector &chain) { add_derived_clause (); } +void Proof::add_derived_rat_clause (Clause *c, int w, + const vector &chain) { + LOG (c, "PROOF adding to proof derived witness %d", w); + CADICAL_assert (clause.empty ()); + CADICAL_assert (proof_chain.empty ()); + add_literals (c); + for (const auto &cid : chain) + proof_chain.push_back (cid); + clause_id = c->id; + redundant = c->redundant; + witness = w; + add_derived_clause (); +} + void Proof::add_derived_clause (int64_t id, bool r, const vector &c, const vector &chain) { LOG (c, "PROOF adding derived clause"); @@ -306,6 +322,22 @@ void Proof::add_derived_clause (int64_t id, bool r, const vector &c, add_derived_clause (); } +void Proof::add_derived_rat_clause (int64_t id, bool r, int l, + const vector &c, + const vector &chain) { + LOG (c, "PROOF adding derived witness %d clause", l); + CADICAL_assert (clause.empty ()); + CADICAL_assert (proof_chain.empty ()); + for (const auto &lit : c) + add_literal (lit); + for (const auto &cid : chain) + proof_chain.push_back (cid); + clause_id = id; + redundant = r; + witness = l; + add_derived_clause (); +} + void Proof::add_assumption_clause (int64_t id, const vector &c, const vector &chain) { // literals of c are already external @@ -530,11 +562,13 @@ void Proof::add_derived_clause () { redundant); CADICAL_assert (clause_id); for (auto &tracer : tracers) { - tracer->add_derived_clause (clause_id, redundant, clause, proof_chain); + tracer->add_derived_clause (clause_id, redundant, witness, clause, + proof_chain); } proof_chain.clear (); clause.clear (); clause_id = 0; + witness = 0; } void Proof::delete_clause () { diff --git a/src/sat/cadical/cadical_propagate.cpp b/src/sat/cadical/cadical_propagate.cpp index a4f21ee6a..4b3e905d0 100644 --- a/src/sat/cadical/cadical_propagate.cpp +++ b/src/sat/cadical/cadical_propagate.cpp @@ -24,7 +24,7 @@ namespace CaDiCaL { // relevant in conflict analysis or in root-level fixing steps. static Clause decision_reason_clause; -static Clause *decision_reason = &decision_reason_clause; +Clause *Internal::decision_reason = &decision_reason_clause; // If chronological backtracking is used the actual assignment level might // be lower than the current decision level. In this case the assignment @@ -89,7 +89,7 @@ void Internal::build_chain_for_units (int lit, Clause *reason, void Internal::build_chain_for_empty () { if (!lrat || !lrat_chain.empty ()) return; - CADICAL_assert (!level); + CADICAL_assert (!level || in_mode (BACKBONE)); CADICAL_assert (lrat_chain.empty ()); CADICAL_assert (conflict); LOG (conflict, "lrat for global empty clause with conflict"); @@ -320,6 +320,7 @@ bool Internal::propagate () { } literal_iterator lits = w.clause->begin (); + CADICAL_assert (lits[0] == lit || lits[1] == lit); // Simplify code by forcing 'lit' to be the second literal in the // clause. This goes back to MiniSAT. We use a branch-less version @@ -351,7 +352,8 @@ bool Internal::propagate () { literal_iterator k = middle; // Find replacement watch 'r' at position 'k' with value 'v'. - + CADICAL_assert (lits + 2 <= k); + LOG (w.clause, "search starting at %d", w.clause->pos); int r = 0; signed char v = -1; @@ -492,6 +494,10 @@ bool Internal::propagate () { } } + if (conflict && randomized_deciding) { + if (!--randomized_deciding) + VERBOSE (3, "last random decision conflict"); + } STOP (propagate); return !conflict; diff --git a/src/sat/cadical/cadical_reduce.cpp b/src/sat/cadical/cadical_reduce.cpp index cc6f35228..5acd8a113 100644 --- a/src/sat/cadical/cadical_reduce.cpp +++ b/src/sat/cadical/cadical_reduce.cpp @@ -48,9 +48,9 @@ void Internal::mark_clauses_to_be_flushed () { const unsigned used = c->used; if (used) c->used--; - if (c->glue < tier1limit && used) + if (c->glue <= tier1limit && used) continue; - if (c->glue < tier2limit && used >= max_used - 1) + if (c->glue <= tier2limit && used >= max_used - 1) continue; mark_garbage (c); // flush unused clauses if (c->hyper) diff --git a/src/sat/cadical/cadical_rephase.cpp b/src/sat/cadical/cadical_rephase.cpp index 3b2e62526..12b1f515d 100644 --- a/src/sat/cadical/cadical_rephase.cpp +++ b/src/sat/cadical/cadical_rephase.cpp @@ -27,6 +27,12 @@ bool Internal::rephasing () { return false; if (opts.forcephase) return false; + if (opts.rephase == 2) { + if (stable) + return stats.stabconflicts > lim.rephase; + else + return false; + } return stats.conflicts > lim.rephase; } @@ -99,7 +105,10 @@ char Internal::rephase_walk () { stats.rephased.walk++; PHASE ("rephase", stats.rephased.total, "starting local search to improve current phase"); - walk (); + if (opts.walkfullocc) + walk_full_occs (); + else + walk (); return 'W'; } @@ -108,9 +117,12 @@ char Internal::rephase_walk () { void Internal::rephase () { stats.rephased.total++; + last.stabilize.rephased++; + CADICAL_assert (last.stabilize.rephased <= stats.rephased.total); PHASE ("rephase", stats.rephased.total, "reached rephase limit %" PRId64 " after %" PRId64 " conflicts", - lim.rephase, stats.conflicts); + lim.rephase, + opts.rephase == 2 ? stats.stabconflicts : stats.conflicts); // Report current 'target' and 'best' and then set 'rephased' below, which // will trigger reporting the new 'target' and 'best' after updating it in @@ -119,8 +131,6 @@ void Internal::rephase () { report ('~', 1); backtrack (); - clear_phases (phases.target); - target_assigned = 0; size_t count = lim.rephased[stable]++; bool single; @@ -208,6 +218,52 @@ void Internal::rephase () { type = 0; break; } + } else if (opts.rephase == 2 && opts.walk) { + // (inverted,best,walk, + // flipping,best,walk, + // random,best,walk, + // original,best,walk)^\omega + switch (count % 12) { + case 0: + type = rephase_inverted (); + break; + case 1: + type = rephase_best (); + break; + case 2: + type = rephase_walk (); + break; + case 3: + type = rephase_flipping (); + break; + case 4: + type = rephase_best (); + break; + case 5: + type = rephase_walk (); + break; + case 6: + type = rephase_random (); + break; + case 7: + type = rephase_best (); + break; + case 8: + type = rephase_walk (); + break; + case 9: + type = rephase_original (); + break; + case 10: + type = rephase_best (); + break; + case 11: + type = rephase_walk (); + break; + default: + type = 0; + break; + } } else if (stable && !opts.walk) { // original,inverted,(best,original,best,inverted)^\omega if (!count) @@ -288,7 +344,7 @@ void Internal::rephase () { CADICAL_assert (!stable && opts.walk && opts.walknonstable); // flipping,(random,best,walk,flipping,best,walk)^\omega if (!count) - type = rephase_flipping (); + type = rephase_original (); else switch ((count - 1) % 6) { case 0: @@ -316,8 +372,14 @@ void Internal::rephase () { } CADICAL_assert (type); + // clear after walk such that random walk can still access the target + // by using the saved phases + copy_phases (phases.target); + target_assigned = 0; + int64_t delta = opts.rephaseint * (stats.rephased.total + 1); - lim.rephase = stats.conflicts + delta; + lim.rephase = + (opts.rephase == 2 ? stats.stabconflicts : stats.conflicts) + delta; PHASE ("rephase", stats.rephased.total, "new rephase limit %" PRId64 " after %" PRId64 " conflicts", @@ -331,6 +393,10 @@ void Internal::rephase () { last.rephase.conflicts = stats.conflicts; rephased = type; + if (!marked_failed || unsat_constraint) { + CADICAL_assert (opts.warmup); + return; + } if (stable) shuffle_scores (); else diff --git a/src/sat/cadical/cadical_report.cpp b/src/sat/cadical/cadical_report.cpp index 18271d17e..20e0518f6 100644 --- a/src/sat/cadical/cadical_report.cpp +++ b/src/sat/cadical/cadical_report.cpp @@ -47,6 +47,7 @@ r start of solving after restoring clauses 1 end of solving returns satisfiable 0 end of solving returns unsatisfiable ? end of solving due to interrupt +k binary backbone extraction ('kernel') l lucky phase solving p failed literal probing round (lower case 'p') . before reducing redundant clauses @@ -138,8 +139,13 @@ Report::Report (const char *h, int precision, int min, double value) REPORT ("rate", 0, 2, averages.current.decisions) \ REPORT ("conflicts", 0, 4, stats.conflicts) \ REPORT ("redundant", 0, 4, stats.current.redundant) \ - REPORT ("trail", -1, 2, TRAIL) \ + REPORT ("size/glue", 1, 2, \ + relative (averages.current.size, averages.current.glue.slow)) \ + REPORT ("size", 0, 1, averages.current.size) \ REPORT ("glue", 0, 1, averages.current.glue.slow) \ + REPORT ("tier1", 0, 1, tier1[stable]) \ + REPORT ("tier2", 0, 1, tier2[stable]) \ + REPORT ("trail", -1, 2, TRAIL) \ REPORT ("irredundant", 0, 4, stats.current.irredundant) \ REPORT ("variables", 0, 3, active ()) \ REPORT ("remaining", -1, 2, REMAINING) @@ -273,12 +279,23 @@ void Internal::report (char type, int verbose) { tout.bold (); tout.underline (); break; + case '(': + case ')': + tout.bold (); + tout.yellow (); + break; + case '{': + case '}': + tout.normal (); + break; default: break; } fputc (type, stdout); if (stable || type == ']') tout.magenta (); + else if (preprocessing || type == ')') + tout.bold (), tout.yellow (); else if (type != 'L' && type != 'P') tout.normal (); for (int i = 0; i < n; i++) { diff --git a/src/sat/cadical/cadical_restart.cpp b/src/sat/cadical/cadical_restart.cpp index d309d5f10..09eae624b 100644 --- a/src/sat/cadical/cadical_restart.cpp +++ b/src/sat/cadical/cadical_restart.cpp @@ -35,12 +35,19 @@ bool Internal::stabilizing () { STOP (stable); else STOP (unstable); - //const int64_t delta_conflicts = - // stats.conflicts - last.stabilize.conflicts; + + CADICAL_assert (last.stabilize.ticks >= 0); + CADICAL_assert (last.stabilize.conflicts >= 0 && + last.stabilize.conflicts <= stats.conflicts); + CADICAL_assert (last.stabilize.ticks <= stats.ticks.search[stable]); const int64_t delta_ticks = stats.ticks.search[stable] - last.stabilize.ticks; - //const char *current_mode = stable ? "stable" : "unstable"; - //const char *next_mode = stable ? "unstable" : "stable"; +#ifndef CADICAL_QUIET + const int64_t delta_conflicts = + stats.conflicts - last.stabilize.conflicts; + const char *current_mode = stable ? "stable" : "unstable"; + const char *next_mode = stable ? "unstable" : "stable"; +#endif PHASE ("stabilizing", stats.stabphases, "reached %s stabilization limit %" PRId64 " after %" PRId64 " conflicts and %" PRId64 " ticks at %" PRId64 @@ -53,24 +60,26 @@ bool Internal::stabilizing () { // ticks inc.stabilize = 1; - stable = !stable; // Switch!!!!! - int64_t next_delta_ticks = inc.stabilize; int64_t stabphases = stats.stabphases + 1; next_delta_ticks *= stabphases * stabphases; - lim.stabilize = stats.ticks.search[stable] + next_delta_ticks; - if (lim.stabilize <= stats.ticks.search[stable]) - lim.stabilize = stats.ticks.search[stable] + 1; + const bool next_stable = !stable; + lim.stabilize = stats.ticks.search[next_stable] + next_delta_ticks; + last.stabilize.ticks = stats.ticks.search[next_stable]; + if (lim.stabilize <= stats.ticks.search[next_stable]) + lim.stabilize = stats.ticks.search[next_stable] + 1; + PHASE ("stabilizing", stats.stabphases, + "next %s stabilization limit %" PRId64 + " at ticks interval %" PRId64, + next_mode, lim.stabilize, next_delta_ticks); + + stable = !stable; // Switch!!!!! if (stable) stats.stabphases++; swap_averages (); - PHASE ("stabilizing", stats.stabphases, - "next %s stabilization limit %" PRId64 - " at ticks interval %" PRId64, - next_mode, lim.stabilize, next_delta_ticks); report (stable ? '[' : '{'); if (stable) START (stable); @@ -91,14 +100,24 @@ bool Internal::restarting () { return false; if ((size_t) level < assumptions.size () + 2) return false; - if (stabilizing ()) + if (stabilizing () && opts.reluctant) return reluctant; if (stats.conflicts <= lim.restart) return false; double f = averages.current.glue.fast; - double margin = (100.0 + opts.restartmargin) / 100.0; - double s = averages.current.glue.slow, l = margin * s; - LOG ("EMA glue slow %.2f fast %.2f limit %.2f", s, f, l); + int p = stable ? opts.restartmarginstable : opts.restartmarginfocused; + double m = (100.0 + p) / 100.0; + double s = averages.current.glue.slow; + double l = m * s; + +#ifndef CADICAL_QUIET + char c = l > f ? '>' : l < f ? '<' : '='; + VERBOSE (3, + "restart glue limit " + "%g = %.2f * %g (slow glue) %c %g (fast glue)", + l, m, s, c, f); +#endif + return l <= f; } diff --git a/src/sat/cadical/cadical_solver.cpp b/src/sat/cadical/cadical_solver.cpp index 84d350cf2..7dd03e8be 100644 --- a/src/sat/cadical/cadical_solver.cpp +++ b/src/sat/cadical/cadical_solver.cpp @@ -1,5 +1,6 @@ #include "global.h" +#include "cadical.hpp" #include "internal.hpp" ABC_NAMESPACE_IMPL_START @@ -60,7 +61,7 @@ void Solver::transition_to_steady_state () { external->reset_assumptions (); external->reset_concluded (); external->reset_constraint (); - } else if (state() == INCONCLUSIVE) { + } else if (state () == INCONCLUSIVE) { external->reset_assumptions (); external->reset_concluded (); external->reset_constraint (); @@ -92,6 +93,18 @@ static void log_api_call (Internal *internal, const char *name, int arg, name, arg, tout.log_code (), suffix); } +static void log_api_call (Internal *internal, const char *name, int arg, + int b, const char *suffix) { + Logger::log (internal, "API call %s'%s (%d, %d)'%s %s", tout.api_code (), + name, arg, b, tout.log_code (), suffix); +} + +static void log_api_call (Internal *internal, const char *name, int arg, + int b, int c) { + Logger::log (internal, "API call %s'%s (%d, %d)'%s %d", tout.api_code (), + name, arg, b, tout.log_code (), c); +} + static void log_api_call (Internal *internal, const char *name, const char *arg, const char *suffix) { Logger::log (internal, "API call %s'%s (\"%s\")'%s %s", tout.api_code (), @@ -119,6 +132,12 @@ static void log_api_call_begin (Internal *internal, const char *name, log_api_call (internal, name, arg, "started"); } +static void log_api_call_begin (Internal *internal, const char *name, + int arg, int b) { + Logger::log_empty_line (internal); + log_api_call (internal, name, arg, b, "started"); +} + static void log_api_call_begin (Internal *internal, const char *name, const char *arg) { Logger::log_empty_line (internal); @@ -196,6 +215,11 @@ static void log_api_call_returns (Internal *internal, const char *name, res ? "returns 'true'" : "returns 'false'"); } +static void log_api_call_returns (Internal *internal, const char *name, + int lit, int b, int res) { + log_api_call (internal, name, lit, b, res); +} + static void log_api_call_returns (Internal *internal, const char *name, const char *arg, const char *res) { Logger::log (internal, "API call %s'%s (\"%s\")'%s returns '%s'", @@ -281,6 +305,13 @@ void Solver::trace_api_call (const char *s0, int i1) const { fflush (trace_api_file); } +void Solver::trace_api_call (const char *s0, int i1, int b) const { + CADICAL_assert (trace_api_file); + LOG ("TRACE %s %d %d", s0, i1, b); + fprintf (trace_api_file, "%s %d %d\n", s0, i1, b); + fflush (trace_api_file); +} + void Solver::trace_api_call (const char *s0, const char *s1) const { CADICAL_assert (trace_api_file); LOG ("TRACE %s %s", s0, s1); @@ -437,23 +468,34 @@ int Solver::vars () { return res; } -void Solver::reserve (int min_max_var) { - TRACE ("reserve", min_max_var); +void Solver::resize (int min_max_var) { + TRACE ("resize", min_max_var); REQUIRE_VALID_STATE (); transition_to_steady_state (); external->reset_extended (); external->init (min_max_var); - LOG_API_CALL_END ("reserve", min_max_var); + LOG_API_CALL_END ("resize", min_max_var); } -int Solver::reserve_difference (int number_of_vars) { - TRACE ("reserve_difference", number_of_vars); +int Solver::declare_more_variables (int number_of_vars) { + TRACE ("declare_more_variables", number_of_vars); REQUIRE_VALID_STATE (); transition_to_steady_state (); external->reset_extended (); int new_max_var = external->max_var + number_of_vars; external->init (new_max_var); - LOG_API_CALL_END ("reserve_difference", number_of_vars); + LOG_API_CALL_END ("declare_more_variables", number_of_vars); + return new_max_var; +} + +int Solver::declare_one_more_variable () { + TRACE ("declare_one_more_variable"); + REQUIRE_VALID_STATE (); + transition_to_steady_state (); + external->reset_extended (); + int new_max_var = external->max_var + 1; + external->init (new_max_var); + LOG_API_CALL_END ("declare_one_more_variable"); return new_max_var; } @@ -673,8 +715,9 @@ void Solver::assume (int lit) { int Solver::lookahead () { TRACE ("lookahead"); REQUIRE_VALID_OR_SOLVING_STATE (); + transition_to_steady_state (); int lit = external->lookahead (); - TRACE ("lookahead"); + LOG_API_CALL_END ("lookahead", lit); return lit; } @@ -730,8 +773,9 @@ int Solver::propagate () { void Solver::implied (std::vector &entrailed) { TRACE ("implied"); REQUIRE_VALID_STATE (); - REQUIRE (state () == INCONCLUSIVE, - "can only get implied literals only in unknown state"); + REQUIRE ( + state () == INCONCLUSIVE || state () == SATISFIED, + "can only get implied literals only in unknown or satisfied state"); external->conclude_unknown (); external->implied (entrailed); if (tracing_nb_lidrup_env_var_method) @@ -775,11 +819,13 @@ int Solver::call_external_solve_and_check_results (bool preprocess_only) { FATAL ("copying assumption checker failed"); } #endif +#if 0 // was necessary when INCONCLUSIVE state did not exist if (!res) { external->reset_assumptions (); external->reset_constraint (); external->reset_concluded (); } +#endif return res; } @@ -799,23 +845,33 @@ int Solver::simplify (int rounds) { REQUIRE (rounds >= 0, "negative number of simplification rounds '%d'", rounds); internal->limit ("preprocessing", rounds); + const int lucky = internal->opts.lucky; + internal->opts.lucky = 0; const int res = call_external_solve_and_check_results (true); + internal->opts.lucky = lucky; LOG_API_CALL_RETURNS ("simplify", rounds, res); return res; } /*------------------------------------------------------------------------*/ -int Solver::val (int lit) { - TRACE ("val", lit); +int Solver::val ( + int lit, bool use_default_value_for_declared_but_not_used_variable) { + LOG_API_CALL_BEGIN ( + "val", lit, + (int) use_default_value_for_declared_but_not_used_variable); REQUIRE_VALID_STATE (); REQUIRE_VALID_LIT (lit); REQUIRE (state () == SATISFIED, "can only get value in satisfied state"); + if (!use_default_value_for_declared_but_not_used_variable) + REQUIRE (lit < external->max_var, "lit of undeclare variable"); if (!external->extended) external->extend (); external->conclude_sat (); int res = external->ival (lit); - LOG_API_CALL_RETURNS ("val", lit, res); + LOG_API_CALL_RETURNS ( + "val", lit, use_default_value_for_declared_but_not_used_variable, + res); CADICAL_assert (state () == SATISFIED); CADICAL_assert (res == lit || res == -lit); return res; @@ -1247,9 +1303,10 @@ bool Solver::disconnect_proof_tracer (FileTracer *tracer) { void Solver::conclude () { TRACE ("conclude"); REQUIRE_VALID_STATE (); - REQUIRE (state () == UNSATISFIED || state () == SATISFIED || - state () == INCONCLUSIVE, - "can only conclude in satisfied, unsatisfied or inconclusive state"); + REQUIRE ( + state () == UNSATISFIED || state () == SATISFIED || + state () == INCONCLUSIVE, + "can only conclude in satisfied, unsatisfied or inconclusive state"); if (state () == UNSATISFIED) internal->conclude_unsat (); else if (state () == SATISFIED) @@ -1759,6 +1816,33 @@ void Solver::error (const char *fmt, ...) { va_end (ap); } +int64_t Solver::get_statistic_value (const char *opt) const { + REQUIRE_INITIALIZED (); + if (!strcmp (opt, "conflicts")) + return internal->stats.conflicts; + if (!strcmp (opt, "decisions")) + return internal->stats.decisions; + if (!strcmp (opt, "ticks")) + return internal->stats.ticks.search[0] + + internal->stats.ticks.search[1]; + if (!strcmp (opt, "propagations")) + return internal->stats.propagations.search; + if (!strcmp (opt, "clauses")) + return internal->stats.current.total; + if (!strcmp (opt, "redundant")) + return internal->stats.current.redundant; + if (!strcmp (opt, "irredundant")) + return internal->stats.current.irredundant; + if (!strcmp (opt, "fixed")) + return internal->stats.all.fixed; + if (!strcmp (opt, "eliminated")) + return internal->stats.all.eliminated + + internal->stats.all.fasteliminated; + if (!strcmp (opt, "subsitutued")) + return internal->stats.all.substituted; + return -1; +} + /*------------------------------------------------------------------------*/ int Solver::clauses () { diff --git a/src/sat/cadical/cadical_stats.cpp b/src/sat/cadical/cadical_stats.cpp index 8b07745d6..b6621f15d 100644 --- a/src/sat/cadical/cadical_stats.cpp +++ b/src/sat/cadical/cadical_stats.cpp @@ -55,13 +55,12 @@ void Stats::print (Internal *internal) { propagations += stats.propagations.search; propagations += stats.propagations.transred; propagations += stats.propagations.vivify; - propagations += stats.propagations.walk; int64_t vivified = stats.vivifysubs + stats.vivifystrs; int64_t searchticks = stats.ticks.search[0] + stats.ticks.search[1]; int64_t inprobeticks = stats.ticks.vivify + stats.ticks.probe + stats.ticks.factor + stats.ticks.ternary + - stats.ticks.sweep; + stats.ticks.sweep + stats.ticks.backbone; int64_t totalticks = searchticks + inprobeticks; size_t extendbytes = internal->external->extension.size (); @@ -96,6 +95,25 @@ void Stats::print (Internal *internal) { PRT (" backtracked: %15" PRId64 " %10.2f %% of conflicts", stats.backtracks, percent (stats.backtracks, stats.conflicts)); } + if (all || stats.incremental_decay) { + PRT ("inc-decay: %15" PRId64 " %10.2f %% per search", + stats.incremental_decay, + percent (stats.incremental_decay, stats.searches)); + } + if (all || stats.backbone.rounds) { + PRT ("backbone: %15" PRId64 " %10.2f %% of vars", + stats.backbone.probes, + percent (stats.backbone.probes, stats.vars)); + PRT (" rounds: %15" PRId64 " %10.2f per phase", + stats.backbone.rounds, + relative (stats.backbone.rounds, stats.backbone.phases)); + PRT (" phases: %15" PRId64 " %10.2f interval", + stats.backbone.phases, + relative (stats.conflicts, stats.backbone.phases)); + PRT (" units: %15" PRId64 " %10.2f per phase", + stats.backbone.units, + relative (stats.backbone.units, stats.backbone.phases)); + } if (all || stats.conditioned) { PRT ("conditioned: %15" PRId64 " %10.2f %% of irredundant clauses", @@ -147,6 +165,15 @@ void Stats::print (Internal *internal) { PRT (" searched: %15" PRId64 " %10.2f per decision", stats.searched, relative (stats.searched, stats.decisions)); } + if (all || stats.randec.random_decisions) { + PRT ("rand. dec phase: %15" PRId64 " %10.2f per interval", + stats.randec.random_decision_phases, + relative (stats.randec.random_decision_phases, stats.decisions)); + PRT ("random decs: %15" PRId64 " %10.2f per phase", + stats.randec.random_decisions, + relative (stats.randec.random_decisions, + stats.randec.random_decision_phases)); + } if (all || stats.all.eliminated) { PRT ("eliminated: %15" PRId64 " %10.2f %% of all variables", stats.all.eliminated, percent (stats.all.eliminated, stats.vars)); @@ -301,7 +328,7 @@ void Stats::print (Internal *internal) { PRT ("learned: %15" PRId64 " %10.2f %% per conflict", stats.learned.clauses, percent (stats.learned.clauses, stats.conflicts)); - PRT ("@ bumped: %15" PRId64 " %10.2f per learned", + PRT (" bumped: %15" PRId64 " %10.2f per learned", stats.bumped, relative (stats.bumped, stats.learned.clauses)); PRT (" recomputed: %15" PRId64 " %10.2f %% per learned", stats.recomputed, @@ -387,9 +414,6 @@ void Stats::print (Internal *internal) { PRT (" vivifyprops: %15" PRId64 " %10.2f %% of propagations", stats.propagations.vivify, percent (stats.propagations.vivify, propagations)); - PRT (" walkprops: %15" PRId64 " %10.2f %% of propagations", - stats.propagations.walk, - percent (stats.propagations.walk, propagations)); if (all || stats.reactivated) { PRT ("reactivated: %15" PRId64 " %10.2f %% of all variables", stats.reactivated, percent (stats.reactivated, stats.vars)); @@ -610,6 +634,8 @@ void Stats::print (Internal *internal) { stats.ticks.search[0], percent (stats.ticks.search[0], searchticks)); PRT (" inprobeticks: %15" PRId64 " %10.2f %% totalticks", inprobeticks, percent (inprobeticks, totalticks)); + PRT (" backboneticks:%15" PRId64 " %10.2f %% searchticks", + stats.ticks.backbone, percent (stats.ticks.backbone, searchticks)); PRT (" factorticks: %15" PRId64 " %10.2f %% searchticks", stats.ticks.factor, percent (stats.ticks.factor, searchticks)); PRT (" probeticks: %15" PRId64 " %10.2f %% searchticks", @@ -620,6 +646,20 @@ void Stats::print (Internal *internal) { stats.ticks.ternary, percent (stats.ticks.ternary, searchticks)); PRT (" vivifyticks: %15" PRId64 " %10.2f %% searchticks", stats.ticks.vivify, percent (stats.ticks.vivify, searchticks)); + PRT (" walkticks: %15" PRId64 " %10.2f %% searchticks", + stats.ticks.walk, percent (stats.ticks.walk, searchticks)); + PRT (" walkflipticks:%15" PRId64 " %10.2f %% searchticks", + stats.ticks.walkflip, percent (stats.ticks.walkflip, searchticks)); + PRT (" walkflipbrk: %15" PRId64 " %10.2f %% searchticks", + stats.ticks.walkflipbroken, + percent (stats.ticks.walkflipbroken, searchticks)); + PRT (" walkflipWL: %15" PRId64 " %10.2f %% searchticks", + stats.ticks.walkflipWL, + percent (stats.ticks.walkflipWL, searchticks)); + PRT (" walkpickticks:%15" PRId64 " %10.2f %% searchticks", + stats.ticks.walkpick, percent (stats.ticks.walkpick, searchticks)); + PRT (" walkbreak: %15" PRId64 " %10.2f %% searchticks", + stats.ticks.walkbreak, percent (stats.ticks.walkbreak, searchticks)); if (all) { PRT ("tier recomputed: %15" PRId64 " %10.2f interval", stats.tierecomputed, @@ -646,6 +686,18 @@ void Stats::print (Internal *internal) { relative (stats.conflicts, stats.vivifications)); PRT (" vivifychecks: %15" PRId64 " %10.2f %% per conflict", stats.vivifychecks, percent (stats.vivifychecks, stats.conflicts)); + const int64_t vivified = stats.vivifiedtier1 + stats.vivifiedtier2 + + stats.vivifiedtier3 + stats.vivifiedirred; + PRT (" vivified: %15" PRId64 " %10.2f %% per check", vivified, + percent (vivified, stats.vivifychecks)); + PRT (" vified-irred: %15" PRId64 " %10.2f %% per vivified", + stats.vivifiedirred, percent (stats.vivifiedirred, vivified)); + PRT (" vified-tier1: %15" PRId64 " %10.2f %% per vivified", + stats.vivifiedtier1, percent (stats.vivifiedtier1, vivified)); + PRT (" vified-tier2: %15" PRId64 " %10.2f %% per vivified", + stats.vivifiedtier2, percent (stats.vivifiedtier2, vivified)); + PRT (" vified-tier3: %15" PRId64 " %10.2f %% per vivified", + stats.vivifiedtier3, percent (stats.vivifiedtier3, vivified)); PRT (" vivifysched: %15" PRId64 " %10.2f %% checks per scheduled", stats.vivifysched, percent (stats.vivifychecks, stats.vivifysched)); @@ -656,6 +708,9 @@ void Stats::print (Internal *internal) { stats.vivifyinst, percent (stats.vivifyinst, stats.vivifychecks)); PRT (" vivifysubs: %15" PRId64 " %10.2f %% per subsumed", stats.vivifysubs, percent (stats.vivifysubs, stats.subsumed)); + PRT (" vivifyflushed: %15" PRId64 " %10.2f %% per subsumed", + stats.vivifyflushed, + percent (stats.vivifyflushed, stats.subsumed)); PRT (" vivifysubred: %15" PRId64 " %10.2f %% per subs", stats.vivifysubred, percent (stats.vivifysubred, stats.vivifysubs)); @@ -681,13 +736,31 @@ void Stats::print (Internal *internal) { percent (stats.vivifydemote, stats.vivifystrs)); PRT (" vivifydecs: %15" PRId64 " %10.2f per checks", stats.vivifydecs, relative (stats.vivifydecs, stats.vivifychecks)); - PRT (" vivifyreused: %15" PRId64 " %10.2f %% per decision", + PRT (" vivifyreused: %15" PRId64 + " %10.2f %% per non-reused decision", stats.vivifyreused, percent (stats.vivifyreused, stats.vivifydecs)); } if (all || stats.walk.count) { PRT ("walked: %15" PRId64 " %10.2f interval", stats.walk.count, relative (stats.conflicts, stats.walk.count)); + if (all || stats.warmup.count) { + PRT (" prop-warmup: %15" PRId64 " %10.2f per warmup", + stats.warmup.propagated, + relative (stats.warmup.propagated, stats.warmup.count)); + PRT (" dec-warmup: %15" PRId64 " %10.2f per warmup", + stats.warmup.decision, + relative (stats.warmup.decision, stats.warmup.count)); + PRT (" dummydec-w: %15" PRId64 " %10.2f per warmup", + stats.warmup.dummydecision, + relative (stats.warmup.dummydecision, stats.warmup.count)); + PRT (" conflicts: %15" PRId64 " %10.2f per warmup", + stats.warmup.conflicts, + relative (stats.warmup.conflicts, stats.warmup.count)); + PRT (" warmup: %15" PRId64 " %10.2f per walk", + stats.warmup.count, + relative (stats.warmup.count, stats.walk.count)); + } #ifndef CADICAL_QUIET if (internal->profiles.walk.value > 0) PRT (" flips: %15" PRId64 " %10.2f M per second", @@ -704,6 +777,9 @@ void Stats::print (Internal *internal) { percent (stats.walk.minimum, stats.added.irredundant)); PRT (" broken: %15" PRId64 " %10.2f per flip", stats.walk.broken, relative (stats.walk.broken, stats.walk.flips)); + PRT (" improved: %15" PRId64 " %10.2f per walk", + stats.walk.improved, + relative (stats.walk.improved, stats.walk.count)); } if (all || stats.weakened) { PRT ("weakened: %15" PRId64 " %10.2f average size", @@ -736,13 +812,23 @@ void Stats::print (Internal *internal) { PRT (" unaries: %15" PRId64 " %10.2f per round", stats.congruence.unaries, relative (stats.congruence.rounds, stats.congruence.unaries)); - PRT (" rewri.-ands: %15" PRId64 " %10.2f per round", + int64_t rewritten = stats.congruence.rewritten_ands + + stats.congruence.rewritten_xors + + stats.congruence.rewritten_ites; + PRT (" rewritten: %15" PRId64 " %10.2f per round", rewritten, + percent (rewritten, stats.congruence.rounds)); + PRT (" rewri.-ands: %15" PRId64 " %10.2f per rewritten", stats.congruence.rewritten_ands, - relative (stats.congruence.rounds, - stats.congruence.rewritten_ands)); - PRT (" subsumed: %15" PRId64 " %10.2f per round", + percent (stats.congruence.rewritten_ands, rewritten)); + PRT (" rewri.-xors: %15" PRId64 " %10.2f%% per rewritten", + stats.congruence.rewritten_xors, + percent (stats.congruence.rewritten_xors, rewritten)); + PRT (" rewri.-ites: %15" PRId64 " %10.2f%% per rewritten", + stats.congruence.rewritten_ites, + percent (stats.congruence.rewritten_ites, rewritten)); + PRT (" subsumed: %15" PRId64 " %10.2f%% per round", stats.congruence.subsumed, - relative (stats.congruence.rounds, stats.congruence.subsumed)); + relative (stats.congruence.subsumed, stats.congruence.rounds)); } LINE (); @@ -750,6 +836,10 @@ void Stats::print (Internal *internal) { tout.magenta_code (), internal->opts.realtime ? "real" : "process", tout.normal_code ()); + SECTION ("glue usage"); + + internal->print_tier_usage_statistics (); + #endif // ifndef CADICAL_QUIET } @@ -810,6 +900,8 @@ void LratChecker::print_stats () { stats.original, percent (stats.original, stats.added)); MSG ("derived: %15" PRId64 " %10.2f %% of all clauses", stats.derived, percent (stats.derived, stats.added)); + MSG ("rat: %15" PRId64 " %10.2f %% of derived clauses", + stats.rat, percent (stats.rat, stats.derived)); MSG ("deleted: %15" PRId64 " %10.2f %% of all clauses", stats.deleted, percent (stats.deleted, stats.added)); MSG ("finalized: %15" PRId64 " %10.2f %% of all clauses", diff --git a/src/sat/cadical/cadical_subsume.cpp b/src/sat/cadical/cadical_subsume.cpp index b75040ca8..c2a5fa1b0 100644 --- a/src/sat/cadical/cadical_subsume.cpp +++ b/src/sat/cadical/cadical_subsume.cpp @@ -308,7 +308,6 @@ inline int Internal::try_to_subsume_clause (Clause *c, return 0; } - struct subsume_less_noccs { Internal *internal; subsume_less_noccs (Internal *i) : internal (i) {} diff --git a/src/sat/cadical/cadical_sweep.cpp b/src/sat/cadical/cadical_sweep.cpp index 4dca2b4bc..05eb9eb4c 100644 --- a/src/sat/cadical/cadical_sweep.cpp +++ b/src/sat/cadical/cadical_sweep.cpp @@ -923,6 +923,13 @@ int64_t Internal::add_sweep_binary (sweep_proof_clause pc, int lit, proof->weaken_minus (id, clause); } external->push_binary_clause_on_extension_stack (id, lit, other); + for (auto &tracer : tracers) { + if (externalize (lit) < 0) + break; + const int elit = externalize (lit); + const int eother = externalize (other); + tracer->notify_equivalence (elit, -eother); + } clause.clear (); lrat_chain.clear (); return id; @@ -1639,7 +1646,7 @@ const char *Internal::sweep_variable (Sweeper &sweeper, int idx) { units = stats.sweep_units - units; solved = stats.sweep_solved - solved; #endif - VERBOSE (3, + VERBOSE (4, "complete swept variable %d backbone with %" PRIu64 " units in %" PRIu64 " solver calls", externalize (idx), units, solved); @@ -1915,7 +1922,7 @@ bool Internal::sweep () { const char *res = #endif sweep_variable (sweeper, idx); - VERBOSE (2, "swept[%" PRIu64 "] external variable %d %s", swept, + VERBOSE (3, "swept[%" PRIu64 "] external variable %d %s", swept, externalize (idx), res); if (++swept == limit) { VERBOSE (2, diff --git a/src/sat/cadical/cadical_tier.cpp b/src/sat/cadical/cadical_tier.cpp index 92faf507f..f8f4e0841 100644 --- a/src/sat/cadical/cadical_tier.cpp +++ b/src/sat/cadical/cadical_tier.cpp @@ -1,6 +1,8 @@ #include "global.h" #include "internal.hpp" +#include "util.hpp" +#include ABC_NAMESPACE_IMPL_START @@ -14,8 +16,9 @@ void Internal::recompute_tier () { const int64_t delta = stats.tierecomputed >= 16 ? 1u << 16 : (1u << stats.tierecomputed); lim.recompute_tier = stats.conflicts + delta; - LOG ("rescheduling in %zd at %zd (conflicts at %zd)", delta, - lim.recompute_tier, stats.conflicts); + LOG ("rescheduling in %" PRId64 " at %" PRId64 " (conflicts at %" PRId64 + ")", + delta, lim.recompute_tier, stats.conflicts); #ifndef CADICAL_NDEBUG uint64_t total_used = 0; for (auto u : stats.used[stable]) @@ -34,13 +37,21 @@ void Internal::recompute_tier () { stats.bump_used[stable] * opts.tier1limit / 100; uint64_t accumulated_tier2_limit = stats.bump_used[stable] * opts.tier2limit / 100; - uint64_t accumulated_used = 0; - for (size_t glue = 0; glue < stats.used[stable].size (); ++glue) { + tier1[stable] = 1; + tier2[stable] = 1; + uint64_t accumulated_used = stats.used[stable][0]; + size_t glue = 1; + for (; glue < stats.used[stable].size (); ++glue) { const uint64_t u = stats.used[stable][glue]; accumulated_used += u; - if (accumulated_used <= accumulated_tier1_limit) { + if (accumulated_used >= accumulated_tier1_limit) { tier1[stable] = glue; + break; } + } + for (; glue < stats.used[stable].size (); ++glue) { + const uint64_t u = stats.used[stable][glue]; + accumulated_used += u; if (accumulated_used >= accumulated_tier2_limit) { tier2[stable] = glue; break; @@ -48,12 +59,137 @@ void Internal::recompute_tier () { } } - LOG ("tier1 limit = %d in %s mode", tier1[stable], - stable ? "stable" : "focused"); - LOG ("tier2 limit = %d in %s mode", tier2[stable], - stable ? "stable" : "focused"); + CADICAL_assert (tier1[stable] > 0); + + CADICAL_assert (tier1[stable]); + CADICAL_assert (tier2[stable]); + + if (tier1[stable] < opts.tier1minglue) { + LOG ("tier1 limit of %d is too low, setting %d instead", tier1[stable], + opts.tier1minglue); + tier1[stable] = opts.tier1minglue; + } + if (tier2[stable] < opts.tier2minglue) { + LOG ("tier2 limit of %d is too low, setting %d instead", tier2[stable], + opts.tier2minglue); + tier2[stable] = opts.tier2minglue; + } + if (tier1[stable] >= tier2[stable]) + tier2[stable] = tier1[stable] + 1; + CADICAL_assert (tier2[stable] > tier1[stable]); + + PHASE ("retiered", stats.tierecomputed, + "tier1 limit = %d in %s mode, tier2 limit = %d in %s mode", + tier1[stable], stable ? "stable" : "focused", tier2[stable], + stable ? "stable" : "focused"); } +void Internal::print_tier_usage_statistics () { + recompute_tier (); + + for (auto stable : {false, true}) { + unsigned total_used = 0; + for (size_t glue = 0; glue < stats.used[stable].size (); ++glue) + total_used += stats.used[stable][glue]; + + const std::string mode = stable ? "stable" : "focused"; + const size_t tier1 = internal->tier1[stable]; + const size_t tier2 = internal->tier2[stable]; + if (tier1 > tier2 && opts.reducetier1glue > opts.reducetier2glue) { + MSG ("tier1 > tier 2 due to the options, giving up"); + break; + } + CADICAL_assert (tier1 <= tier2); + unsigned prefix, suffix; + unsigned span = tier2 - tier1 + 1; + const unsigned max_printed = 5; + CADICAL_assert (max_printed & 1), CADICAL_assert (max_printed / 2 > 0); + if (span > max_printed) { + prefix = tier1 + max_printed / 2 - 1; + suffix = tier2 - max_printed / 2 + 1; + } else + prefix = UINT_MAX, suffix = 0; + + uint64_t accumulated_middle = 0; + int glue_digits = 1, clauses_digits = 1; + for (unsigned glue = 0; glue <= stats.used[stable].size (); glue++) { + if (glue < tier1) + continue; + uint64_t used = stats.used[stable][glue]; + int tmp_glue = 0, tmp_clauses = 0; + if (glue <= prefix || suffix <= glue) { + tmp_glue = glue; + tmp_clauses = used; + } else { + accumulated_middle += used; + if (glue + 1 == suffix) { + tmp_glue = (prefix + 1) + (glue) + 1; + tmp_clauses = (accumulated_middle); + } + } + if (tmp_glue > glue_digits) + glue_digits = tmp_glue; + if (tmp_clauses > clauses_digits) + clauses_digits = tmp_clauses; + if (glue == tier2) + break; + } + + accumulated_middle = 0; + uint64_t accumulated = 0; + std::string output; + for (unsigned glue = 0; glue <= stats.used[stable].size (); glue++) { + uint64_t used = stats.used[stable][glue]; + accumulated += used; + if (glue < tier1) + continue; + if (glue <= prefix || suffix <= glue + 1) { + output += mode + " glue "; + } + if (glue <= prefix || suffix <= glue) { + std::string glue_str = std::to_string (glue); + output += glue_str; + output += " used " + std::to_string (used); + output += + " clauses " + + std::to_string (percent (used, total_used)).substr (0, 5) + "%"; + output += " accumulated " + + std::to_string (percent (accumulated, total_used)) + .substr (0, 5) + + "%"; + if (glue == tier1) + output += " tier1"; + if (glue == tier2) + output += " tier2"; + MSG ("%s", output.c_str ()); + output.clear (); + } else { + accumulated_middle += used; + if (glue + 1 == suffix) { + std::string glue_str = std::to_string (prefix + 1) + "-" + + std::to_string (suffix - 1); + output += + glue_str + " used " + std::to_string (accumulated_middle); + output += + " clauses " + + std::to_string (percent (accumulated_middle, total_used)) + .substr (0, 5) + + "%"; + output += " accumulated " + + std::to_string (percent (accumulated, total_used)) + .substr (0, 5) + + "%"; + MSG ("%s", output.c_str ()); + output.clear (); + } + } + if (glue == tier2) + break; + } + + LINE (); + } +} } // namespace CaDiCaL ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_veripbtracer.cpp b/src/sat/cadical/cadical_veripbtracer.cpp index b1f7c95a7..4d743bf9b 100644 --- a/src/sat/cadical/cadical_veripbtracer.cpp +++ b/src/sat/cadical/cadical_veripbtracer.cpp @@ -190,11 +190,41 @@ inline void VeripbTracer::put_binary_id (int64_t id, bool can_be_negative) { /*------------------------------------------------------------------------*/ +void VeripbTracer::veripb_add_derived_clause (int64_t id, bool redundant, + int witness, + const vector &clause) { + CADICAL_assert (witness == clause[0]); + file->put ("red "); + for (const auto &external_lit : clause) { + file->put ("1 "); + if (external_lit < 0) + file->put ('~'); + file->put ('x'); + file->put (abs (external_lit)); + file->put (' '); + } + file->put (">= 1 : "); + file->put ('x'); + file->put (abs (witness)); + file->put (" -> "); + if (witness < 0) + file->put ("0"); + else + file->put ("1"); + file->put (";\n"); + if (!redundant && checked_deletions) { + file->put ("core id "); + file->put (id); + file->put (";\n"); + } +} + void VeripbTracer::veripb_add_derived_clause ( int64_t id, bool redundant, const vector &clause, const vector &chain) { file->put ("pol "); bool first = true; + CADICAL_assert (!chain.empty ()); for (auto p = chain.rbegin (); p != chain.rend (); p++) { auto cid = *p; if (first) { @@ -206,7 +236,7 @@ void VeripbTracer::veripb_add_derived_clause ( file->put (" + s"); } } - file->put ("\n"); + file->put (";\n"); file->put ("e "); for (const auto &external_lit : clause) { file->put ("1 "); @@ -216,13 +246,13 @@ void VeripbTracer::veripb_add_derived_clause ( file->put (abs (external_lit)); file->put (' '); } - file->put (">= 1 ; "); + file->put (">= 1 : "); file->put (id); - file->put (" ;\n"); + file->put (";\n"); if (!redundant && checked_deletions) { file->put ("core id "); file->put (id); - file->put ("\n"); + file->put (";\n"); } } @@ -237,19 +267,19 @@ void VeripbTracer::veripb_add_derived_clause (int64_t id, bool redundant, file->put (abs (external_lit)); file->put (' '); } - file->put (">= 1 ;\n"); + file->put (">= 1;\n"); if (!redundant && checked_deletions) { file->put ("core id "); file->put (id); - file->put ("\n"); + file->put (";\n"); } } void VeripbTracer::veripb_begin_proof (int64_t reserved_ids) { - file->put ("pseudo-Boolean proof version 2.0\n"); + file->put ("pseudo-Boolean proof version 3.0\n"); file->put ("f "); file->put (reserved_ids); - file->put ("\n"); + file->put (";\n"); } void VeripbTracer::veripb_delete_clause (int64_t id, bool redundant) { @@ -261,18 +291,18 @@ void VeripbTracer::veripb_delete_clause (int64_t id, bool redundant) { file->put ("delc "); } file->put (id); - file->put ("\n"); + file->put (";\n"); } void VeripbTracer::veripb_report_status (bool unsat, int64_t conflict_id) { - file->put ("output NONE\n"); + file->put ("output NONE;\n"); if (unsat) { file->put ("conclusion UNSAT : "); file->put (conflict_id); - file->put (" \n"); + file->put (";\n"); } else - file->put ("conclusion NONE\n"); - file->put ("end pseudo-Boolean proof\n"); + file->put ("conclusion NONE;\n"); + file->put ("end pseudo-Boolean proof;\n"); } void VeripbTracer::veripb_strengthen (int64_t id) { @@ -280,7 +310,7 @@ void VeripbTracer::veripb_strengthen (int64_t id) { return; file->put ("core id "); file->put (id); - file->put ("\n"); + file->put (";\n"); } /*------------------------------------------------------------------------*/ @@ -295,12 +325,15 @@ void VeripbTracer::begin_proof (int64_t id) { } void VeripbTracer::add_derived_clause (int64_t id, bool redundant, + int witness, const vector &clause, const vector &chain) { if (file->closed ()) return; LOG ("VERIPB TRACER tracing addition of derived clause[%" PRId64 "]", id); - if (with_antecedents) + if (witness) + veripb_add_derived_clause (id, redundant, witness, clause); + else if (with_antecedents) veripb_add_derived_clause (id, redundant, clause, chain); else veripb_add_derived_clause (id, redundant, clause); diff --git a/src/sat/cadical/cadical_version.cpp b/src/sat/cadical/cadical_version.cpp index a0cceef63..dfc9c79fe 100644 --- a/src/sat/cadical/cadical_version.cpp +++ b/src/sat/cadical/cadical_version.cpp @@ -26,15 +26,15 @@ #ifdef CADICAL_NBUILD #ifndef VERSION -#define VERSION "2.2.0-rc1" +#define VERSION "2.2.0" #endif // VERSION #endif // CADICAL_NBUILD - /*------------------------------------------------------------------------*/ +/*------------------------------------------------------------------------*/ - // The copyright of the code is here. +// The copyright of the code is here. - static const char *COPYRIGHT = "Copyright (c) 2016-2024"; +static const char *COPYRIGHT = "Copyright (c) 2016-2024"; static const char *AUTHORS = "A. Biere, M. Fleury, N. Froleyks, K. Fazekas, F. Pollitt, T. Faller"; static const char *AFFILIATIONS = diff --git a/src/sat/cadical/cadical_vivify.cpp b/src/sat/cadical/cadical_vivify.cpp index 5ec6401cf..44f3cf306 100644 --- a/src/sat/cadical/cadical_vivify.cpp +++ b/src/sat/cadical/cadical_vivify.cpp @@ -52,6 +52,7 @@ namespace CaDiCaL { inline void Internal::vivify_subsume_clause (Clause *subsuming, Clause *subsumed) { + CADICAL_assert (subsumed != subsuming); stats.subsumed++; stats.vivifysubs++; #ifndef CADICAL_NDEBUG @@ -75,7 +76,12 @@ inline void Internal::vivify_subsume_clause (Clause *subsuming, } CADICAL_assert (real_size_subsuming <= real_size_subsumed); #endif - LOG (subsumed, "subsumed"); + LOG (subsumed, "subsumed to be deleted"); + LOG (subsuming, "subsuming to be (un)deleted"); + if (subsumed->redundant && subsuming->redundant && + subsuming->glue < subsumed->glue) { + promote_clause (subsuming, subsumed->glue); + } if (subsumed->redundant) { stats.subred++; ++stats.vivifysubred; @@ -116,7 +122,9 @@ inline void Internal::vivify_subsume_clause (Clause *subsuming, } // demoting a clause (opposite is promote from subsume.cpp) - +// +// This turned out to not be useful, but we kept it for the API call in the +// proof tracers. inline void Internal::demote_clause (Clause *c) { stats.subsumed++; stats.vivifydemote++; @@ -329,112 +337,22 @@ struct vivify_more_noccs_kissat { vivify_more_noccs_kissat (Internal *i) : internal (i) {} bool operator() (int a, int b) { - unsigned t = internal->noccs (a); - unsigned s = internal->noccs (b); - return ((t - s) | ((b - a) & ~(s - t))) >> 31; - } -}; - -// Sort candidate clauses by the number of occurrences (actually by their -// score) of their literals, with clauses to be vivified first last. We -// assume that clauses are sorted w.r.t. more occurring (higher score) -// literals first (with respect to 'vivify_more_noccs'). -// -// For example if there are the following (long irredundant) clauses -// -// 1 -3 -4 (A) -// -1 -2 3 4 (B) -// 2 -3 4 (C) -// -// then we have the following literal scores using Jeroslow Wang scores and -// normalizing it with 2^12 (which is the same as 1<<12): -// -// nocc ( 1) = 2^12 * (2^-3 ) = 512 3. -// nocc (-1) = 2^12 * (2^-4 ) = 256 6. -// nocc ( 2) = 2^12 * (2^-3 ) = 512 4. -// nocc (-2) = 2^12 * (2^-4 ) = 256 7. @1 -// nocc ( 3) = 2^12 * (2^-4 ) = 256 8. -// nocc (-3) = 2^12 * (2^-3 + 2^-3) = 1024 1. -// nocc ( 4) = 2^12 * (2^-3 + 2^-4) = 768 2. -// nocc (-4) = 2^12 * (2^-3 ) = 512 5. -// -// which gives the literal order (according to 'vivify_more_noccs') -// -// -3, 4, 1, 2, -4, -1, -2, 3 -// -// Then sorting the literals in each clause gives -// -// -3 1 -4 (A') -// 4 -1 -2 3 (B') @2 -// -3 4 2 (C') -// -// and finally sorting those clauses lexicographically w.r.t. scores is -// -// -3 4 2 (C') -// -3 1 -4 (A') @3 -// 4 -1 -2 3 (B') -// -// This order is defined by 'vivify_clause_later' which returns 'true' if -// the first clause should be vivified later than the second. - -struct vivify_clause_later { - - Internal *internal; - - vivify_clause_later (Internal *i) : internal (i) {} - - bool operator() (Clause *a, Clause *b) const { - - if (a == b) - return false; - - // First focus on clauses scheduled in the last vivify round but not - // checked yet since then. - // - if (!a->vivify && b->vivify) - return true; - if (a->vivify && !b->vivify) - return false; - - // Among redundant clauses (in redundant mode) prefer small glue. - // - if (a->redundant) { - CADICAL_assert (b->redundant); - if (a->glue > b->glue) - return true; - if (a->glue < b->glue) - return false; - } - - // Then prefer shorter size. - // - if (a->size > b->size) - return true; - if (a->size < b->size) - return false; - - // Now compare literals in the clauses lexicographically with respect to - // the literal order 'vivify_more_noccs' assuming literals are sorted - // decreasingly with respect to that order. - // - const auto eoa = a->end (), eob = b->end (); - auto j = b->begin (); - for (auto i = a->begin (); i != eoa && j != eob; i++, j++) - if (*i != *j) - return vivify_more_noccs (internal) (*j, *i); - - return j == eob; // Prefer shorter clauses to be vivified first. + unsigned s = internal->noccs (a); + unsigned t = internal->noccs (b); + return (((t - s) | + ((internal->vlit (a) - internal->vlit (b)) & ~(s - t))) >> + 31); } }; /*------------------------------------------------------------------------*/ -// Attempting on-the-fly subsumption during sorting when the last line is -// reached in 'vivify_clause_later' above turned out to be trouble some for -// identical clauses. This is the single point where 'vivify_clause_later' -// is not asymmetric and would require 'stable' sorting for determinism. It -// can also not be made 'complete' on-the-fly. Instead of on-the-fly -// subsumption we thus go over the sorted scheduled in a linear scan +// Attempting on-the-fly subsumption during sorting when the last line +// is turned out to be trouble some for identical clauses. This is +// the single point where sorting is not asymmetric and would require +// 'stable' sorting for determinism. It can also not be made +// 'complete' on-the-fly. Instead of on-the-fly subsumption, we +// (optionnaly) thus go over the sorted scheduled in a linear scan // again and remove certain subsumed clauses (the subsuming clause is // syntactically a prefix of the subsumed clause), which includes // those troublesome syntactically identical clauses. @@ -448,16 +366,16 @@ struct vivify_flush_smaller { for (; i != eoa && j != eob; i++, j++) if (*i != *j) return *i < *j; - - return j == eob && i != eoa; + const bool smaller = j == eob && i != eoa; + return smaller; } }; void Internal::flush_vivification_schedule (std::vector &schedule, int64_t &ticks) { ticks += 1 + 3 * cache_lines (schedule.size (), sizeof (Clause *)); + // we cannot use msort here, as we cannot calculate a score stable_sort (schedule.begin (), schedule.end (), vivify_flush_smaller ()); - const auto end = schedule.end (); auto j = schedule.begin (), i = j; @@ -493,6 +411,7 @@ void Internal::flush_vivification_schedule (std::vector &schedule, "flushed %" PRId64 " subsumed scheduled clauses", subsumed); stats.vivifysubs += subsumed; + stats.vivifyflushed += subsumed; if (subsumed) { schedule.resize (j - schedule.begin ()); @@ -507,7 +426,6 @@ void Internal::flush_vivification_schedule (std::vector &schedule, // we schedule a clause to be vivified. For redundant clauses we initially // only try to vivify them if they are likely to survive the next 'reduce' // operation, but this left the last schedule empty most of the time. - bool Internal::consider_to_vivify_clause (Clause *c) { if (c->garbage) return false; @@ -552,7 +470,7 @@ struct vivify_better_watch { // Common code to actually strengthen a candidate clause. The resulting // strengthened clause is communicated through the global 'clause'. -void Internal::vivify_strengthen (Clause *c) { +void Internal::vivify_strengthen (Clause *c, int64_t &ticks) { CADICAL_assert (!clause.empty ()); @@ -566,7 +484,7 @@ void Internal::vivify_strengthen (Clause *c) { // lrat_chain.clear (); done in search_assign stats.vivifyunits++; - bool ok = propagate (); + bool ok = vivify_propagate (ticks); if (!ok) learn_empty_clause (); @@ -613,6 +531,8 @@ void Internal::vivify_strengthen (Clause *c) { ++stats.vivifystrs; } +// When shortening clauses, we have to update the watched literals. +// Actually we don't do it, and instead simply backtrack back enough. void Internal::vivify_sort_watched (Clause *c) { sort (c->begin (), c->end (), vivify_better_watch (this)); @@ -637,18 +557,20 @@ void Internal::vivify_sort_watched (Clause *c) { CADICAL_assert (new_level >= 0); if (new_level < level) - backtrack (new_level); + backtrack_without_updating_phases (new_level); CADICAL_assert (val (lit0) >= 0); CADICAL_assert (val (lit1) >= 0 || (val (lit0) > 0 && val (lit1) < 0 && var (lit0).level <= var (lit1).level)); } + +/*------------------------------------------------------------------------*/ + // Conflict analysis from 'start' which learns a decision only clause. // // We cannot use the stack-based implementation of Kissat, because we need // to iterate over the conflict in topological ordering to produce a valid // LRAT proof - void Internal::vivify_analyze (Clause *start, bool &subsumes, Clause **subsuming, const Clause *const candidate, int implied, @@ -685,7 +607,7 @@ void Internal::vivify_analyze (Clause *start, bool &subsumes, continue; LOG ("unit reason for %d", other); int64_t id = unit_id (-other); - LOG ("adding unit reason %zd for %d", id, other); + LOG ("adding unit reason %" PRId64 " for %s", id, LOGLIT (other)); unit_chain.push_back (id); f.seen = true; analyzed.push_back (other); @@ -703,9 +625,9 @@ void Internal::vivify_analyze (Clause *start, bool &subsumes, analyzed.push_back (other); f.seen = true; } - if (start->redundant) { - const int new_glue = recompute_glue (start); - promote_clause (start, new_glue); + if (reason != start && reason->redundant) { + const int new_glue = recompute_glue (reason); + promote_clause (reason, new_glue); } if (subsumes) { CADICAL_assert (reason); @@ -740,6 +662,9 @@ void Internal::vivify_analyze (Clause *start, bool &subsumes, (void) candidate; } +/*------------------------------------------------------------------------*/ +// First decide which clause (candidate or conflict) to analyze and +// how to do it. We also prepare the clause by removing units. void Internal::vivify_deduce (Clause *candidate, Clause *conflict, int implied, Clause **subsuming, bool &redundant) { @@ -761,7 +686,7 @@ void Internal::vivify_deduce (Clause *candidate, Clause *conflict, } else { reason = (conflict ? conflict : candidate); CADICAL_assert (reason); - CADICAL_assert (!reason->garbage); + CADICAL_assert (!reason->garbage || reason->size == 2); mark2 (candidate); subsumes = (candidate != reason); redundant = reason->redundant; @@ -780,7 +705,7 @@ void Internal::vivify_deduce (Clause *candidate, Clause *conflict, // nevertheless we can use var (l) as if l was still assigned // because var is updated lazily int64_t id = unit_id (-lit); - LOG ("adding unit reason %zd for %d", id, lit); + LOG ("adding unit reason %" PRId64 " for %s", id, LOGLIT (lit)); unit_chain.push_back (id); } f.seen = true; @@ -837,8 +762,10 @@ void Internal::vivify_deduce (Clause *candidate, Clause *conflict, lrat_chain.clear (); } } + /*------------------------------------------------------------------------*/ +// checks whether the clause can be strengthen or not. bool Internal::vivify_shrinkable (const std::vector &sorted, Clause *conflict) { @@ -875,6 +802,7 @@ bool Internal::vivify_shrinkable (const std::vector &sorted, } return false; } + /*------------------------------------------------------------------------*/ inline void Internal::vivify_increment_stats (const Vivifier &vivifier) { @@ -894,10 +822,11 @@ inline void Internal::vivify_increment_stats (const Vivifier &vivifier) { break; } } + /*------------------------------------------------------------------------*/ -// instantiate last literal (see the description of the hack track 2023), -// fix the watches and -// backtrack two level back +// instantiate last literal (see the description of the hack track +// 2023), fix the watches and backtrack two level back to make sure +// the watches are correct. bool Internal::vivify_instantiate ( const std::vector &sorted, Clause *c, std::vector> &lrat_stack, @@ -909,7 +838,7 @@ bool Internal::vivify_instantiate ( CADICAL_assert (!var (lit).reason); CADICAL_assert (var (lit).level); CADICAL_assert (val (lit)); - backtrack (level - 1); + backtrack_without_updating_phases (level - 1); CADICAL_assert (val (lit) == 0); stats.vivifydecs++; vivify_assume (lit); @@ -948,6 +877,7 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { CADICAL_assert (c->size > 2); // see (NO-BINARY) below CADICAL_assert (analyzed.empty ()); + CADICAL_assert (!ignore); c->vivify = false; // mark as checked / tried c->vivified = true; // and globally remember @@ -984,6 +914,8 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { } sort (sorted.begin (), sorted.end (), vivify_more_noccs_kissat (this)); + CADICAL_assert (std::is_sorted (sorted.begin (), sorted.end (), + vivify_more_noccs (this))); // The actual vivification checking is performed here, by assuming the // negation of each of the remaining literals of the clause in turn and @@ -1047,7 +979,7 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { const int decision = control[l].decision; if (-lit == decision) { LOG ("reusing decision %d at decision level %d", decision, l); - stats.vivifyreused++; + ++stats.vivifyreused; if (++l > level) break; } else { @@ -1169,15 +1101,13 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { reverse (lrat_chain.begin (), lrat_chain.end ()); } + // For an explanation of the code, see our POS'25 paper ``Revisiting + // Clause Vivification'' (although for the experiments we focused on + // Kissat instead of CaDiCaL) if (subsuming) { CADICAL_assert (c != subsuming); - LOG (c, "deleting subsumed clause"); - if (c->redundant && subsuming->redundant && c->glue < subsuming->glue) { - promote_clause (c, c->glue); - } vivify_subsume_clause (subsuming, c); res = false; - // stats.vivifysubs++; // already done in vivify_subsume_clause } else if (vivify_shrinkable (sorted, conflict)) { vivify_increment_stats (vivifier); LOG ("vivify succeeded, learning new clause"); @@ -1185,7 +1115,7 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { LOG (lrat_chain, "lrat"); LOG (clause, "learning clause"); conflict = nullptr; // TODO dup from below - vivify_strengthen (c); + vivify_strengthen (c, ticks); res = true; } else if (subsume && c->redundant) { LOG (c, "vivification implied"); @@ -1193,8 +1123,8 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { ++stats.vivifyimplied; res = true; } else if ((conflict || subsume) && !c->redundant && !redundant) { - LOG ("demote clause from irredundant to redundant"); if (opts.vivifydemote) { + LOG ("demote clause from irredundant to redundant"); demote_clause (c); const int new_glue = recompute_glue (c); promote_clause (c, new_glue); @@ -1235,6 +1165,25 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { clear_analyzed_literals (); // TODO why needed? lrat_chain.clear (); conflict = nullptr; + ignore = nullptr; + + if (res) { + switch (vivifier.tier) { + case Vivify_Mode::IRREDUNDANT: + ++stats.vivifiedirred; + break; + case Vivify_Mode::TIER1: + ++stats.vivifiedtier1; + break; + case Vivify_Mode::TIER2: + ++stats.vivifiedtier2; + break; + default: + CADICAL_assert (vivifier.tier == Vivify_Mode::TIER3); + ++stats.vivifiedtier3; + break; + } + } return res; } @@ -1251,9 +1200,9 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { // its own but this is not actually necessary. // -// Non-recursive version, as some bugs have been found. DFS over the -// reasons with preordering (aka we explore the entire reason before -// exploring deeper) +// Non-recursive version, as some bugs have been found by Dominik Schreiber +// during his very large experiments. DFS over the reasons with preordering +// (aka we explore the entire reason before exploring deeper) void Internal::vivify_build_lrat ( int lit, Clause *reason, std::vector> &stack) { @@ -1309,8 +1258,6 @@ void Internal::vivify_build_lrat ( inline void Internal::vivify_chain_for_units (int lit, Clause *reason) { if (!lrat) return; - // LOG ("building chain for units"); bad line for debugging - // equivalence if (opts.chrono && assignment_level (lit, reason)) return; if (level) return; // not decision level 0 CADICAL_assert (lrat_chain.empty ()); @@ -1349,7 +1296,8 @@ vivify_ref create_ref (Internal *internal, Clause *c) { { const int64_t lit_count = internal->noccs (lit); CADICAL_assert (lit_count); - LOG ("checking literal %d with %zd occurrences", lit, lit_count); + LOG ("checking literal %s with %" PRId64 " occurrences", + LOGLIT (lit), lit_count); if (lit_count <= best_count) continue; best_count = lit_count; @@ -1361,32 +1309,35 @@ vivify_ref create_ref (Internal *internal, Clause *c) { CADICAL_assert (best_count); CADICAL_assert (best_count < UINT32_MAX); ref.count[i] = - ((uint64_t) best_count << 32) + (uint64_t) internal->vlit (best); - LOG ("final count at position %d is %d - %d: %lu", i, best, best_count, - ref.count[i]); + (((uint64_t) best_count) << 32) + (uint64_t) internal->vlit (best); + LOG ("final count at position %d is %s - %u: %" PRIu64, i, + LOGLIT (best), best_count, ref.count[i]); lits[i] = best; } return ref; } /*------------------------------------------------------------------------*/ inline void -Internal::vivify_prioritize_leftovers ([[maybe_unused]] char tag, - size_t prioritized, +Internal::vivify_prioritize_leftovers (char tag, size_t prioritized, std::vector &schedule) { if (prioritized) { PHASE ("vivify", stats.vivifications, - "[phase %c] leftovers of %" PRId64 " clause", tag, prioritized); + "[phase %c] leftovers of %zu clause", tag, prioritized); } else { PHASE ("vivify", stats.vivifications, "[phase %c] prioritizing all clause", tag); for (auto c : schedule) c->vivify = true; } +#ifdef CADICAL_QUIET + (void) tag; +#endif const size_t max = opts.vivifyschedmax; if (schedule.size () > max) { if (prioritized) { - std::partition (begin (schedule), end (schedule), - [] (Clause *c) { return c->vivify; }); + // put the left-overs first to be kept + std::stable_partition (begin (schedule), end (schedule), + [] (Clause *c) { return c->vivify; }); } schedule.resize (max); } @@ -1463,6 +1414,8 @@ void Internal::vivify_initialize (Vivifier &vivifier, int64_t &ticks) { if (opts.vivifyflush) { clear_watches (); for (auto &sched : vivifier.schedules) { + // approximation for schedule + ticks += 1 + cache_lines (sched.size (), sizeof (Clause *)); for (const auto &c : sched) { // Literals in scheduled clauses are sorted with their highest score // literals first (as explained above in the example at '@2'). This @@ -1471,41 +1424,26 @@ void Internal::vivify_initialize (Vivifier &vivifier, int64_t &ticks) { // below. // sort (c->begin (), c->end (), vivify_more_noccs (this)); + // ++ticks; } // Flush clauses subsumed by another clause with the same prefix, // which also includes flushing syntactically identical clauses. // flush_vivification_schedule (sched, ticks); } + // approximation for schedule + // ticks += 1 + cache_lines (clauses.size (), sizeof (Clause *)); connect_watches (); // watch all relevant clauses } -#if 0 - connect_watches (); // watch all relevant clauses - vivify_propagate (ticks); -#endif vivify_propagate (ticks); + + PHASE ("vivify", stats.vivifications, + "[phase %c] leftovers out of %zu clauses", 'u', + vivifier.schedule_tier1.size ()); } inline std::vector ¤t_refs_schedule (Vivifier &vivifier) { - switch (vivifier.tier) { - case Vivify_Mode::TIER1: - return vivifier.refs_schedule_tier1; - break; - case Vivify_Mode::TIER2: - return vivifier.refs_schedule_tier2; - break; - case Vivify_Mode::TIER3: - return vivifier.refs_schedule_tier3; - break; - default: - return vivifier.refs_schedule_irred; - break; - } -#if defined(WIN32) && !defined(__MINGW32__) - __assume(false); -#else - __builtin_unreachable (); -#endif + return vivifier.refs_schedule; } inline std::vector ¤t_schedule (Vivifier &vivifier) { @@ -1580,19 +1518,16 @@ void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) { if (terminated_asynchronously ()) return; - PHASE ("vivify", stats.vivifications, - "starting %c vivification round ticks limit %" PRId64 "", - vivifier.tag, ticks_limit); - - PHASE ("vivify", stats.vivifications, - "starting %c vivification round ticks limit %" PRId64 "", - vivifier.tag, ticks_limit); - - CADICAL_assert (watching ()); - auto &refs_schedule = current_refs_schedule (vivifier); auto &schedule = current_schedule (vivifier); + PHASE ("vivify", stats.vivifications, + "starting %c vivification round ticks limit %" PRId64 + " with %zu clauses", + vivifier.tag, ticks_limit, schedule.size ()); + + CADICAL_assert (watching ()); + int64_t ticks = 1 + schedule.size (); // Sort candidates, with first to be tried candidate clause last, i.e., @@ -1609,7 +1544,7 @@ void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) { // // first build the schedule with vivifier_refs - auto end_schedule = end (schedule); + const auto end_schedule = end (schedule); refs_schedule.resize (schedule.size ()); std::transform (begin (schedule), end_schedule, begin (refs_schedule), [&] (Clause *c) { return create_ref (this, c); }); @@ -1629,8 +1564,7 @@ void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) { std::transform (begin (refs_schedule), end (refs_schedule), begin (schedule), [] (vivify_ref c) { return c.clause; }); - erase_vector (refs_schedule); - LOG ("clause after sorting final:"); + refs_schedule.clear (); } else { // skip sorting but still put clauses with the vivify tag at the end to // be done first Kissat does this implicitely by going twice over all @@ -1697,10 +1631,10 @@ void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) { for (auto c : schedule) c->vivify = true; #elif 1 - // if we have gone through all the leftovers, the current clauses are - // leftovers for the next round - if (!schedule.empty () && !schedule.front ()->vivify && - schedule.back ()->vivify) + // if we have gone through all the leftovers (the next candidate + // is not one), all the current clauses are leftovers for the next + // round + if (!schedule.empty () && !schedule.back ()->vivify) for (auto c : schedule) c->vivify = true; #else @@ -1852,8 +1786,15 @@ bool Internal::vivify () { } int64_t init_ticks = 0; - // Refill the schedule every time. Unchecked clauses are 'saved' by + // Refill the schedule every time. Unchecked clauses are 'saved' by // setting their 'vivify' bit, such that they can be tried next time. + // There are two things to denote: the option 'vivifyonce' does what it is + // supposed to do, and it works because the ticks are kept for the next + // schedule. Also, we limit the size of the schedule to limit the cost of + // sorting. + // + // TODO: After limiting, the cost we fixed some heuristics bug, so maybe + // we could increase the limit. // // TODO: count against ticks.vivify directly instead of this unholy // shifting. @@ -1880,9 +1821,9 @@ bool Internal::vivify () { } if (!unsat && tier2effort) { - erase_vector ( - vivifier.schedule_tier1); // save memory (well, not really as we - // already reached the peak memory) + // save memory (well, not really as we + // already reached the peak memory) + erase_vector (vivifier.schedule_tier1); if (limit < stats.ticks.vivify) limit = stats.ticks.vivify; const double effort = (total * tier2effort) / sumeffort; @@ -1953,6 +1894,7 @@ bool Internal::vivify () { STOP_SIMPLIFIER (vivify, VIVIFY); private_steps = false; + CADICAL_assert (!ignore); return true; } diff --git a/src/sat/cadical/cadical_walk.cpp b/src/sat/cadical/cadical_walk.cpp index 13b1bf9f4..6cfb5580b 100644 --- a/src/sat/cadical/cadical_walk.cpp +++ b/src/sat/cadical/cadical_walk.cpp @@ -1,6 +1,8 @@ #include "global.h" +#include "walk.hpp" #include "internal.hpp" +#include "random.hpp" ABC_NAMESPACE_IMPL_START @@ -10,21 +12,45 @@ namespace CaDiCaL { // Random walk local search based on 'ProbSAT' ideas. +// We (based on the Master project from Leah Hohl) tried to ticks +// local search similarly to the other parts of the solver with +// limited success however. +// +// On the problem `ncc_none_5047_6_3_3_3_0_435991723', the broken part +// of walk_flip is very cheap and should not be counted in ticks, but +// on various other problems `9pipe_k' it is very important to ticks +// this part too. + +// using ClauseOrBinary = std::variant ; + 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); + // for efficiency, storing the model each time an improvement is + // found is too costly. Instead we store some of the flips since + // last time and the position of the best model found so far. + Random random; // local random number generator + int64_t ticks; // ticks to approximate run time + int64_t limit; // limit on number of propagations + vector broken; // currently unsatisfied clauses + double epsilon; // smallest considered score + vector table; // break value to score table + vector scores; // scores of candidate literals + std::vector + flips; // remember the flips compared to the last best saved model + int best_trail_pos; + int64_t minimum = INT64_MAX; + std::vector best_values; // best model stored so far + double score (unsigned); // compute score from break count +#ifndef CADICAL_NDEBUG + std::vector current_best_model; // best model found so far +#endif + Walker (Internal *, int64_t limit); + void populate_table (double size); + void push_flipped (int flipped); + void save_walker_trail (bool); + void save_final_minimum (int64_t old_minimum); }; // These are in essence the CB values from Adrian Balint's thesis. They @@ -64,11 +90,18 @@ inline static double fitcbval (double size) { // Initialize the data structures for one local search round. -Walker::Walker (Internal *i, double size, int64_t l) +Walker::Walker (Internal *i, int64_t l) : internal (i), random (internal->opts.seed), // global random seed - propagations (0), limit (l) { + ticks (0), limit (l), best_trail_pos (-1) { random += internal->stats.walk.count; // different seed every time + flips.reserve (i->max_var / 4); + best_values.resize (i->max_var + 1, 0); +#ifndef CADICAL_NDEBUG + current_best_model.resize (i->max_var + 1, 0); +#endif +} +void Walker::populate_table (double size) { // This is the magic constant in ProbSAT (also called 'CB'), which we pick // according to the average size every second invocation and otherwise // just the default '2.0', which turns into the base '0.5'. @@ -87,6 +120,117 @@ Walker::Walker (Internal *i, double size, int64_t l) table.size ()); } +// Add the literal to flip to the queue + +void Walker::push_flipped (int flipped) { + LOG ("push literal %s on the flips", LOGLIT (flipped)); + CADICAL_assert (flipped); + if (best_trail_pos < 0) { + LOG ("not pushing flipped %s to already invalid trail", + LOGLIT (flipped)); + return; + } + + const size_t size_trail = flips.size (); + const size_t limit = internal->max_var / 4 + 1; + if (size_trail < limit) { + flips.push_back (flipped); + LOG ("pushed flipped %s to trail which now has size %zd", + LOGLIT (flipped), size_trail + 1); + return; + } + + if (best_trail_pos) { + LOG ("trail reached limit %zd but has best position %d", limit, + best_trail_pos); + save_walker_trail (true); + flips.push_back (flipped); + LOG ("pushed flipped %s to trail which now has size %zu", + LOGLIT (flipped), flips.size ()); + return; + } else { + LOG ("trail reached limit %zd without best position", limit); + flips.clear (); + LOG ("not pushing %s to invalidated trail", LOGLIT (flipped)); + best_trail_pos = -1; + LOG ("best trail position becomes invalid"); + } +} + +void Walker::save_walker_trail (bool keep) { + CADICAL_assert (best_trail_pos != -1); + CADICAL_assert ((size_t) best_trail_pos <= flips.size ()); +// CADICAL_assert (!keep || best_trail_pos == flips.size()); +#ifdef LOGGING + const size_t size_trail = flips.size (); +#endif + const int kept = flips.size () - best_trail_pos; + LOG ("saving %d values of flipped literals on trail of size %zd", + best_trail_pos, flips.size ()); + + const auto begin = flips.begin (); + const auto best = flips.begin () + best_trail_pos; + const auto end = flips.end (); + + auto it = begin; + for (; it != best; ++it) { + const int lit = *it; + CADICAL_assert (lit); + const signed char value = sign (lit); + const int idx = std::abs (lit); + best_values[idx] = value; + } + if (!keep) { + LOG ("no need to shift and keep remaining %u literals", kept); + return; + } + +#ifndef CADICAL_NDEBUG + for (auto v : internal->vars) { + if (internal->active (v)) + CADICAL_assert (best_values[v] == current_best_model[v]); + } +#endif + LOG ("flushed %u literals %.0f%% from trail", best_trail_pos, + percent (best_trail_pos, size_trail)); + CADICAL_assert (it == best); + auto jt = begin; + for (; it != end; ++it, ++jt) { + CADICAL_assert (jt <= it); + CADICAL_assert (it < end); + *jt = *it; + } + + CADICAL_assert ((int) (end - jt) == best_trail_pos); + CADICAL_assert ((int) (jt - begin) == kept); + flips.resize (kept); + LOG ("keeping %u literals %.0f%% on trail", kept, + percent (kept, size_trail)); + LOG ("reset best trail position to 0"); + best_trail_pos = 0; +} + +// finally export_ the final minimum +void Walker::save_final_minimum (int64_t old_init_minimum) { + CADICAL_assert (minimum <= old_init_minimum); +#ifdef CADICAL_NDEBUG + (void) old_init_minimum; +#endif + + if (!best_trail_pos || best_trail_pos == -1) + LOG ("minimum already saved"); + else + save_walker_trail (false); + + ++internal->stats.walk.improved; + for (auto v : internal->vars) { + if (best_values[v]) + internal->phases.saved[v] = best_values[v]; + else + CADICAL_assert (!internal->active (v)); + } + internal->copy_phases (internal->phases.prev); +} // The scores are tabulated for faster computation (to avoid 'pow'). inline double Walker::score (unsigned i) { @@ -97,15 +241,22 @@ inline double Walker::score (unsigned i) { /*------------------------------------------------------------------------*/ -Clause *Internal::walk_pick_clause (Walker &walker) { +ClauseOrBinary Internal::walk_pick_clause (Walker &walker) { require_mode (WALK); CADICAL_assert (!walker.broken.empty ()); int64_t size = walker.broken.size (); if (size > INT_MAX) size = INT_MAX; int pos = walker.random.pick_int (0, size - 1); - Clause *res = walker.broken[pos]; - LOG (res, "picking random position %d", pos); + ClauseOrBinary res = walker.broken[pos]; +#ifdef LOGGING + Clause *c; + if (!res.is_binary ()) + c = res.clause (); + else + c = res.tagged_binary ().d; + LOG (c, "picking random position %d", pos); +#endif return res; } @@ -114,12 +265,14 @@ Clause *Internal::walk_pick_clause (Walker &walker) { // Compute the number of clauses which would be become unsatisfied if 'lit' // is flipped and set to false. This is called the 'break-count' of 'lit'. -unsigned Internal::walk_break_value (int lit) { - +unsigned Internal::walk_break_value (int lit, int64_t &ticks) { require_mode (WALK); + START (walkbreak); CADICAL_assert (val (lit) > 0); + const int64_t oldticks = ticks; unsigned res = 0; // The computed break-count of 'lit'. + ticks += (1 + cache_lines (watches (lit).size (), sizeof (Clause *))); for (auto &w : watches (lit)) { CADICAL_assert (w.blit != lit); @@ -131,6 +284,11 @@ unsigned Internal::walk_break_value (int lit) { } Clause *c = w.clause; +#ifdef LOGGING + CADICAL_assert (c != dummy_binary); +#endif + ++ticks; + CADICAL_assert (lit == c->literals[0]); // Now try to find a second satisfied literal starting at 'literals[1]' @@ -168,9 +326,10 @@ unsigned Internal::walk_break_value (int lit) { *i = prev; prev = other; } - res++; // Literal 'lit' single satisfies clause 'c'. } + stats.ticks.walkbreak += (ticks - oldticks); + STOP (walkbreak); return res; } @@ -191,6 +350,8 @@ unsigned Internal::walk_break_value (int lit) { int Internal::walk_pick_lit (Walker &walker, Clause *c) { LOG ("picking literal by break-count"); CADICAL_assert (walker.scores.empty ()); + const int64_t old = walker.ticks; + walker.ticks += 1; double sum = 0; int64_t propagations = 0; for (const auto lit : *c) { @@ -201,16 +362,15 @@ int Internal::walk_pick_lit (Walker &walker, Clause *c) { } CADICAL_assert (active (lit)); propagations++; - unsigned tmp = walk_break_value (-lit); + unsigned tmp = walk_break_value (-lit, walker.ticks); double score = walker.score (tmp); LOG ("literal %d break-count %u score %g", lit, tmp, score); walker.scores.push_back (score); sum += score; } + (void) propagations; // TODO actually unused? LOG ("scored %zd literals", walker.scores.size ()); CADICAL_assert (!walker.scores.empty ()); - walker.propagations += propagations; - stats.propagations.walk += propagations; CADICAL_assert (walker.scores.size () <= (size_t) c->size); const double lim = sum * walker.random.generate_double (); LOG ("score sum %g limit %g", sum, lim); @@ -236,13 +396,78 @@ int Internal::walk_pick_lit (Walker &walker, Clause *c) { } walker.scores.clear (); LOG ("picking literal %d by break-count", res); + stats.ticks.walkpick += walker.ticks - old; + return res; +} + +int Internal::walk_pick_lit (Walker &walker, ClauseOrBinary c) { + if (c.is_binary ()) + return walk_pick_lit (walker, c.tagged_binary ()); + return walk_pick_lit (walker, c.clause ()); +} + +int Internal::walk_pick_lit (Walker &walker, const TaggedBinary c) { + LOG ("picking literal by break-count on binary clause [%" PRIu64 "]%s %s", + c.d->id, LOGLIT (c.lit), LOGLIT (c.other)); + CADICAL_assert (walker.scores.empty ()); + const int64_t old = walker.ticks; + double sum = 0; + int64_t propagations = 0; + const std::array clause = {c.lit, c.other}; + for (const auto lit : clause) { + CADICAL_assert (active (lit)); + if (var (lit).level == 1) { + LOG ("skipping assumption %d for scoring", -lit); + continue; + } + CADICAL_assert (active (lit)); + CADICAL_assert (val (lit) < 0); + propagations++; + unsigned tmp = walk_break_value (-lit, walker.ticks); + double score = walker.score (tmp); + LOG ("literal %d break-count %u score %g", lit, tmp, score); + walker.scores.push_back (score); + sum += score; + } + (void) propagations; // TODO unused? + LOG ("scored %zd literals", walker.scores.size ()); + CADICAL_assert (!walker.scores.empty ()); + CADICAL_assert (walker.scores.size () <= (size_t) 2); + const double lim = sum * walker.random.generate_double (); + LOG ("score sum %g limit %g", sum, lim); + const auto end = clause.end (); + auto i = clause.begin (); + auto j = walker.scores.begin (); + int res = 0; + for (;;) { + CADICAL_assert (i != end); + res = *i++; + if (var (res).level > 1) + break; + LOG ("skipping assumption %d without score", -res); + } + sum = *j++; + while (sum <= lim && i != end) { + res = *i++; + if (var (res).level == 1) { + LOG ("skipping assumption %d without score", -res); + continue; + } + sum += *j++; + } + CADICAL_assert (res); + walker.scores.clear (); + LOG ("picking literal %d by break-count", res); + stats.ticks.walkpick += walker.ticks - old; return res; } /*------------------------------------------------------------------------*/ -void Internal::walk_flip_lit (Walker &walker, int lit) { - +// flips a literal unless we run out of ticks. +bool Internal::walk_flip_lit (Walker &walker, int lit) { + START (walkflip); + const int64_t old = walker.ticks; require_mode (WALK); LOG ("flipping assign %d", lit); CADICAL_assert (val (lit) < 0); @@ -254,39 +479,74 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { set_val (idx, tmp); CADICAL_assert (val (lit) > 0); + // we are going to need it anyway and it probably still is in memory + const Watches &ws = watches (-lit); + if (!ws.empty ()) { + const Watch &w = ws[0]; +#ifndef WIN32 + __builtin_prefetch (&w, 0, 1); +#endif + } + // Then remove 'c' and all other now satisfied (made) clauses. { // Simply go over all unsatisfied (broken) clauses. LOG ("trying to make %zd broken clauses", walker.broken.size ()); - // We need to measure (and bound) the memory accesses during traversing - // broken clauses in terms of 'propagations'. This is tricky since we - // are not actually propagating literals. Instead we use the clause - // variable 'ratio' as an approximation to the number of clauses used - // during propagating a literal. Note that we use a one-watch scheme. - // Accordingly the number of broken clauses traversed divided by that - // ratio is an approximation of the number of propagation this would - // correspond to (in terms of memory access). To eagerly update these - // statistics we simply increment the propagation counter after every - // 'ratio' traversed clause. These propagations are particularly - // expensive if the number of broken clauses is large which usually - // happens initially. - // - const double ratio = clause_variable_ratio (); const auto eou = walker.broken.end (); + // broken is in cache given how central it is... but not always (see the + // ncc problems). Value was heuristically determined to give reasonnable + // values. + walker.ticks += + 1 + cache_lines (walker.broken.size (), sizeof (Clause *)); auto j = walker.broken.begin (), i = j; -#ifdef LOGGING +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) int64_t made = 0; #endif - int64_t count = 0; while (i != eou) { - Clause *d = *j++ = *i++; + ClauseOrBinary tagged = *j++ = *i++; - int *literals = d->literals, prev = 0; + if (tagged.is_binary ()) { + const TaggedBinary &b = tagged.tagged_binary (); + const int clit = b.lit; + const int other = b.other; + CADICAL_assert (val (clit) < 0 || val (other) < 0); +#if defined(LOGGING) + CADICAL_assert (b.d->literals[0] == clit || b.d->literals[1] == clit); + CADICAL_assert (b.d->literals[0] == other || b.d->literals[1] == other); +#endif + if (clit == lit || other == lit) { + LOG (b.d, "made"); + const int first_lit = lit; + const int second_lit = clit ^ lit ^ other; +#ifdef LOGGING + watch_binary_literal (first_lit, second_lit, b.d); +#else + // placeholder for the clause, does not matter + watch_binary_literal (first_lit, second_lit, dummy_binary); +#endif + ++walker.ticks; +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) + made++; +#endif + j--; + } else { + LOG (b.d, "still broken"); + CADICAL_assert (val (clit) < 0 && val (other) < 0); + } + continue; + } + + // now the expansive part + Clause *d = tagged.clause (); + ++walker.ticks; + int *literals = d->literals; + LOG (d, "search for replacement"); + int prev = 0; // Find 'lit' in 'd'. // const int size = d->size; @@ -299,18 +559,17 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { break; CADICAL_assert (val (other) < 0); } - // If 'lit' is in 'd' then move it to the front to watch it. // if (prev == lit) { literals[0] = lit; LOG (d, "made"); watch_literal (literals[0], literals[1], d); -#ifdef LOGGING + ++walker.ticks; +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) made++; #endif j--; - } else { // Otherwise the clause is not satisfied, undo shift. for (int i = size - 1; i >= 0; i--) { @@ -319,41 +578,81 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { prev = other; } } - - if (count--) - continue; - - // Update these counters eagerly. Otherwise if we delay the update - // until all clauses are traversed, interrupting the solver has a high - // chance of giving bogus statistics on the number of 'propagations' - // in 'walk', if it is interrupted in this loop. - - count = ratio; // Starting counting down again. - walker.propagations++; - stats.propagations.walk++; + LOG (d, "clause after undoing shift"); } - LOG ("made %" PRId64 " clauses by flipping %d", made, lit); + CADICAL_assert ((int64_t) (j - walker.broken.begin ()) + made == + (int64_t) walker.broken.size ()); walker.broken.resize (j - walker.broken.begin ()); + LOG ("made %" PRId64 " clauses by flipping %d, still %zu broken", made, + lit, walker.broken.size ()); +#ifndef CADICAL_NDEBUG + for (auto d : walker.broken) { + if (d.is_binary ()) { + const TaggedBinary &b = d.tagged_binary (); + CADICAL_assert (val (b.lit) < 0 && val (b.other) < 0); + } else { + for (auto lit : *d.clause ()) + CADICAL_assert (val (lit) < 0); + } + } +#endif + if (walker.ticks > walker.limit) { + STOP (walkflip); + return false; + } } + stats.ticks.walkflipbroken += walker.ticks - old; + + const int64_t old_after_broken = walker.ticks; + // Finally add all new unsatisfied (broken) clauses. { - walker.propagations++; // This really corresponds now to one - stats.propagations.walk++; // propagation (in a one-watch scheme). - #ifdef LOGGING int64_t broken = 0; #endif Watches &ws = watches (-lit); + // probably still in cache + walker.ticks += 1 + cache_lines (ws.size (), sizeof (Clause *)); LOG ("trying to break %zd watched clauses", ws.size ()); for (const auto &w : ws) { Clause *d = w.clause; - LOG (d, "unwatch %d in", -lit); + const bool binary = w.binary (); + if (binary) { + const int other = w.blit; + CADICAL_assert (w.blit != -lit); + if (val (other) > 0) { + LOG (d, "unwatch %d in", -lit); + watch_binary_literal (other, -lit, d); + ++walker.ticks; + continue; + } + LOG (d, "broken"); +#ifdef LOGGING + CADICAL_assert (d != dummy_binary); +#endif + walker.broken.push_back (TaggedBinary (d, -lit, other)); + ++walker.ticks; +#ifdef LOGGING + broken++; +#endif + continue; + } + + if (walker.ticks > walker.limit) { + STOP (walkflip); + return false; + } + // now the expansive part + CADICAL_assert (d->size != 2); + ++walker.ticks; int *literals = d->literals, replacement = 0, prev = -lit; - CADICAL_assert (literals[0] == -lit); + CADICAL_assert (d->size == w.size); const int size = d->size; + CADICAL_assert (literals[0] == -lit); + for (int i = 1; i < size; i++) { const int other = literals[i]; CADICAL_assert (active (other)); @@ -366,19 +665,23 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { break; } if (replacement) { + CADICAL_assert (-lit != replacement); literals[1] = -lit; literals[0] = replacement; - CADICAL_assert (-lit != replacement); watch_literal (replacement, -lit, d); + ++walker.ticks; + LOG (d, "found replacement"); } else { for (int i = size - 1; i > 0; i--) { // undo shift const int other = literals[i]; literals[i] = prev; prev = other; } + CADICAL_assert (literals[0] == -lit); LOG (d, "broken"); walker.broken.push_back (d); + ++walker.ticks; #ifdef LOGGING broken++; #endif @@ -387,6 +690,10 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { LOG ("broken %" PRId64 " clauses by flipping %d", broken, lit); ws.clear (); } + STOP (walkflip); + stats.ticks.walkflipWL += walker.ticks - old_after_broken; + stats.ticks.walkflip += walker.ticks - old; + return true; } /*------------------------------------------------------------------------*/ @@ -395,14 +702,58 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { inline void Internal::walk_save_minimum (Walker &walker) { int64_t broken = walker.broken.size (); - if (broken >= stats.walk.minimum) + if (broken >= walker.minimum) return; - VERBOSE (3, "new global minimum %" PRId64 "", broken); - stats.walk.minimum = broken; + if (broken <= stats.walk.minimum) { + stats.walk.minimum = broken; + VERBOSE (3, "new global minimum %" PRId64 "", broken); + } else { + VERBOSE (3, "new walk minimum %" PRId64 "", broken); + } + + walker.minimum = broken; + +#ifndef CADICAL_NDEBUG for (auto i : vars) { const signed char tmp = vals[i]; if (tmp) - phases.min[i] = phases.saved[i] = tmp; + walker.current_best_model[i] = tmp; + } + if (walker.minimum == 0) { + for (auto c : clauses) { + if (c->garbage) + continue; + if (c->redundant) + continue; + int satisfied = 0; + for (const auto &lit : *c) { + const int tmp = internal->val (lit); + if (tmp > 0) { + LOG (c, "satisfied literal %d in", lit); + satisfied++; + } + } + CADICAL_assert (satisfied); + } + } +#endif + if (walker.best_trail_pos == -1) { + VERBOSE (3, "saving the new walk minimum %" PRId64 "", broken); + for (auto i : vars) { + const signed char tmp = vals[i]; + if (tmp) { + walker.best_values[i] = tmp; +#ifndef CADICAL_NDEBUG + CADICAL_assert (tmp == walker.current_best_model[i]); +#endif + } else { + CADICAL_assert (!active (i)); + } + } + walker.best_trail_pos = 0; + } else { + walker.best_trail_pos = walker.flips.size (); + LOG ("new best trail position %u", walker.best_trail_pos); } } @@ -410,13 +761,6 @@ inline void Internal::walk_save_minimum (Walker &walker) { int Internal::walk_round (int64_t limit, bool prev) { - backtrack (); - if (propagated < trail.size () && !propagate ()) { - LOG ("empty clause after root level propagation"); - learn_empty_clause (); - return 20; - } - stats.walk.count++; clear_watches (); @@ -435,34 +779,15 @@ int Internal::walk_round (int64_t limit, bool prev) { } #endif - PHASE ("walk", stats.walk.count, - "random walk limit of %" PRId64 " propagations", limit); - - // First compute the average clause size for picking the CB constant. - // - double size = 0; - int64_t n = 0; - for (const auto c : clauses) { - if (c->garbage) - continue; - if (c->redundant) { - if (!opts.walkredundant) - continue; - if (!likely_to_be_kept_clause (c)) - continue; - } - size += c->size; - n++; - } - double average_size = relative (size, n); - - PHASE ("walk", stats.walk.count, - "%" PRId64 " clauses average size %.2f over %d variables", n, - average_size, active ()); + PHASE ("walk", stats.walk.count, "random walk limit of %" PRId64 " ticks", + limit); // Instantiate data structures for this local search round. // - Walker walker (internal, average_size, limit); + Walker walker (internal, limit); +#ifndef CADICAL_QUIET + int old_global_minimum = stats.walk.minimum; +#endif bool failed = false; // Inconsistent assumptions? @@ -498,6 +823,8 @@ int Internal::walk_round (int64_t limit, bool prev) { if (!failed) { + // warmup stores the result in phases, not in target + const bool target = opts.warmup ? false : stable || opts.target == 2; for (auto idx : vars) { if (!active (idx)) { LOG ("skipping inactive variable %d", idx); @@ -512,7 +839,7 @@ int Internal::walk_round (int64_t limit, bool prev) { if (prev) tmp = phases.prev[idx]; if (!tmp) - tmp = sign (decide_phase (idx, true)); + tmp = sign (decide_phase (idx, target)); CADICAL_assert (tmp == 1 || tmp == -1); set_val (idx, tmp); CADICAL_assert (level == 2); @@ -524,6 +851,9 @@ int Internal::walk_round (int64_t limit, bool prev) { #ifdef LOGGING int64_t watched = 0; #endif + + double size = 0; + int64_t n = 0; for (const auto c : clauses) { if (c->garbage) @@ -539,6 +869,8 @@ int Internal::walk_round (int64_t limit, bool prev) { int satisfied = 0; // clause satisfied? int *lits = c->literals; + size += c->size; + n++; const int size = c->size; // Move to front satisfied literals and determine whether there @@ -565,16 +897,31 @@ int Internal::walk_round (int64_t limit, bool prev) { } if (satisfied) { - watch_literal (lits[0], lits[1], c); + LOG (c, "pushing to satisfied"); + if (c->size == 2) + watch_binary_literal (lits[0], lits[1], c); + else + watch_literal (lits[0], lits[1], c); #ifdef LOGGING watched++; #endif } else { CADICAL_assert (satisfiable); // at least one non-assumed variable ... LOG (c, "broken"); - walker.broken.push_back (c); + CADICAL_assert (c->size == size); + if (size == 2) + walker.broken.push_back (TaggedBinary (c)); + else + walker.broken.push_back (c); } } + + double average_size = relative (size, n); + walker.populate_table (average_size); + PHASE ("walk", stats.walk.count, + "%" PRId64 " clauses average size %.2f over %d variables", n, + average_size, active ()); + #ifdef LOGGING if (!failed) { int64_t broken = walker.broken.size (); @@ -586,13 +933,14 @@ int Internal::walk_round (int64_t limit, bool prev) { #endif } - int64_t old_global_minimum = stats.walk.minimum; + CADICAL_assert (failed || walker.table.size ()); int res; // Tells caller to continue with local search. if (!failed) { int64_t broken = walker.broken.size (); + int64_t initial_minimum = broken; PHASE ("walk", stats.walk.count, "starting with %" PRId64 " unsatisfied clauses " @@ -601,21 +949,25 @@ int Internal::walk_round (int64_t limit, bool prev) { stats.current.irredundant); walk_save_minimum (walker); + CADICAL_assert (stats.walk.minimum <= walker.minimum); int64_t minimum = broken; #ifndef CADICAL_QUIET int64_t flips = 0; #endif while (!terminated_asynchronously () && !walker.broken.empty () && - walker.propagations < walker.limit) { + walker.ticks < walker.limit) { #ifndef CADICAL_QUIET flips++; #endif stats.walk.flips++; stats.walk.broken += broken; - Clause *c = walk_pick_clause (walker); + ClauseOrBinary c = walk_pick_clause (walker); const int lit = walk_pick_lit (walker, c); - walk_flip_lit (walker, lit); + bool finished = walk_flip_lit (walker, lit); + if (!finished) + break; + walker.push_flipped (lit); broken = walker.broken.size (); LOG ("now have %" PRId64 " broken clauses in total", broken); if (broken >= minimum) @@ -626,33 +978,41 @@ int Internal::walk_round (int64_t limit, bool prev) { walk_save_minimum (walker); } - if (minimum < old_global_minimum) + walker.save_final_minimum (initial_minimum); + +#ifndef CADICAL_QUIET + if (minimum == initial_minimum) { + PHASE ("walk", internal->stats.walk.count, + "%sno improvement %" PRId64 "%s in %" PRId64 " flips and " + "%" PRId64 " ticks", + tout.bright_yellow_code (), minimum, tout.normal_code (), + flips, walker.ticks); + } else if (minimum < old_global_minimum) PHASE ("walk", stats.walk.count, "%snew global minimum %" PRId64 "%s in %" PRId64 " flips and " - "%" PRId64 " propagations", + "%" PRId64 " ticks", tout.bright_yellow_code (), minimum, tout.normal_code (), - flips, walker.propagations); + flips, walker.ticks); else PHASE ("walk", stats.walk.count, "best phase minimum %" PRId64 " in %" PRId64 " flips and " - "%" PRId64 " propagations", - minimum, flips, walker.propagations); + "%" PRId64 " ticks", + minimum, flips, walker.ticks); if (opts.profile >= 2) { - PHASE ("walk", stats.walk.count, - "%.2f million propagations per second", - relative (1e-6 * walker.propagations, - time () - profiles.walk.started)); + PHASE ("walk", stats.walk.count, "%.2f million ticks per second", + 1e-6 * + relative (walker.ticks, time () - profiles.walk.started)); PHASE ("walk", stats.walk.count, "%.2f thousand flips per second", relative (1e-3 * flips, time () - profiles.walk.started)); } else { - PHASE ("walk", stats.walk.count, "%.2f million propagations", - 1e-6 * walker.propagations); + PHASE ("walk", stats.walk.count, "%.2f ticks", 1e-6 * walker.ticks); PHASE ("walk", stats.walk.count, "%.2f thousand flips", 1e-3 * flips); } +#endif if (minimum > 0) { LOG ("minimum %" PRId64 " non-zero thus potentially continue", @@ -671,8 +1031,6 @@ int Internal::walk_round (int64_t limit, bool prev) { "aborted due to inconsistent assumptions"); } - copy_phases (phases.prev); - for (auto idx : vars) if (active (idx)) set_val (idx, 0); @@ -689,20 +1047,48 @@ int Internal::walk_round (int64_t limit, bool prev) { force_phase_messages = false; } #endif - + stats.ticks.walk += walker.ticks; return res; } void Internal::walk () { START_INNER_WALK (); - int64_t limit = stats.propagations.search; + + backtrack (); + if (propagated < trail.size () && !propagate ()) { + LOG ("empty clause after root level propagation"); + learn_empty_clause (); + STOP_INNER_WALK (); + return; + } + + int res = 0; + if (opts.warmup) + res = warmup (); + if (res) { + LOG ("stopping walk due to warmup"); + STOP_INNER_WALK (); + return; + } + const int64_t ticks = stats.ticks.search[0] + stats.ticks.search[1]; + int64_t limit = ticks - last.walk.ticks; + VERBOSE (2, + "walk scheduling: last %" PRId64 " current %" PRId64 + " delta %" PRId64, + last.walk.ticks, ticks, limit); + last.walk.ticks = ticks; limit *= 1e-3 * opts.walkeffort; if (limit < opts.walkmineff) limit = opts.walkmineff; - if (limit > opts.walkmaxeff) - limit = opts.walkmaxeff; + // local search is very cache friendly, so we actually really go over a + // lot of ticks + if (limit > 1e3 * opts.walkmaxeff) { + MSG ("reached maximum efficiency %" PRId64, limit); + limit = 1e3 * opts.walkmaxeff; + } (void) walk_round (limit, false); STOP_INNER_WALK (); + CADICAL_assert (!unsat); } } // namespace CaDiCaL diff --git a/src/sat/cadical/cadical_walk_full_occs.cpp b/src/sat/cadical/cadical_walk_full_occs.cpp new file mode 100644 index 000000000..67fbbb990 --- /dev/null +++ b/src/sat/cadical/cadical_walk_full_occs.cpp @@ -0,0 +1,972 @@ +#include "global.h" + +#include "internal.hpp" +#include + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Random walk local search based on 'ProbSAT' ideas. +struct Tagged { + bool binary : 1; + unsigned counter_pos : 31; +#ifndef CADICAL_NDEBUG + Clause *c; +#endif + explicit Tagged () { CADICAL_assert (false); } + explicit Tagged (Clause *d, unsigned pos) + : binary (d->size == 2), counter_pos (pos) { + CADICAL_assert ((pos & (1 << 31)) == 0); +#ifndef CADICAL_NDEBUG + c = d; +#endif + } +}; + +struct Counter { + unsigned count; // number of false literals + unsigned pos; // pos in the broken clauses + Clause *clause; // pointer to the clause itself + explicit Counter (unsigned c, unsigned p, Clause *d) + : count (c), pos (p), clause (d) {} + explicit Counter (unsigned c, Clause *d) + : count (c), pos (UINT32_MAX), clause (d) {} +}; + +struct WalkerFO { + + Internal *internal; + + // for efficiency, storing the model each time an improvement is + // found is too costly. Instead we store some of the flips since + // last time and the position of the best model found so far. + Random random; // local random number generator + int64_t ticks; // ticks to approximate run time + int64_t limit; // limit on number of propagations + vector broken; // currently unsatisfied clauses + double epsilon; // smallest considered score + vector table; // break value to score table + vector scores; // scores of candidate literals + std::vector + flips; // remember the flips compared to the last best saved model + int best_trail_pos; + int64_t minimum = INT64_MAX; + std::vector best_values; // best model found so far + double score (unsigned); // compute score from break count + + std::vector tclauses; + std::vector> tcounters; + + using TOccs = std::vector; + TOccs &occs (int lit) { + const int idx = internal->vlit (lit); + CADICAL_assert ((size_t) idx < tcounters.size ()); + return tcounters[idx]; + } + const TOccs &occs (int lit) const { + const int idx = internal->vlit (lit); + CADICAL_assert ((size_t) idx < tcounters.size ()); + return tcounters[idx]; + } + void connect_clause (int lit, Clause *clause, unsigned pos) { + CADICAL_assert (pos < tclauses.size ()); + CADICAL_assert (tclauses[pos].clause == clause); + LOG (clause, "connecting clause on %d with already in occurrences %zu", + lit, occs (lit).size ()); + occs (lit).push_back (Tagged (clause, pos)); + } + void connect_clause (Clause *clause, unsigned pos) { + CADICAL_assert (pos < tclauses.size ()); + CADICAL_assert (tclauses[pos].clause == clause); + for (auto lit : *clause) + connect_clause (lit, clause, pos); + } + + void check_occs () const { +#ifndef CADICAL_NDEBUG + for (auto lit : internal->lits) { + for (auto w : occs (lit)) { + CADICAL_assert (w.counter_pos < tclauses.size ()); + } + } + + unsigned unsatisfied = 0; + for (auto c : tclauses) { + unsigned count = 0; + LOG (c.clause, "checking clause with counter %d:", c.count); + for (auto lit : *c.clause) { + if (internal->val (lit) > 0) + ++count; + } + CADICAL_assert (count == c.count); + if (!count) + ++unsatisfied; + } + CADICAL_assert (broken.size () == unsatisfied); +#endif + } + void check_broken () const { +#ifndef CADICAL_NDEBUG + for (size_t i = 0; i < broken.size (); ++i) { + const Tagged t = broken[i]; + CADICAL_assert (t.c); + CADICAL_assert (t.counter_pos < tclauses.size ()); + CADICAL_assert (tclauses[t.counter_pos].clause == t.c); + for (auto lit : *t.c) { + CADICAL_assert (internal->val (lit) < 0); + } + } +#endif + } + + void check_all () const { + check_broken (); + check_occs (); + } + + static const uint32_t invalid_position = UINT32_MAX; + void make_clause (Tagged t); + + WalkerFO (Internal *, double size, int64_t limit); + void push_flipped (int flipped); + void save_walker_trail (bool); + void save_final_minimum (int64_t old_minimum); + void make_clauses_along_occurrences (int lit); + void make_clauses_along_unsatisfied (int lit); + void make_clauses (int lit); + void break_clauses (int lit); + unsigned walk_full_occs_pick_clause (); + void walk_full_occs_flip_lit (int lit); + int walk_full_occs_pick_lit (Clause *); + unsigned walk_full_occs_break_value (int lit); +}; + +// These are in essence the CB values from Adrian Balint's thesis. They +// denote the inverse 'cb' of the base 'b' of the (probability) weight +// 'b^-i' for picking a literal with the break value 'i' (first column is +// the 'size', second the 'CB' value). + +static double cbvals[][2] = { + {0.0, 2.00}, {3.0, 2.50}, {4.0, 2.85}, {5.0, 3.70}, + {6.0, 5.10}, {7.0, 7.40}, // Adrian has '5.4', but '7.4' looks better. +}; + +static const int ncbvals = sizeof cbvals / sizeof cbvals[0]; + +// We interpolate the CB values for uniform random SAT formula to the non +// integer situation of average clause size by piecewise linear functions. +// +// y2 - y1 +// ------- * (x - x1) + y1 +// x2 - x1 +// +// where 'x' is the average size of clauses and 'y' the CB value. + +inline static double fitcbval (double size) { + int i = 0; + while (i + 2 < ncbvals && + (cbvals[i][0] > size || cbvals[i + 1][0] < size)) + i++; + const double x2 = cbvals[i + 1][0], x1 = cbvals[i][0]; + const double y2 = cbvals[i + 1][1], y1 = cbvals[i][1]; + const double dx = x2 - x1, dy = y2 - y1; + CADICAL_assert (dx); + const double res = dy * (size - x1) / dx + y1; + CADICAL_assert (res > 0); + return res; +} + +// Initialize the data structures for one local search round. + +WalkerFO::WalkerFO (Internal *i, double size, int64_t l) + : internal (i), random (internal->opts.seed), // global random seed + ticks (0), limit (l), best_trail_pos (-1) { + random += internal->stats.walk.count; // different seed every time + flips.reserve (i->max_var / 4); + best_values.resize (i->max_var + 1, 0); + tcounters.resize (i->max_var * 2 + 2); + + // This is the magic constant in ProbSAT (also called 'CB'), which we pick + // according to the average size every second invocation and otherwise + // just the default '2.0', which turns into the base '0.5'. + // + const bool use_size_based_cb = (internal->stats.walk.count & 1); + const double cb = use_size_based_cb ? fitcbval (size) : 2.0; + CADICAL_assert (cb); + const double base = 1 / cb; // scores are 'base^0,base^1,base^2,... + + double next = 1; + for (epsilon = next; next; next = epsilon * base) + table.push_back (epsilon = next); + + PHASE ("walk", internal->stats.walk.count, + "CB %.2f with inverse %.2f as base and table size %zd", cb, base, + table.size ()); +} + +// Add the literal to flip to the queue + +void WalkerFO::push_flipped (int flipped) { + LOG ("push literal %s on the flips", LOGLIT (flipped)); + CADICAL_assert (flipped); + if (best_trail_pos < 0) { + LOG ("not pushing flipped %s to already invalid trail", + LOGLIT (flipped)); + return; + } + + const size_t size_trail = flips.size (); + const size_t limit = internal->max_var / 4 + 1; + if (size_trail < limit) { + flips.push_back (flipped); + LOG ("pushed flipped %s to trail which now has size %zd", + LOGLIT (flipped), size_trail + 1); + return; + } + + if (best_trail_pos) { + LOG ("trail reached limit %zd but has best position %d", limit, + best_trail_pos); + save_walker_trail (true); + flips.push_back (flipped); + LOG ("pushed flipped %s to trail which now has size %zu", + LOGLIT (flipped), flips.size ()); + return; + } else { + LOG ("trail reached limit %zd without best position", limit); + flips.clear (); + LOG ("not pushing %s to invalidated trail", LOGLIT (flipped)); + best_trail_pos = -1; + LOG ("best trail position becomes invalid"); + } +} + +void WalkerFO::save_walker_trail (bool keep) { + CADICAL_assert (best_trail_pos != -1); + CADICAL_assert ((size_t) best_trail_pos <= flips.size ()); +#ifdef LOGGING + const size_t size_trail = flips.size (); +#endif + const int kept = flips.size () - best_trail_pos; + LOG ("saving %d values of flipped literals on trail of size %zd", + best_trail_pos, size_trail); + + const auto begin = flips.begin (); + const auto best = flips.begin () + best_trail_pos; + const auto end = flips.end (); + + auto it = begin; + for (; it != best; ++it) { + const int lit = *it; + CADICAL_assert (lit); + const signed char value = sign (lit); + const int idx = std::abs (lit); + best_values[idx] = value; + } + if (!keep) { + LOG ("no need to shift and keep remaining %u literals", kept); + return; + } + +#ifndef CADICAL_NDEBUG + for (auto v : internal->vars) { + if (internal->active (v)) + CADICAL_assert (best_values[v] == internal->phases.saved[v]); + } +#endif + LOG ("flushed %u literals %.0f%% from trail", best_trail_pos, + percent (best_trail_pos, size_trail)); + CADICAL_assert (it == best); + auto jt = begin; + for (; it != end; ++it, ++jt) { + CADICAL_assert (jt <= it); + CADICAL_assert (it < end); + *jt = *it; + } + + CADICAL_assert ((int) (end - jt) == best_trail_pos); + CADICAL_assert ((int) (jt - begin) == kept); + flips.resize (kept); + LOG ("keeping %u literals %.0f%% on trail", kept, + percent (kept, size_trail)); + LOG ("reset best trail position to 0"); + best_trail_pos = 0; +} + +// finally export_ the final minimum +void WalkerFO::save_final_minimum (int64_t old_init_minimum) { + CADICAL_assert (minimum <= old_init_minimum); +#ifdef CADICAL_NDEBUG + (void) old_init_minimum; +#endif + + if (!best_trail_pos || best_trail_pos == -1) + LOG ("minimum already saved"); + else + save_walker_trail (false); + + ++internal->stats.walk.improved; + for (auto v : internal->vars) { + if (best_values[v]) + internal->phases.saved[v] = best_values[v]; + else + CADICAL_assert (!internal->active (v)); + } + internal->copy_phases (internal->phases.prev); +} +// The scores are tabulated for faster computation (to avoid 'pow'). + +inline double WalkerFO::score (unsigned i) { + const double res = (i < table.size () ? table[i] : epsilon); + LOG ("break %u mapped to score %g", i, res); + return res; +} + +/*------------------------------------------------------------------------*/ + +unsigned WalkerFO::walk_full_occs_pick_clause () { + internal->require_mode (internal->WALK); + CADICAL_assert (!broken.empty ()); + int64_t size = broken.size (); + if (size > INT_MAX) + size = INT_MAX; + int pos = random.pick_int (0, size - 1); + unsigned res = broken[pos].counter_pos; + LOG (tclauses[res].clause, "picking random position %d", pos); + return res; +} + +/*------------------------------------------------------------------------*/ + +// Compute the number of clauses which would be become unsatisfied if 'lit' +// is flipped and set to false. This is called the 'break-count' of 'lit'. + +unsigned WalkerFO::walk_full_occs_break_value (int lit) { + + internal->require_mode (internal->WALK); + CADICAL_assert (internal->val (lit) > 0); + START (walkbreak); + + const uint64_t old = ticks; + unsigned res = 0; // The computed break-count of 'lit'. + ticks += + (1 + internal->cache_lines (occs (lit).size (), sizeof (Clause *))); + + for (const auto &w : occs (lit)) { + const unsigned ref = w.counter_pos; + CADICAL_assert (ref < tclauses.size ()); + res += (tclauses[ref].count == 1); + } + + internal->stats.ticks.walkbreak += ticks - old; + STOP (walkbreak); + return res; +} + +/*------------------------------------------------------------------------*/ + +// Given an unsatisfied clause 'c', in which we want to flip a literal, we +// first determine the exponential score based on the break-count of its +// literals and then sample the literals based on these scores. The CB +// value is smaller than one and thus the score is exponentially decreasing +// with the break-count increasing. The sampling works as in 'ProbSAT' and +// 'YalSAT' by summing up the scores and then picking a random limit in the +// range of zero to the sum, then summing up the scores again and picking +// the first literal which reaches the limit. Note, that during incremental +// SAT solving we can not flip assumed variables. Those are assigned at +// decision level one, while the other variables are assigned at two. + +int WalkerFO::walk_full_occs_pick_lit (Clause *c) { + START (walkpick); + LOG ("picking literal by break-count"); + CADICAL_assert (scores.empty ()); + const int64_t old = ++ticks; + double sum = 0; + int64_t propagations = 0; + for (const auto lit : *c) { + CADICAL_assert (internal->active (lit)); + if (internal->var (lit).level == 1) { + LOG ("skipping assumption %d for scoring", -lit); + continue; + } + propagations++; + unsigned tmp = walk_full_occs_break_value (-lit); + double score = this->score (tmp); + LOG ("literal %d break-count %u score %g", lit, tmp, score); + scores.push_back (score); + sum += score; + } + (void) propagations; // TODO unused? + LOG ("scored %zd literals", scores.size ()); + CADICAL_assert (!scores.empty ()); + CADICAL_assert (this->scores.size () <= (size_t) c->size); + const double lim = sum * random.generate_double (); + LOG ("score sum %g limit %g", sum, lim); + + const auto end = c->end (); + auto i = c->begin (); + auto j = scores.begin (); + int res; + for (;;) { + CADICAL_assert (i != end); + res = *i++; + if (internal->var (res).level > 1) + break; + LOG ("skipping assumption %d without score", -res); + } + sum = *j++; + while (sum <= lim && i != end) { + res = *i++; + if (internal->var (res).level == 1) { + LOG ("skipping assumption %d without score", -res); + continue; + } + sum += *j++; + } + scores.clear (); + LOG ("picking literal %d by break-count", res); + internal->stats.ticks.walkpick += ticks - old; + STOP (walkpick); + return res; +} + +/*------------------------------------------------------------------------*/ +void WalkerFO::make_clause (Tagged t) { + CADICAL_assert (t.counter_pos < tclauses.size ()); + // TODO invalidate position in 'unsatisfied' + auto count = tclauses[t.counter_pos].count++; + if (count) { + LOG (tclauses[t.counter_pos].clause, + "already made with counter %d at position %d", + tclauses[t.counter_pos].count, tclauses[t.counter_pos].pos); + CADICAL_assert (tclauses[t.counter_pos].clause == t.c); + CADICAL_assert (tclauses[t.counter_pos].pos == WalkerFO::invalid_position); + return; + } + LOG (tclauses[t.counter_pos].clause, + "make with counter %d at position %d", tclauses[t.counter_pos].count, + tclauses[t.counter_pos].pos); + CADICAL_assert (tclauses[t.counter_pos].pos != WalkerFO::invalid_position); + CADICAL_assert (tclauses[t.counter_pos].pos < broken.size ()); + ++ticks; + auto last = broken.back (); +#ifndef CADICAL_NDEBUG + CADICAL_assert (tclauses[t.counter_pos].clause == t.c); + CADICAL_assert (last.counter_pos < tclauses.size ()); + CADICAL_assert (tclauses[last.counter_pos].clause == last.c); +#endif + unsigned pos = tclauses[t.counter_pos].pos; + CADICAL_assert (pos < broken.size ()); + broken[pos] = last; + // the order is important + tclauses[last.counter_pos].pos = pos; + tclauses[t.counter_pos].pos = WalkerFO::invalid_position; + broken.pop_back (); +} + +void WalkerFO::make_clauses_along_occurrences (int lit) { + const auto &occs = this->occs (lit); + LOG ("making clauses with %s along %zu occurrences", LOGLIT (lit), + occs.size ()); + CADICAL_assert (internal->val (lit) > 0); + size_t made = 0; + ticks += (1 + internal->cache_lines (occs.size (), sizeof (Clause *))); + + for (auto c : occs) { +#if 0 + // only works if make is after break... but we don't want that + if (broken.empty()) { + LOG ("early abort: satisfiable!"); + return; + } +#endif + this->make_clause (c); + made++; + } + LOG ("made %zu clauses by flipping %d, still %zu broken", made, lit, + broken.size ()); + LOG ("made %zu clauses with flipped %s", made, LOGLIT (lit)); + (void) made; +} + +void WalkerFO::make_clauses_along_unsatisfied (int lit) { + LOG ("making clauses with %s along %zu unsatisfied", LOGLIT (lit), + this->broken.size ()); + CADICAL_assert (internal->val (lit) > 0); + size_t made = 0; + // TODO flush made clauses from 'unsatisfied' directly. + // TODO 'stats.make_visited++', 'made++' appropriately. + size_t j = 0; + const size_t size = this->broken.size (); + ticks += + (1 + internal->cache_lines (this->broken.size (), sizeof (Clause *))); + for (size_t i = 0, j = 0; i < size; ++i) { + CADICAL_assert (i >= j); + const auto &c = this->broken[i]; + Counter &d = this->tclauses[c.counter_pos]; + this->broken[j++] = this->broken[i]; + CADICAL_assert (d.pos != WalkerFO::invalid_position); + for (auto other : *d.clause) { + if (lit == other) { + ++made; + --j; + ++d.count; + LOG (d.clause, "made with count %d", d.count); + d.pos = WalkerFO::invalid_position; + break; + } + } + if (d.pos != WalkerFO::invalid_position) + LOG (d.clause, "still broken"); + CADICAL_assert (made + j == i + 1); // assertions holds after incrementing 'i' + } + CADICAL_assert (j <= size); + this->broken.resize (j); + LOG ("made %zu clauses with flipped %s", made, LOGLIT (lit)); + (void) made; +} + +void WalkerFO::make_clauses (int lit) { + START (walkflipWL); + const int64_t old = ticks; + // In babywalk this work because there are not counter + // if (this->occs(lit).size() > broken.size()) + // make_clauses_along_unsatisfied(lit); + // else + make_clauses_along_occurrences (lit); + internal->stats.ticks.walkflipWL += ticks - old; + STOP (walkflipWL); +} + +void WalkerFO::break_clauses (int lit) { + START (walkflipbroken); + const int64_t old = ticks; + LOG ("breaking clauses on %s", LOGLIT (lit)); + // Finally add all new unsatisfied (broken) clauses. + +#ifdef LOGGING + int64_t broken = 0; +#endif + const WalkerFO::TOccs &ws = occs (lit); + ticks += (1 + internal->cache_lines (ws.size (), sizeof (Clause *))); + + LOG ("trying to break %zd clauses", ws.size ()); + + for (const auto &w : ws) { + unsigned pos = w.counter_pos; + Counter &d = tclauses[pos]; +#ifndef CADICAL_NDEBUG + LOG (d.clause, "trying to break"); +#endif + if (--d.count) + continue; + d.pos = this->broken.size (); + ++ticks; + this->broken.push_back (w); +#ifdef LOGGING + broken++; +#endif + } + LOG ("broken %" PRId64 " clauses by flipping %d", broken, lit); + internal->stats.ticks.walkflipbroken += ticks - old; + STOP (walkflipbroken); +} + +void WalkerFO::walk_full_occs_flip_lit (int lit) { + + internal->require_mode (internal->WALK); + LOG ("flipping assign %d", lit); + CADICAL_assert (internal->val (lit) < 0); + const int64_t old = ticks; + + // First flip the literal value. + // + const int tmp = sign (lit); + const int idx = abs (lit); + internal->set_val (idx, tmp); + CADICAL_assert (internal->val (lit) > 0); + + make_clauses (lit); + break_clauses (-lit); + + if (!broken.empty ()) + check_all (); + internal->stats.ticks.walkflip += ticks - old; +} + +/*------------------------------------------------------------------------*/ + +// Check whether to save the current phases as new global minimum. + +inline void Internal::walk_full_occs_save_minimum (WalkerFO &walker) { + int64_t broken = walker.broken.size (); + if (broken >= walker.minimum) + return; + if (broken <= stats.walk.minimum) { + stats.walk.minimum = broken; + VERBOSE (3, "new global minimum %" PRId64 "", broken); + } else { + VERBOSE (3, "new walk minimum %" PRId64 "", broken); + } + + walker.minimum = broken; + +#ifndef CADICAL_NDEBUG + for (auto i : vars) { + const signed char tmp = vals[i]; + if (tmp) + phases.saved[i] = tmp; + } +#endif + + if (walker.best_trail_pos == -1) { + for (auto i : vars) { + const signed char tmp = vals[i]; + if (tmp) { + walker.best_values[i] = tmp; +#ifndef CADICAL_NDEBUG + CADICAL_assert (tmp == phases.saved[i]); +#endif + } + } + walker.best_trail_pos = 0; + } else { + walker.best_trail_pos = walker.flips.size (); + LOG ("new best trail position %u", walker.best_trail_pos); + } +} + +/*------------------------------------------------------------------------*/ + +int Internal::walk_full_occs_round (int64_t limit, bool prev) { + + stats.walk.count++; + + reset_watches (); + + // Remove all fixed variables first (assigned at decision level zero). + // + if (last.collect.fixed < stats.all.fixed) + garbage_collection (); + +#ifndef CADICAL_QUIET + // We want to see more messages during initial local search. + // + if (localsearching) { + CADICAL_assert (!force_phase_messages); + force_phase_messages = true; + } +#endif + + PHASE ("walk", stats.walk.count, + "random walk limit of %" PRId64 " propagations", limit); + + // First compute the average clause size for picking the CB constant. + // + double size = 0; + int64_t n = 0; + for (const auto c : clauses) { + if (c->garbage) + continue; + if (c->redundant) { + if (!opts.walkredundant) + continue; + if (!likely_to_be_kept_clause (c)) + continue; + } + size += c->size; + n++; + } + double average_size = relative (size, n); + + PHASE ("walk", stats.walk.count, + "%" PRId64 " clauses average size %.2f over %d variables", n, + average_size, active ()); + + // Instantiate data structures for this local search round. + // + WalkerFO walker (internal, average_size, limit); + + bool failed = false; // Inconsistent assumptions? + + level = 1; // Assumed variables assigned at level 1. + + if (assumptions.empty ()) { + LOG ("no assumptions so assigning all variables to decision phase"); + } else { + LOG ("assigning assumptions to their forced phase first"); + for (const auto lit : assumptions) { + signed char tmp = val (lit); + if (tmp > 0) + continue; + if (tmp < 0) { + LOG ("inconsistent assumption %d", lit); + failed = true; + break; + } + if (!active (lit)) + continue; + tmp = sign (lit); + const int idx = abs (lit); + LOG ("initial assign %d to assumption phase", tmp < 0 ? -idx : idx); + set_val (idx, tmp); + CADICAL_assert (level == 1); + var (idx).level = 1; + } + if (!failed) + LOG ("now assigning remaining variables to their decision phase"); + } + + level = 2; // All other non assumed variables assigned at level 2. + + if (!failed) { + + const bool target = opts.warmup ? false : stable || opts.target == 2; + for (auto idx : vars) { + if (!active (idx)) { + LOG ("skipping inactive variable %d", idx); + continue; + } + if (vals[idx]) { + CADICAL_assert (var (idx).level == 1); + LOG ("skipping assumed variable %d", idx); + continue; + } + int tmp = 0; + if (prev) + tmp = phases.prev[idx]; + if (!tmp) + tmp = sign (decide_phase (idx, target)); + CADICAL_assert (tmp == 1 || tmp == -1); + set_val (idx, tmp); + CADICAL_assert (level == 2); + var (idx).level = 2; + walker.best_values[idx] = tmp; + LOG ("initial assign %d to decision phase", tmp < 0 ? -idx : idx); + } + + LOG ("watching satisfied and registering broken clauses"); +#ifdef LOGGING + int64_t watched = 0; +#endif + for (const auto c : clauses) { + + if (c->garbage) + continue; + if (c->redundant) { + if (!opts.walkredundant) + continue; + if (!likely_to_be_kept_clause (c)) + continue; + } + + bool satisfiable = false; // contains not only assumptions + int satisfied = 0; // clause satisfied? + + int *lits = c->literals; + const int size = c->size; + + // Move to front satisfied literals and determine whether there + // is at least one (non-assumed) literal that can be flipped. + // + for (int i = 0; i < size; i++) { + const int lit = lits[i]; + CADICAL_assert (active (lit)); // Due to garbage collection. + if (val (lit) > 0) { + swap (lits[satisfied], lits[i]); + if (!satisfied++) + LOG ("first satisfying literal %d", lit); + } else if (!satisfiable && var (lit).level > 1) { + LOG ("non-assumption potentially satisfying literal %d", lit); + satisfiable = true; + } + } + + if (!satisfied && !satisfiable) { + LOG (c, "due to assumptions unsatisfiable"); + LOG ("stopping local search since assumptions falsify a clause"); + failed = true; + break; + } + + unsigned pos = walker.tclauses.size (); + walker.tclauses.push_back (Counter (satisfied, c)); + walker.connect_clause (c, pos); + + if (!satisfied) { + CADICAL_assert (satisfiable); // at least one non-assumed variable ... + LOG (c, "broken"); + walker.tclauses[pos].pos = walker.broken.size (); + walker.broken.push_back (Tagged (c, pos)); + } else { +#ifdef LOGGING + watched++; // to be able to compare the number with walk +#endif + } + } +#ifdef LOGGING + if (!failed) { + int64_t broken = walker.broken.size (); + int64_t total = watched + broken; + MSG ("watching %" PRId64 " clauses %.0f%% " + "out of %" PRId64 " (watched and broken)", + watched, percent (watched, total), total); + } +#endif + } + walker.check_all (); + int res; // Tells caller to continue with local search. + + if (!failed) { + + int64_t broken = walker.broken.size (); + int64_t initial_minimum = broken; + + PHASE ("walk", stats.walk.count, + "starting with %" PRId64 " unsatisfied clauses " + "(%.0f%% out of %" PRId64 ")", + broken, percent (broken, stats.current.irredundant), + stats.current.irredundant); + + walk_full_occs_save_minimum (walker); + CADICAL_assert (stats.walk.minimum <= walker.minimum); + + int64_t minimum = broken; +#ifndef CADICAL_QUIET + int64_t flips = 0; +#endif + while (!terminated_asynchronously () && !walker.broken.empty () && + walker.ticks < walker.limit) { +#ifndef CADICAL_QUIET + flips++; +#endif + stats.walk.flips++; + stats.walk.broken += broken; + unsigned pos = walker.walk_full_occs_pick_clause (); + Clause *c = walker.tclauses[pos].clause; + const int lit = walker.walk_full_occs_pick_lit (c); + walker.walk_full_occs_flip_lit (lit); + walker.push_flipped (lit); + broken = walker.broken.size (); + LOG ("now have %" PRId64 " broken clauses in total", broken); + if (broken >= minimum) + continue; + minimum = broken; + VERBOSE (3, "new phase minimum %" PRId64 " after %" PRId64 " flips", + minimum, flips); + walk_full_occs_save_minimum (walker); + } + + walker.save_final_minimum (initial_minimum); +#ifndef CADICAL_QUIET + if (minimum == initial_minimum) { + PHASE ("walk", internal->stats.walk.count, + "%sno improvement %" PRId64 "%s in %" PRId64 " flips and " + "%" PRId64 " ticks", + tout.bright_yellow_code (), minimum, tout.normal_code (), + flips, walker.ticks); + } else { + PHASE ("walk", internal->stats.walk.count, + "best phase minimum %" PRId64 " in %" PRId64 " flips and " + "%" PRId64 " ticks", + minimum, flips, walker.ticks); + } +#endif + + if (opts.profile >= 2) { + PHASE ("walk", stats.walk.count, "%.2f million ticks per second", + 1e-6 * + relative (walker.ticks, time () - profiles.walk.started)); + + PHASE ("walk", stats.walk.count, "%.2f thousand flips per second", + relative (1e-3 * flips, time () - profiles.walk.started)); + + } else { + PHASE ("walk", stats.walk.count, "%.2f ticks", 1e-6 * walker.ticks); + + PHASE ("walk", stats.walk.count, "%.2f thousand flips", 1e-3 * flips); + } + + if (minimum > 0) { + LOG ("minimum %" PRId64 " non-zero thus potentially continue", + minimum); + res = 0; + } else { + LOG ("minimum is zero thus stop local search"); + res = 10; + } + + } else { + + res = 20; + + PHASE ("walk", stats.walk.count, + "aborted due to inconsistent assumptions"); + } + + for (auto idx : vars) + if (active (idx)) + set_val (idx, 0); + + CADICAL_assert (level == 2); + level = 0; + + init_watches (); + connect_watches (); + +#ifndef CADICAL_QUIET + if (localsearching) { + CADICAL_assert (force_phase_messages); + force_phase_messages = false; + } +#endif + stats.ticks.walk += walker.ticks; + return res; +} + +void Internal::walk_full_occs () { + START_INNER_WALK (); + + backtrack (); + if (propagated < trail.size () && !propagate ()) { + LOG ("empty clause after root level propagation"); + learn_empty_clause (); + STOP_INNER_WALK (); + return; + } + + int res = 0; + if (opts.warmup) + res = warmup (); + if (res) { + LOG ("stopping walk due to warmup"); + STOP_INNER_WALK (); + return; + } + const int64_t ticks = stats.ticks.search[0] + stats.ticks.search[1]; + int64_t limit = ticks - last.walk.ticks; + VERBOSE (2, + "walk scheduling: last %" PRId64 " current %" PRId64 + " delta %" PRId64, + last.walk.ticks, ticks, limit); + last.walk.ticks = ticks; + limit *= 1e-3 * opts.walkeffort; + if (limit < opts.walkmineff) + limit = opts.walkmineff; + // local search is very cache friendly, so we actually really go over a + // lot of ticks + if (limit > 1e3 * opts.walkmaxeff) { + MSG ("reached maximum efficiency %" PRId64, limit); + limit = 1e3 * opts.walkmaxeff; + } + (void) walk_full_occs_round (limit, false); + STOP_INNER_WALK (); + CADICAL_assert (!unsat); +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_warmup.cpp b/src/sat/cadical/cadical_warmup.cpp new file mode 100644 index 000000000..9f2a06bb8 --- /dev/null +++ b/src/sat/cadical/cadical_warmup.cpp @@ -0,0 +1,404 @@ +#include "global.h" + +#include "internal.hpp" + +ABC_NAMESPACE_IMPL_START + +namespace CaDiCaL { + +// The idea of warmup is to reuse the strength of CDCL, namely +// propagating, before calling random walk that is not good at +// propagating long chains. Therefore, we propagate (ignoring all conflicts) +// discovered along the way. +// The asignmend is the same as the normal assignment however, it updates +// the target phases so that local search can pick them up later + +// specific warmup version with saving of the target. +inline void Internal::warmup_assign (int lit, Clause *reason) { + + CADICAL_assert (level); // no need to learn unit clauses here + require_mode (SEARCH); + + const int idx = vidx (lit); + CADICAL_assert (reason != external_reason); + CADICAL_assert (!vals[idx]); + CADICAL_assert (!flags (idx).eliminated () || reason == decision_reason); + CADICAL_assert (!searching_lucky_phases); + CADICAL_assert (lrat_chain.empty ()); + Var &v = var (idx); + int lit_level; + CADICAL_assert ( + !(reason == external_reason && + ((size_t) level <= assumptions.size () + (!!constraint.size ())))); + CADICAL_assert (reason); + CADICAL_assert (level || reason == decision_reason); + // we purely assign in order here + lit_level = level; + + v.level = lit_level; + v.trail = trail.size (); + v.reason = reason; + CADICAL_assert ((int) num_assigned < max_var); + CADICAL_assert (num_assigned == trail.size ()); + num_assigned++; + const signed char tmp = sign (lit); + phases.saved[idx] = tmp; + set_val (idx, tmp); + CADICAL_assert (val (lit) > 0); + CADICAL_assert (val (-lit) < 0); + + trail.push_back (lit); +#ifdef LOGGING + if (!lit_level) + LOG ("root-level unit assign %d @ 0", lit); + else + LOG (reason, "search assign %d @ %d", lit, lit_level); +#endif + + CADICAL_assert (watching ()); + const Watches &ws = watches (-lit); + if (!ws.empty ()) { + const Watch &w = ws[0]; +#ifndef WIN32 + __builtin_prefetch (&w, 0, 1); +#endif + } +} + +void Internal::warmup_propagate_beyond_conflict () { + + CADICAL_assert (!unsat); + + START (propagate); + CADICAL_assert (!ignore); + + int64_t before = propagated; + + while (propagated != trail.size ()) { + + const int lit = -trail[propagated++]; + LOG ("propagating %d", -lit); + Watches &ws = watches (lit); + + const const_watch_iterator eow = ws.end (); + watch_iterator j = ws.begin (); + const_watch_iterator i = j; + + while (i != eow) { + + const Watch w = *j++ = *i++; + const signed char b = val (w.blit); + + if (b > 0) + continue; // blocking literal satisfied + + if (w.binary ()) { + + // In principle we can ignore garbage binary clauses too, but that + // would require to dereference the clause pointer all the time with + // + // if (w.clause->garbage) { j--; continue; } // (*) + // + // This is too costly. It is however necessary to produce correct + // proof traces if binary clauses are traced to be deleted ('d ...' + // line) immediately as soon they are marked as garbage. Actually + // finding instances where this happens is pretty difficult (six + // parallel fuzzing jobs in parallel took an hour), but it does + // occur. Our strategy to avoid generating incorrect proofs now is + // to delay tracing the deletion of binary clauses marked as garbage + // until they are really deleted from memory. For large clauses + // this is not necessary since we have to access the clause anyhow. + // + // Thanks go to Mathias Fleury, who wanted me to explain why the + // line '(*)' above was in the code. Removing it actually really + // improved running times and thus I tried to find concrete + // instances where this happens (which I found), and then + // implemented the described fix. + + // Binary clauses are treated separately since they do not require + // to access the clause at all (only during conflict analysis, and + // there also only to simplify the code). + + if (b < 0) + // ignoring conflict + ++stats.warmup.conflicts; + else { + warmup_assign (w.blit, w.clause); + } + + } else { + CADICAL_assert (w.clause->size > 2); + + // The cache line with the clause data is forced to be loaded here + // and thus this first memory access below is the real hot-spot of + // the solver. Note, that this check is positive very rarely and + // thus branch prediction should be almost perfect here. + + if (w.clause->garbage) { + j--; + continue; + } + literal_iterator lits = w.clause->begin (); + const int other = lits[0] ^ lits[1] ^ lit; + const signed char u = val (other); + if (u > 0) + j[-1].blit = other; + else { + const int size = w.clause->size; + const const_literal_iterator end = lits + size; + const literal_iterator middle = lits + w.clause->pos; + literal_iterator k = middle; + signed char v = -1; + int r = 0; + while (k != end && (v = val (r = *k)) < 0) + k++; + if (v < 0) { + k = lits + 2; + CADICAL_assert (w.clause->pos <= size); + while (k != middle && (v = val (r = *k)) < 0) + k++; + } + + w.clause->pos = k - lits; // always save position + + CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ()); + + if (v > 0) { + + // Replacement satisfied, so just replace 'blit'. + + j[-1].blit = r; + + } else if (!v) { + + // Found new unassigned replacement literal to be watched. + + LOG (w.clause, "unwatch %d in", lit); + + lits[0] = other; + lits[1] = r; + *k = lit; + + watch_literal (r, lit, w.clause); + + j--; // Drop this watch from the watch list of 'lit'. + + } else if (!u) { + + CADICAL_assert (v < 0); + + // The other watch is unassigned ('!u') and all other literals + // assigned to false (still 'v < 0'), thus we found a unit. + // + build_chain_for_units (other, w.clause, 0); + warmup_assign (other, w.clause); + } else { + CADICAL_assert (u < 0); + CADICAL_assert (v < 0); + + // ignoring conflict + ++stats.warmup.conflicts; + } + } + } + } + + if (j != i) { + ws.resize (j - ws.begin ()); + } + } + + CADICAL_assert (propagated == trail.size ()); + + stats.warmup.propagated += (trail.size () - before); + STOP (propagate); +} + +int Internal::warmup_decide () { + CADICAL_assert (!satisfied ()); + START (decide); + int res = 0; + if ((size_t) level < assumptions.size ()) { + const int lit = assumptions[level]; + CADICAL_assert (assumed (lit)); + const signed char tmp = val (lit); + if (tmp < 0) { + LOG ("assumption %d falsified", lit); + res = 20; + } else if (tmp > 0) { + LOG ("assumption %d already satisfied", lit); + new_trail_level (0); + LOG ("added pseudo decision level"); + } else { + LOG ("deciding assumption %d", lit); + search_assume_decision (lit); + } + } else if ((size_t) level == assumptions.size () && constraint.size ()) { + + int satisfied_lit = 0; // The literal satisfying the constrain. + int unassigned_lit = 0; // Highest score unassigned literal. + int previous_lit = 0; // Move satisfied literals to the front. + + const size_t size_constraint = constraint.size (); + +#ifndef CADICAL_NDEBUG + unsigned sum = 0; + for (auto lit : constraint) + sum += lit; +#endif + for (size_t i = 0; i != size_constraint; i++) { + + // Get literal and move 'constraint[i] = constraint[i-1]'. + + int lit = constraint[i]; + constraint[i] = previous_lit; + previous_lit = lit; + + const signed char tmp = val (lit); + if (tmp < 0) { + LOG ("constraint literal %d falsified", lit); + continue; + } + + if (tmp > 0) { + LOG ("constraint literal %d satisfied", lit); + satisfied_lit = lit; + break; + } + + CADICAL_assert (!tmp); + LOG ("constraint literal %d unassigned", lit); + + if (!unassigned_lit || better_decision (lit, unassigned_lit)) + unassigned_lit = lit; + } + + if (satisfied_lit) { + + constraint[0] = satisfied_lit; // Move satisfied to the front. + + LOG ("literal %d satisfies constraint and " + "is implied by assumptions", + satisfied_lit); + + new_trail_level (0); + LOG ("added pseudo decision level for constraint"); + notify_decision (); + + } else { + + // Just move all the literals back. If we found an unsatisfied + // literal then it will be satisfied (most likely) at the next + // decision and moved then to the first position. + + if (size_constraint) { + + for (size_t i = 0; i + 1 != size_constraint; i++) + constraint[i] = constraint[i + 1]; + + constraint[size_constraint - 1] = previous_lit; + } + + if (unassigned_lit) { + + LOG ("deciding %d to satisfy constraint", unassigned_lit); + search_assume_decision (unassigned_lit); + + } else { + + LOG ("failing constraint"); + unsat_constraint = true; + res = 20; + } + } + +#ifndef CADICAL_NDEBUG + for (auto lit : constraint) + sum -= lit; + CADICAL_assert (!sum); // Checksum of literal should not change! +#endif + + } else { + const bool target = (stable || opts.target == 2); + stats.warmup.decision++; + int idx = next_decision_variable (); + if (flags (idx).eliminated ()) + ++stats.warmup.dummydecision; + int decision = decide_phase (idx, target); + new_trail_level (decision); + warmup_assign (decision, decision_reason); + } + if (res) + marked_failed = false; + STOP (decide); + return res; +} + +int Internal::warmup () { + CADICAL_assert (!unsat); + CADICAL_assert (!level); + if (!opts.warmup) + return 0; + require_mode (WALK); + START (warmup); + ++stats.warmup.count; + int res = 0; + +#ifndef CADICAL_QUIET + const int64_t warmup_propagated = stats.warmup.propagated; + const int64_t decision = stats.warmup.decision; + const int64_t dummydecision = stats.warmup.dummydecision; +#endif + // first propagate assumptions in case we find a conflict. One + // subtle thing, if we find a conflict in the assumption, then we + // actually do need the notifications. Otherwise, we there should be + // no notification at all (not even the `backtrack ()` at the end). + const size_t assms_contraint_level = + assumptions.size () + !constraint.empty (); + while (!res && (size_t) level < assms_contraint_level && + num_assigned < (size_t) max_var) { + CADICAL_assert (num_assigned < (size_t) max_var); + res = warmup_decide (); + warmup_propagate_beyond_conflict (); + } + const bool no_backtrack_notification = (level == 0); + + // now we do not need any notification and can simply propagate + CADICAL_assert (propagated == trail.size ()); + CADICAL_assert (!private_steps); + private_steps = true; + + LOG ("propagating beyond conflicts to warm-up walk"); + while (!res && num_assigned < (size_t) max_var) { + CADICAL_assert (propagated == trail.size ()); + res = warmup_decide (); + warmup_propagate_beyond_conflict (); + LOG (lrat_chain, "during warmup with lrat chain:"); + } + CADICAL_assert (res || num_assigned == (size_t) max_var); +#ifndef CADICAL_QUIET + // constrains with empty levels break this + // CADICAL_assert (res || stats.warmup.propagated - warmup_propagated == + // (int64_t)num_assigned); + VERBOSE (3, + "warming-up needed %" PRIu64 " propagations including %" PRIu64 + " decisions (with %" PRIu64 " dummy ones)", + stats.warmup.propagated - warmup_propagated, + stats.warmup.decision - decision, + stats.warmup.dummydecision - dummydecision); +#endif + + // now we backtrack, notifying only if there was something to + // notify. + private_steps = no_backtrack_notification; + if (!res) + backtrack_without_updating_phases (); + private_steps = false; + STOP (warmup); + require_mode (WALK); + return res; +} + +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/ccadical.h b/src/sat/cadical/ccadical.h index d4c2ce8f0..ef442e7a3 100644 --- a/src/sat/cadical/ccadical.h +++ b/src/sat/cadical/ccadical.h @@ -52,11 +52,14 @@ int ccadical_frozen (CCaDiCaL *, int lit); void ccadical_melt (CCaDiCaL *, int lit); int ccadical_simplify (CCaDiCaL *); int ccadical_vars (CCaDiCaL *); -int ccadical_reserve_difference (CCaDiCaL *, int number_of_vars); +int ccadical_declare_more_variables (CCaDiCaL *, int number_of_vars); +int ccadical_declare_one_more_variable (CCaDiCaL *); +void ccadical_phase (CCaDiCaL *wrapper, int lit); +void ccadical_unphase (CCaDiCaL *wrapper, int lit); // Extra -void ccadical_reserve(CCaDiCaL *, int min_max_var); +void ccadical_resize(CCaDiCaL *, int min_max_var); int ccadical_is_inconsistent(CCaDiCaL *); int ccadical_clauses(CCaDiCaL *); int ccadical_conflicts(CCaDiCaL *); diff --git a/src/sat/cadical/checker.hpp b/src/sat/cadical/checker.hpp index 8c351e326..40661581e 100644 --- a/src/sat/cadical/checker.hpp +++ b/src/sat/cadical/checker.hpp @@ -47,7 +47,7 @@ struct CheckerWatch { : blit (b), size (c->size), clause (c) {} }; -typedef vector CheckerWatcher; +typedef std::vector CheckerWatcher; /*------------------------------------------------------------------------*/ @@ -69,8 +69,8 @@ class Checker : public StatTracer { // and thus we access them by first mapping a literal to 'unsigned'. // static unsigned l2u (int lit); - vector watchers; // watchers of literals - vector marks; // mark bits of literals + std::vector watchers; // watchers of literals + std::vector marks; // mark bits of literals signed char &mark (int lit); CheckerWatcher &watcher (int lit); @@ -83,16 +83,16 @@ class Checker : public StatTracer { CheckerClause **clauses; // hash table of clauses CheckerClause *garbage; // linked list of garbage clauses - vector unsimplified; // original clause for reporting - vector simplified; // clause for sorting + std::vector unsimplified; // original clause for reporting + std::vector simplified; // clause for sorting - vector trail; // for propagation + std::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 &); + void import_clause (const std::vector &); bool tautological (); static const unsigned num_nonces = 4; @@ -156,17 +156,18 @@ public: void connect_internal (Internal *i) override; - void add_original_clause (int64_t, bool, const vector &, + void add_original_clause (int64_t, bool, const std::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 add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; + void delete_clause (int64_t, bool, const std::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 finalize_clause (int64_t, const std::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 std::vector &, + const std::vector &) override; void print_stats () override; void dump (); // for debugging purposes only }; diff --git a/src/sat/cadical/clause.hpp b/src/sat/cadical/clause.hpp index e5d0290f6..161b99199 100644 --- a/src/sat/cadical/clause.hpp +++ b/src/sat/cadical/clause.hpp @@ -31,6 +31,7 @@ typedef const int *const_literal_iterator; // memory but more importantly also requires another memory access and thus // is very costly. +#define USED_SIZE 5 struct Clause { union { int64_t id; // Used to create LRAT-style proofs @@ -41,6 +42,8 @@ struct Clause { // compactly in a contiguous memory arena. Otherwise, so almost all of // the time, 'id' is valid. See 'collect.cpp' for details. }; + unsigned used + : USED_SIZE; // resolved in conflict analysis since last 'reduce' bool conditioned : 1; // Tried for globally blocked clause elimination. bool covered : 1; // Already considered for covered clause elimination. bool enqueued : 1; // Enqueued on backward queue. @@ -56,9 +59,8 @@ struct Clause { bool subsume : 1; // not checked in last subsumption round bool swept : 1; // clause used to sweep equivalences bool flushed : 1; // garbage in proof deleted binaries - unsigned used : 8; // resolved in conflict analysis since last 'reduce' - bool vivified : 1; // clause already vivified - bool vivify : 1; // clause scheduled to be vivified + bool vivified : 1; // clause already vivified + bool vivify : 1; // clause scheduled to be vivified // The glucose level ('LBD' or short 'glue') is a heuristic value for the // expected usefulness of a learned clause, where smaller glue is consider diff --git a/src/sat/cadical/congruence.hpp b/src/sat/cadical/congruence.hpp index e0acf9b00..b36968da1 100644 --- a/src/sat/cadical/congruence.hpp +++ b/src/sat/cadical/congruence.hpp @@ -73,7 +73,7 @@ struct Internal; #define LD_MAX_ARITY 26 #define MAX_ARITY ((1 << LD_MAX_ARITY) - 1) -enum class Gate_Type { And_Gate, XOr_Gate, ITE_Gate }; +enum class Gate_Type : int8_t { And_Gate, XOr_Gate, ITE_Gate }; // Wrapper when we are looking for implication in if-then-else gates struct lit_implication { @@ -94,7 +94,7 @@ struct lit_equivalence { int second; Clause *first_clause; Clause *second_clause; - void check_invariant () { + void check_invariant () const { CADICAL_assert (second_clause); CADICAL_assert (first_clause); CADICAL_assert (std::find (begin (*first_clause), end (*first_clause), first) != @@ -155,7 +155,7 @@ struct ClauseSize { size_t size; Clause *clause; ClauseSize (int s, Clause *c) : size (s), clause (c) {} - ClauseSize (Clause *c): size (c->size), clause (c) {} + ClauseSize (Clause *c) : size (c->size), clause (c) {} ClauseSize () {} }; @@ -168,23 +168,38 @@ struct smaller_clause_size_rank { // There are many special cases for ITE gates and we have to keep track of // them as it is a gate property (rewriting might not make it obvious // anymore). -// a = (a ? t : e) results in no -t and no +e gate (a --> a = t == (-a v -a v t) & (-a v a v -t)) -// a = (-a ? t : e) results in no +t and no -e gate -// a = (c ? a : e) results in no t gate (none of them) -// a = (c ? t : a) results in no e gate (none of them) - -enum Special_ITE_GATE { +// a = (a ? t : e) results in no -t and no +e gate (a --> a = t == (-a v -a +// v t) & (-a v a v -t)) a = (-a ? t : e) results in no +t and no -e gate a +// = (c ? a : e) results in no t gate (none of them) a = (c ? t : a) results +// in no e gate (none of them) +// +// We also use it for AND gates: +// a = (a & b) +// false = (a & b) +// +// The latter version can only happen when converting ITE gates to AND +// gates. +// +// They have a peculiarity: being special can fix itself. To avoid +// one more special case, we rewrite the LHS in that case too to make sure +// it remains special. +// +enum Special_Gate { NORMAL = 0, NO_PLUS_THEN = (1 << 0), NO_NEG_THEN = (1 << 1), NO_THEN = NO_PLUS_THEN + NO_NEG_THEN, NO_PLUS_ELSE = (1 << 2), NO_NEG_ELSE = (1 << 3), + DEGENERATED_AND = (1 << 4), // a = (a & b) + DEGENERATED_AND_LHS_FALSE = (1 << 5), // false = (a & b) NO_ELSE = NO_PLUS_ELSE + NO_NEG_ELSE, COND_LHS = NO_NEG_THEN + NO_PLUS_ELSE, UCOND_LHS = NO_PLUS_THEN + NO_NEG_ELSE, }; +std::string special_gate_str (int8_t f); + inline bool ite_flags_no_then_clauses (int8_t flag) { return (flag & NO_THEN) == NO_THEN; } @@ -201,6 +216,18 @@ inline bool ite_flags_cond_lhs (int8_t flag) { return (flag & COND_LHS) == COND_LHS; } +// std::optional is C++17 sadly +struct my_dummy_optional { + LitClausePair content; + my_dummy_optional () : content (0, 0) {} + bool operator() () const { return content.current_lit; } + my_dummy_optional operator= (LitClausePair p) { + content = p; + return *this; + } + void reset () { content = LitClausePair (0, 0); } +}; + /*------------------------------------------------------------------------*/ // The core structure of this algorithm: the gate. It is composed of a @@ -245,19 +272,15 @@ struct Gate { bool indexed : 1; bool marked : 1; bool shrunken : 1; - size_t hash; // TODO remove this field (the C++ implementation is caching - // it anyway) vector 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; + my_dummy_optional neg_lhs_ids; + int8_t degenerated_gate = Special_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; + return tag == lhs.tag && rhs == lhs.rhs; } }; @@ -279,9 +302,9 @@ struct CompactBinary { }; struct Hash { - Hash (std::array &ncs) : nonces (ncs) {} - std::array &nonces; - size_t operator() (const Gate *const g) const; + Hash (std::array &ncs) : nonces (ncs) {} + const std::array &nonces; + inline size_t operator() (const Gate *const g) const; }; struct Rewrite { @@ -297,13 +320,15 @@ struct Rewrite { struct Closure { Closure (Internal *i); + ~Closure () { delete dummy_search_gate; } + Gate *dummy_search_gate = nullptr; Internal *const internal; - vector extra_clauses; + vector extra_clauses; vector binaries; std::vector> offsetsize; bool full_watching = false; - std::array nonces; + std::array nonces; typedef unordered_set GatesTable; vector scheduled; @@ -358,6 +383,11 @@ struct Closure { int find_lrat_representative_with_marks (int lit); // representative in the union-find structure in the lazy equivalences int find_representative (int lit); + // representative in the union-find structure in the lazy equivalences. + // only useful if you do not care about proofs like during forward + // subsumption. + int find_representative_and_compress_no_proofs (int lit); + int find_representative_already_compressed (int lit); // find the representative and produce the binary clause representing the // normalization from the literal to the result. int find_representative_and_compress (int, bool update_eager = true); @@ -425,12 +455,17 @@ struct Closure { // chain. bool merge_literals_equivalence (int lit, int other, Clause *c1, Clause *c2); - bool merge_literals_lrat (Gate *g, Gate *h, int lit, int other, - const std::vector & = {}, - const std::vector & = {}); - bool merge_literals_lrat (int lit, int other, - const std::vector & = {}, - const std::vector & = {}); + bool merge_literals (Gate *g, Gate *h, int lit, int other, + const std::vector & = {}, + const std::vector & = {}); + bool merge_literals (int lit, int other, + const std::vector & = {}, + const std::vector & = {}); + // factoring out the merge w.r.t. both cases above + bool really_merge_literals (int lit, int other, int repr_lit, + int repr_other, + const std::vector & = {}, + const std::vector & = {}); // proof production vector lrat_chain_and_gate; @@ -458,11 +493,13 @@ struct Closure { // TODO: does nothing except pushing on the stack, remove! void push_id_on_chain (std::vector &chain, const std::vector &c); + void push_id_on_chain (std::vector &chain, + const my_dummy_optional &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); + 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, @@ -528,17 +565,18 @@ struct Closure { Gate *find_remaining_and_gate (Clause *base_clause, int lhs); void extract_and_gates (); - Gate *find_and_lits (const vector &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); + Gate *find_and_lits (vector &rhs, Gate *except = nullptr); + Gate * + find_gate_lits (vector &rhs, + Gate_Type typ, // rhs unchanged but swapped back and forth + Gate *except = nullptr); + Gate *find_xor_lits (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); + 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 &, @@ -557,7 +595,7 @@ struct Closure { void check_ite_implied (int lhs, int cond, int then_lit, int else_lit); void check_ite_gate_implied (Gate *g); void check_and_gate_implied (Gate *g); - void check_ite_lrat_reasons (Gate *g, bool = false); + void check_ite_lrat_reasons (Gate *g); void check_contained_module_rewriting (Clause *c, int lit, bool, int except); void delete_proof_chain (); @@ -598,17 +636,16 @@ struct Closure { void check_binary_implied (int a, int b); void check_implied (); - // learn units. You can delay units if you want to learn several at once before - // propagation. Otherwise, propagate! If you need propagation even if nothing is set, use the - // second parameter. + // learn units. You can delay units if you want to learn several at once + // before propagation. Otherwise, propagate! If you need propagation even + // if nothing is set, use the second parameter. // - // The function can also learn the empty clause if the unit is already set. Do not add the unit in - // the chain! + // The function can also learn the empty clause if the unit is already + // set. Do not add the unit in the chain! bool learn_congruence_unit (int unit, bool = false, bool = false); - bool fully_propagate (); + bool fully_propagate (); void learn_congruence_unit_falsifies_lrat_chain (Gate *g, int src, - int dst, - int clashing, + int dst, int clashing, int falsified, int unit); void learn_congruence_unit_when_lhs_set (Gate *g, int src, LRAT_ID id1, LRAT_ID id2, int dst); @@ -618,8 +655,12 @@ struct Closure { void subsume_clause (Clause *subsuming, Clause *subsumed); bool find_subsuming_clause (Clause *c); void produce_rewritten_clause_lrat_and_clean (vector &, + int execept_lhs = 0, + bool = true, bool = false); + void produce_rewritten_clause_lrat_and_clean (my_dummy_optional &, int execept_lhs = 0, bool = true); + // rewrite the clause using eager rewriting and rew1 and rew2, except for // 2 literals Usage: // - the except are used to ignore LHS of gates that have not and should @@ -629,10 +670,10 @@ struct Closure { // to be taken into account without being added to the eager rewriting // (yet) Clause *produce_rewritten_clause_lrat (Clause *c, int execept_lhs = 0, - bool remove_units = true, bool = true); + bool remove_units = true, + bool = false); void produce_rewritten_clause_lrat (vector &, - int execept_lhs = 0, - bool = true); + int execept_lhs = 0, bool = true); void compute_rewritten_clause_lrat_simple (Clause *c, int except); // variant where we update the indices after removing the tautologies and // remove the tautological clauses @@ -681,7 +722,8 @@ struct Closure { bool flip = 0); bool rewrite_ite_gate_to_and (Gate *g, int dst, int src, size_t c, - size_t d, int cond_lit_to_learn_if_degenerated); + size_t d, + int cond_lit_to_learn_if_degenerated); void produce_ite_merge_then_else_reasons ( Gate *g, int dst, int src, std::vector &reasons_implication, std::vector &reasons_back); @@ -699,10 +741,10 @@ struct Closure { // has length 3 bool simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, int removed); - void - merge_ite_gate_same_then_else_lrat (std::vector &clauses, - std::vector &reasons_implication, - std::vector &reasons_back); + 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); diff --git a/src/sat/cadical/contract.hpp b/src/sat/cadical/contract.hpp index 4a3ba066a..d70e7b6ab 100644 --- a/src/sat/cadical/contract.hpp +++ b/src/sat/cadical/contract.hpp @@ -98,7 +98,9 @@ void require_solver_pointer_to_be_non_zero (const void *ptr, REQUIRE ((int) (LIT) && ((int) (LIT)) != INT_MIN, \ "invalid literal '%d'", (int) (LIT)); \ REQUIRE (external->is_valid_input ((int) (LIT)), \ - "extension variable %d defined by the solver", (int) (LIT)); \ + "extension variable %d defined by the solver \ + (try using `vars ()` or `set (factor, 0)`)", \ + (int) (LIT)); \ } while (0) #define REQUIRE_STEADY_STATE() \ diff --git a/src/sat/cadical/drattracer.hpp b/src/sat/cadical/drattracer.hpp index 8c2c023d5..cf722f6a2 100644 --- a/src/sat/cadical/drattracer.hpp +++ b/src/sat/cadical/drattracer.hpp @@ -3,6 +3,7 @@ #include "global.h" +#include "file.hpp" #include "tracer.hpp" ABC_NAMESPACE_CXX_HEADER_START @@ -21,8 +22,8 @@ class DratTracer : public FileTracer { void put_binary_lit (int external_lit); // support DRAT - void drat_add_clause (const vector &); - void drat_delete_clause (const vector &); + void drat_add_clause (const std::vector &); + void drat_delete_clause (const std::vector &); public: // own and delete 'file' @@ -32,15 +33,16 @@ public: void connect_internal (Internal *i) override; void begin_proof (int64_t) override {} // skip - void add_original_clause (int64_t, bool, const vector &, + void add_original_clause (int64_t, bool, const std::vector &, bool = false) override {} // skip - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; + void add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; - void delete_clause (int64_t, bool, const vector &) override; + void delete_clause (int64_t, bool, const std::vector &) override; - void finalize_clause (int64_t, const vector &) override {} // skip + void finalize_clause (int64_t, const std::vector &) override { + } // skip void report_status (int, int64_t) override {} // skip diff --git a/src/sat/cadical/external.hpp b/src/sat/cadical/external.hpp index 7618fb936..f17334ecf 100644 --- a/src/sat/cadical/external.hpp +++ b/src/sat/cadical/external.hpp @@ -6,6 +6,7 @@ /*------------------------------------------------------------------------*/ #include "range.hpp" +#include #include #include @@ -321,7 +322,32 @@ struct External { // We call it 'ival' as abbreviation for 'val' with 'int' return type to // avoid bugs due to using 'signed char tmp = val (lit)', which might turn // a negative value into a positive one (happened in 'extend'). + + // This is due to the IPASIR semantics which returns 'elit' if it is + // 'true' and '-elit' if it is 'false'. This is a bit confusing but has + // been standardized in IPASIR: // + // Consiert 'eidx = 13' and 'vals[13] == false' then '13' is 'false' in + // this terminology of the IPASIR interface. Accordingly we get + // + // ival (13) = -13 + // + // However and this is the confusing thing, as '-13' is true the 'ival' + // function should also return '-13': + // + // ival (-13) = -13 + // + // Now with '13' assumed 'true' so 'vals[13] = true' we similarly have + // + // ival (13) = 13 as '13' is true' + // + // ival (-13) = 13 as '-13' is false + // + // To summarize we can think of the IPASIR 'ipasir_val' function, which + // 'CaDiCaL' follows as returning the phase of the literal which is true + // under the current assignment no matter whether you give the positive + // literal or its negation and thus 'ival (lit) == ival (-lit))" + inline int ival (int elit) const { CADICAL_assert (elit != INT_MIN); int eidx = abs (elit); diff --git a/src/sat/cadical/flags.hpp b/src/sat/cadical/flags.hpp index 995438c7a..422111dea 100644 --- a/src/sat/cadical/flags.hpp +++ b/src/sat/cadical/flags.hpp @@ -36,13 +36,14 @@ struct Flags { // Variable flags. // unsigned char block : 2; // removed since last 'block' round (*) unsigned char skip : 2; // skip this literal as blocking literal - + bool backbone1, backbone0; // Bits for handling assumptions. // unsigned char assumed : 2; unsigned char failed : 2; // 0 if not part of failure // 1 if positive lit is in failure - // 2 if negated lit is in failure + // 2 if negated lit is in failure + bool factored_but_on_reconstruction_stack : 1; enum { UNUSED = 0, @@ -58,7 +59,8 @@ struct Flags { // Variable flags. // Initialized explicitly in 'Internal::init' through this function. // Flags () { - seen = keep = poison = removable = shrinkable = added = sweep = false; + seen = keep = poison = removable = shrinkable = added = sweep = + backbone1 = backbone0 = false; subsume = elim = ternary = true; block = 3u; skip = assumed = failed = marked_signed = factor = 0; @@ -81,6 +83,12 @@ struct Flags { // Variable flags. dst.subsume = subsume; dst.ternary = ternary; dst.block = block; + dst.sweep = sweep; + dst.backbone0 = backbone0; + dst.backbone1 = backbone1; + dst.added = added; + dst.factor = factor; + // seen, keep, poison, removable, shrinkable are unused } }; diff --git a/src/sat/cadical/frattracer.hpp b/src/sat/cadical/frattracer.hpp index cadfa3721..09125ca3b 100644 --- a/src/sat/cadical/frattracer.hpp +++ b/src/sat/cadical/frattracer.hpp @@ -3,6 +3,11 @@ #include "global.h" +#include "file.hpp" +#include "tracer.hpp" + +#include + ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -19,19 +24,19 @@ class FratTracer : public FileTracer { int64_t finalized, original; #endif - vector delete_ids; + std::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 &); + void frat_add_original_clause (int64_t, const std::vector &); + void frat_add_derived_clause (int64_t, const std::vector &); + void frat_add_derived_clause (int64_t, const std::vector &, + const std::vector &); + void frat_delete_clause (int64_t, const std::vector &); + void frat_finalize_clause (int64_t, const std::vector &); public: // own and delete 'file' @@ -41,15 +46,15 @@ public: void connect_internal (Internal *i) override; void begin_proof (int64_t) override {} // skip - void add_original_clause (int64_t, bool, const vector &, + void add_original_clause (int64_t, bool, const std::vector &, bool = false) override; - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; + void add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; - void delete_clause (int64_t, bool, const vector &) override; + void delete_clause (int64_t, bool, const std::vector &) override; - void finalize_clause (int64_t, const vector &) override; + void finalize_clause (int64_t, const std::vector &) override; void report_status (int, int64_t) override {} // skip diff --git a/src/sat/cadical/idruptracer.hpp b/src/sat/cadical/idruptracer.hpp index 4ea478705..7d343cc5f 100644 --- a/src/sat/cadical/idruptracer.hpp +++ b/src/sat/cadical/idruptracer.hpp @@ -3,9 +3,10 @@ #include "global.h" -ABC_NAMESPACE_CXX_HEADER_START +#include "file.hpp" +#include "tracer.hpp" -class FileTracer; +ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -29,8 +30,8 @@ class IdrupTracer : public FileTracer { uint64_t num_clauses; // number of clauses in hash table uint64_t size_clauses; // size of clause hash table IdrupClause **clauses; // hash table of clauses - vector imported_clause; - vector assumptions; + std::vector imported_clause; + std::vector assumptions; static const unsigned num_nonces = 4; @@ -60,14 +61,14 @@ class IdrupTracer : public FileTracer { void put_binary_lit (int external_lit); void put_binary_id (int64_t id, bool = false); - void idrup_add_derived_clause (const vector &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_add_derived_clause (const std::vector &clause); + void idrup_delete_clause (int64_t id, const std::vector &clause); + void idrup_add_restored_clause (const std::vector &clause); + void idrup_add_original_clause (const std::vector &clause); + void idrup_conclude_and_delete (const std::vector &conclusion); void idrup_report_status (int status); - void idrup_conclude_sat (const vector &model); - void idrup_conclude_unknown (const vector &trail); + void idrup_conclude_sat (const std::vector &model); + void idrup_conclude_unknown (const std::vector &trail); void idrup_solve_query (); public: @@ -75,18 +76,19 @@ public: ~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 &, + void add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; + void add_assumption_clause (int64_t, const std::vector &, + const std::vector &) override; + void weaken_minus (int64_t, const std::vector &) override; + void delete_clause (int64_t, bool, const std::vector &) override; + void add_original_clause (int64_t, bool, const std::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 conclude_sat (const std::vector &) override; + void conclude_unsat (ConclusionType, + const std::vector &) override; + void conclude_unknown (const std::vector &) override; void solve_query () override; void add_assumption (int) override; @@ -94,9 +96,9 @@ public: // skip void begin_proof (int64_t) override {} - void finalize_clause (int64_t, const vector &) override {} + void finalize_clause (int64_t, const std::vector &) override {} void strengthen (int64_t) override {} - void add_constraint (const vector &) override {} + void add_constraint (const std::vector &) override {} // logging and file io void connect_internal (Internal *i) override; diff --git a/src/sat/cadical/internal.hpp b/src/sat/cadical/internal.hpp index 53c698c58..a9104898f 100644 --- a/src/sat/cadical/internal.hpp +++ b/src/sat/cadical/internal.hpp @@ -23,6 +23,7 @@ #include #include #include +#include // Less common 'C' header. @@ -107,6 +108,7 @@ extern "C" { #include "veripbtracer.hpp" #include "version.hpp" #include "vivify.hpp" +#include "walk.hpp" #include "watch.hpp" // c headers @@ -123,6 +125,7 @@ using namespace std; struct Coveror; struct External; +struct WalkerFO; struct Walker; class Tracer; class FileTracer; @@ -161,6 +164,7 @@ struct Internal { TRANSRED = (1 << 15), VIVIFY = (1 << 16), WALK = (1 << 17), + BACKBONE = (1 << 18), }; bool in_mode (Mode m) const { return (mode & m) != 0; } @@ -275,6 +279,7 @@ struct Internal { vector sweep_schedule; // remember sweep varibles to reschedule bool sweep_incomplete; // sweep + uint64_t randomized_deciding; cadical_kitten *citten; @@ -313,7 +318,8 @@ struct Internal { Internal *internal; // proxy to 'this' in macros External *external; // proxy to 'external' buddy in 'Solver' - const unsigned max_used = 255; // must fix into the header of the clause! + static constexpr unsigned max_used = + 31; // must fit into the header of the clause! /*----------------------------------------------------------------------*/ // Asynchronous termination flag written by 'terminate' and read by @@ -626,9 +632,20 @@ struct Internal { CADICAL_assert (lit != blit); Watches &ws = watches (lit); ws.push_back (Watch (blit, c)); + CADICAL_assert (c->literals[0] == lit || c->literals[1] == lit); LOG (c, "watch %d blit %d in", lit, blit); } + // Watch literal 'lit' in clause with blocking literal 'blit'. + // Inlined here, since it occurs in the tight inner loop of 'propagate'. + // + inline void watch_binary_literal (int lit, int blit, Clause *c) { + CADICAL_assert (lit != blit); + Watches &ws = watches (lit); + ws.push_back (Watch (true, blit, c)); + LOG (c, "watch binary %d blit %d in", lit, blit); + } + // Add two watches to a clause. This is used initially during allocation // of a clause and during connecting back all watches after preprocessing. // @@ -720,6 +737,7 @@ struct Internal { void search_assign_driving (int lit, Clause *reason); void search_assign_external (int lit); void search_assume_decision (int decision); + static Clause *decision_reason; void assign_unit (int lit); int64_t cache_lines (size_t bytes) { return (bytes + 127) / 128; } int64_t cache_lines (size_t n, size_t bytes) { @@ -789,6 +807,7 @@ struct Internal { int otfs_find_backtrack_level (int &forced); Clause *on_the_fly_strengthen (Clause *conflict, int lit); void update_decision_rate_average (); + void lazy_external_propagator_out_of_order_clause (int &); void analyze (); void iterate (); // report learned unit clause @@ -820,6 +839,13 @@ struct Internal { void renotify_trail_after_ilb (); void renotify_trail_after_local_search (); void renotify_full_trail (); + + // adds the assigned literals to assigned + void renotify_full_trail_between_trail_pos (int start_level, + int end_level, + int propagator_level, + std::vector &assigned, + bool start_new_level); void connect_propagator (); void mark_garbage_external_forgettable (int64_t id); bool is_external_forgettable (int64_t id); @@ -829,6 +855,8 @@ struct Internal { #endif void recompute_tier (); + void decay_clauses_upon_incremental_clauses (); + void print_tier_usage_statistics (); // Use last learned clause to subsume some more. // void eagerly_subsume_recently_learned_clauses (Clause *); @@ -844,6 +872,8 @@ struct Internal { // void clear_phases (vector &); // reset argument to zero void copy_phases (vector &); // copy 'saved' to argument + void save_assigned_phases ( + vector &); // save assigned literals to argument // Resetting the saved phased in 'rephase.cpp'. // @@ -861,6 +891,7 @@ struct Internal { // Lucky feasible case checking. // int unlucky (int res); + int lucky_decide_assumptions (); bool lucky_propagate_discrepency (int); int trivially_false_satisfiable (); int trivially_true_satisfiable (); @@ -977,7 +1008,7 @@ struct Internal { void vivify_build_lrat (int, Clause *, std::vector> &); void vivify_chain_for_units (int lit, Clause *reason); - void vivify_strengthen (Clause *candidate); + void vivify_strengthen (Clause *candidate, int64_t &); void vivify_assign (int lit, Clause *); void vivify_assume (int lit); bool vivify_propagate (int64_t &); @@ -999,6 +1030,28 @@ struct Internal { // void transred (); + // backbone computation + // + void backbone_decision (int lit); + bool backbone_propagate (int64_t &); + void backbone_propagate2 (int64_t &); + unsigned compute_backbone (); + void backbone_unit_reassign ( + int lit); // only for reassigning already derived clauses! + void backbone_unit_assign ( + int lit); // only for reassigning already derived clauses! + void backbone_assign_any (int lit, Clause *reason); + void backbone_assign (int lit, Clause *reason); + void backbone_lrat_for_units (int lit, Clause *c); + unsigned compute_backbone_round (std::vector &candidates, + std::vector &units, + const int64_t ticks_limit, + int64_t &ticks, unsigned inconsistent); + void schedule_backbone_cands (std::vector &candidates); + void keep_backbone_candidates (const std::vector &candidates); + int backbone_analyze (Clause *, int64_t &); + void binary_clauses_backbone (); + // We monitor the maximum size and glue of clauses during 'reduce' and // thus can predict if a redundant extended clause is likely to be kept in // the next 'reduce' phase. These clauses are target of subsumption and @@ -1292,7 +1345,8 @@ struct Internal { bool run_factorization (int64_t limit); bool factor (); int get_new_extension_variable (); - Clause *new_factor_clause (); + Clause *new_factor_clause (int); + void adjust_scores_and_phases_of_fresh_variables (Factoring &); // instantiate // @@ -1347,13 +1401,27 @@ struct Internal { // ProbSAT/WalkSAT implementation called initially or from 'rephase'. // void walk_save_minimum (Walker &); - Clause *walk_pick_clause (Walker &); - unsigned walk_break_value (int lit); + ClauseOrBinary walk_pick_clause (Walker &); + unsigned walk_break_value (int lit, int64_t &ticks); + int walk_pick_lit (Walker &walker, ClauseOrBinary); int walk_pick_lit (Walker &, Clause *); - void walk_flip_lit (Walker &, int lit); + bool walk_flip_lit (Walker &, int lit); + int walk_pick_lit (Walker &walker, TaggedBinary c); int walk_round (int64_t limit, bool prev); void walk (); + int walk_full_occs_round (int64_t limit, bool prev); + void walk_full_occs (); + void walk_full_occs_save_minimum (WalkerFO &); + void make_clauses_along_occurrences (WalkerFO &walker, int lit); + void make_clauses_along_unsatisfied (WalkerFO &walker, int lit); + + // Warmup + inline void warmup_assign (int lit, Clause *reason); + void warmup_propagate_beyond_conflict (); + int warmup_decide (); + int warmup (); + // Detect strongly connected components in the binary implication graph // (BIG) and equivalent literal substitution (ELS) in 'decompose.cpp'. // @@ -1427,6 +1495,8 @@ struct Internal { // Part on picking the next decision in 'decide.cpp'. // bool satisfied (); + void start_random_sequence (); + int next_random_decision (); int next_decision_variable_on_queue (); int next_decision_variable_with_best_score (); int next_decision_variable (); @@ -1442,6 +1512,7 @@ struct Internal { void limit_conflicts (int); // Force conflict limit. void limit_preprocessing (int); // Enable 'n' preprocessing rounds. void limit_local_search (int); // Enable 'n' local search rounds. + void limit_ticks (int64_t); // Force ticks limit. // External versions can access limits by 'name'. // @@ -1478,8 +1549,8 @@ struct Internal { int already_solved (); int restore_clauses (); bool preprocess_round (int round); - void preprocess_quickly (); - int preprocess (); + void preprocess_quickly (bool always); + int preprocess (bool always); int local_search_round (int round); int local_search (); int lucky_phases (); @@ -1607,7 +1678,7 @@ struct Internal { } // Congruence closure - bool extract_gates (); + bool extract_gates (bool remove_units_before_run = false); // Parsing functions in 'parse.cpp'. // @@ -1856,6 +1927,12 @@ inline bool Internal::search_limits_hit () { return true; } + if (lim.ticks >= 0 && + stats.ticks.search[0] + stats.ticks.search[1] >= lim.ticks) { + LOG ("ticks limit %" PRId64 " reached", lim.ticks); + return true; + } + return false; } diff --git a/src/sat/cadical/lidruptracer.hpp b/src/sat/cadical/lidruptracer.hpp index 02cd33fcd..ec8352712 100644 --- a/src/sat/cadical/lidruptracer.hpp +++ b/src/sat/cadical/lidruptracer.hpp @@ -3,9 +3,10 @@ #include "global.h" -ABC_NAMESPACE_CXX_HEADER_START +#include "file.hpp" +#include "tracer.hpp" -class FileTracer; +ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -29,12 +30,12 @@ class LidrupTracer : public FileTracer { uint64_t num_clauses; // number of clauses in hash table uint64_t size_clauses; // size of clause hash table LidrupClause **clauses; // hash table of clauses - vector imported_clause; - vector assumptions; - vector imported_chain; - vector batch_weaken; - vector batch_delete; - vector batch_restore; + std::vector imported_clause; + std::vector assumptions; + std::vector imported_chain; + std::vector batch_weaken; + std::vector batch_delete; + std::vector batch_restore; static const unsigned num_nonces = 4; @@ -64,16 +65,19 @@ class LidrupTracer : public FileTracer { void put_binary_lit (int external_lit); void put_binary_id (int64_t id, bool = true); - void lidrup_add_derived_clause (int64_t id, const vector &clause, - const vector &chain); - void lidrup_delete_clause (int64_t id); //, const vector &clause); + void lidrup_add_derived_clause (int64_t id, + const std::vector &clause, + const std::vector &chain); void - lidrup_add_restored_clause (int64_t id); //, const vector &clause); - void lidrup_add_original_clause (int64_t id, const vector &clause); - void lidrup_conclude_and_delete (const vector &conclusion); + lidrup_delete_clause (int64_t id); //, const std::vector &clause); + void lidrup_add_restored_clause ( + int64_t id); //, const std::vector &clause); + void lidrup_add_original_clause (int64_t id, + const std::vector &clause); + void lidrup_conclude_and_delete (const std::vector &conclusion); void lidrup_report_status (int status); - void lidrup_conclude_sat (const vector &model); - void lidrup_conclude_unknown (const vector &trail); + void lidrup_conclude_sat (const std::vector &model); + void lidrup_conclude_unknown (const std::vector &trail); void lidrup_solve_query (); void lidrup_batch_weaken_restore_and_delete (); @@ -82,18 +86,19 @@ public: ~LidrupTracer (); // proof section: - void add_derived_clause (int64_t, bool, const vector &, - 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 &, + void add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; + void add_assumption_clause (int64_t, const std::vector &, + const std::vector &) override; + void weaken_minus (int64_t, const std::vector &) override; + void delete_clause (int64_t, bool, const std::vector &) override; + void add_original_clause (int64_t, bool, const std::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 conclude_sat (const std::vector &) override; + void conclude_unsat (ConclusionType, + const std::vector &) override; + void conclude_unknown (const std::vector &) override; void solve_query () override; void add_assumption (int) override; @@ -101,9 +106,9 @@ public: // skip void begin_proof (int64_t) override {} - void finalize_clause (int64_t, const vector &) override {} + void finalize_clause (int64_t, const std::vector &) override {} void strengthen (int64_t) override {} - void add_constraint (const vector &) override {} + void add_constraint (const std::vector &) override {} // logging and file io void connect_internal (Internal *i) override; diff --git a/src/sat/cadical/limit.hpp b/src/sat/cadical/limit.hpp index 0f6923214..284a38b58 100644 --- a/src/sat/cadical/limit.hpp +++ b/src/sat/cadical/limit.hpp @@ -20,17 +20,21 @@ struct Limit { int64_t decisions; // decision limit if non-negative int64_t preprocessing; // limit on preprocessing rounds int64_t localsearch; // limit on local search rounds + int64_t ticks; // ticks limit if non-negative - int64_t compact; // conflict limit for next 'compact' - int64_t condition; // conflict limit for next 'condition' - int64_t elim; // conflict limit for next 'elim' - int64_t flush; // conflict limit for next 'flush' - int64_t inprobe; // conflict limit for next 'inprobe' - int64_t reduce; // conflict limit for next 'reduce' - int64_t rephase; // conflict limit for next 'rephase' - int64_t report; // report limit for header - int64_t restart; // conflict limit for next 'restart' - int64_t stabilize; // conflict/ticks limit for next 'stabilize' + int64_t compact; // conflict limit for next 'compact' + int64_t condition; // conflict limit for next 'condition' + int64_t elim; // conflict limit for next 'elim' + int64_t flush; // conflict limit for next 'flush' + int64_t inprobe; // conflict limit for next 'inprobe' + int64_t reduce; // conflict limit for next 'reduce' + int64_t rephase; // conflict limit for next 'rephase' + int64_t report; // report limit for header + int64_t restart; // conflict limit for next 'restart' + int64_t stabilize; // conflict/ticks limit for next 'stabilize' + int64_t incremental_decay; // conflict/ticks limit for next clause 'decay' + // for incremental clauses + int64_t random_decision; // randomized decision limit for conflicts int keptsize; // maximum kept size in 'reduce' int keptglue; // maximum kept glue in 'reduce' @@ -59,7 +63,7 @@ struct Delay { bool delay () { if (bypass) - return true; + return false; if (limit) { --limit; return true; @@ -91,7 +95,7 @@ struct Last { } transred; struct { int64_t ticks; - } sweep, vivify, probe; + } backbone, probe, sweep, vivify, walk; struct { int64_t fixed, subsumephases, marked; } elim; @@ -114,7 +118,11 @@ struct Last { struct { int64_t conflicts; int64_t ticks; + int64_t rephased; } stabilize; + struct { + int64_t last_id; + } incremental_decay; Last (); }; @@ -123,6 +131,7 @@ struct Inc { int64_t stabilize; // base ticks limit after first mode switch int64_t conflicts; // next conflict limit if non-negative int64_t decisions; // next decision limit if non-negative + int64_t ticks; // next ticks limit if non-negative int64_t preprocessing; // next preprocessing limit if non-negative int64_t localsearch; // next local search limit if non-negative Inc (); @@ -153,6 +162,9 @@ struct Inc { last.NAME.ticks = TICKS; \ const int64_t NEW_LIMIT = OLD_LIMIT + DELTA; \ LIMIT = NEW_LIMIT; \ + VERBOSE (2, \ + "new ticks limit %" PRId64 "= %" PRId64 " + %f * %" PRId64, \ + NEW_LIMIT, OLD_LIMIT, EFFORT, REFERENCE); \ } while (0) } // namespace CaDiCaL diff --git a/src/sat/cadical/lratchecker.hpp b/src/sat/cadical/lratchecker.hpp index 0e791e9f4..7765ec73b 100644 --- a/src/sat/cadical/lratchecker.hpp +++ b/src/sat/cadical/lratchecker.hpp @@ -4,6 +4,8 @@ #include "global.h" /*------------------------------------------------------------------------*/ +#include "tracer.hpp" +#include #include ABC_NAMESPACE_CXX_HEADER_START @@ -49,11 +51,11 @@ class LratChecker : public StatTracer { signed char &checked_lit (int lit); signed char &mark (int lit); - vector checked_lits; - vector marks; // mark bits of literals - unordered_map> clauses_to_reconstruct; - vector assumptions; - vector constraint; + std::vector checked_lits; + std::vector marks; // mark bits of literals + std::unordered_map> clauses_to_reconstruct; + std::vector assumptions; + std::vector constraint; bool concluded; uint64_t num_clauses; // number of clauses in hash table @@ -63,12 +65,12 @@ class LratChecker : public StatTracer { LratCheckerClause **clauses; // hash table of clauses LratCheckerClause *garbage; // linked list of garbage clauses - vector imported_clause; // original clause for reporting - vector assumption_clauses; + std::vector imported_clause; // original clause for reporting + std::vector assumption_clauses; void enlarge_vars (int64_t idx); void import_literal (int lit); - void import_clause (const vector &); + void import_clause (const std::vector &); static const unsigned num_nonces = 4; @@ -94,15 +96,16 @@ class LratChecker : public StatTracer { LratCheckerClause *new_clause (); void delete_clause (LratCheckerClause *); - bool check (vector); // check RUP - bool check_resolution (vector); // check resolution - bool check_blocked (vector); // check ER + bool check (std::vector); // check RUP + bool check_resolution (std::vector); // check resolution + bool check_blocked (std::vector); // check ER struct { int64_t added; // number of added clauses int64_t original; // number of added original clauses int64_t derived; // number of added derived clauses + int64_t rat; // number of added rat clauses int64_t deleted; // number of deleted clauses int64_t finalized; // number of finalized clauses @@ -124,40 +127,41 @@ public: void connect_internal (Internal *i) override; void begin_proof (int64_t) override; - void add_original_clause (int64_t, bool, const vector &, + void add_original_clause (int64_t, bool, const std::vector &, bool restore) override; - void restore_clause (int64_t, const vector &); + void restore_clause (int64_t, const std::vector &); // check the proof chain for the new clause and add it to the checker - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; + void add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; // check if the clause is present and delete it from the checker - void delete_clause (int64_t, bool, const vector &) override; + void delete_clause (int64_t, bool, const std::vector &) override; // check if the clause is present and delete it from the checker - void weaken_minus (int64_t, const vector &) override; + void weaken_minus (int64_t, const std::vector &) override; // check if the clause is present and delete it from the checker - void finalize_clause (int64_t, const vector &) override; + void finalize_clause (int64_t, const std::vector &) 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 &, - const vector &) override; + void add_assumption_clause (int64_t, const std::vector &, + const std::vector &) override; // mark lit as assumption void add_assumption (int) override; // mark lits as constraint - void add_constraint (const vector &) override; + void add_constraint (const std::vector &) 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 &) override; + void conclude_unsat (ConclusionType, + const std::vector &) override; void print_stats () override; void dump (); // for debugging purposes only diff --git a/src/sat/cadical/lrattracer.hpp b/src/sat/cadical/lrattracer.hpp index 9d8e92b6a..eeb7d8556 100644 --- a/src/sat/cadical/lrattracer.hpp +++ b/src/sat/cadical/lrattracer.hpp @@ -3,6 +3,9 @@ #include "global.h" +#include "file.hpp" +#include "tracer.hpp" + ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -17,15 +20,15 @@ class LratTracer : public FileTracer { int64_t added, deleted; #endif int64_t latest_id; - vector delete_ids; + std::vector 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 &, - const vector &); + void lrat_add_clause (int64_t, const std::vector &, + const std::vector &); void lrat_delete_clause (int64_t); public: @@ -36,15 +39,16 @@ public: void connect_internal (Internal *i) override; void begin_proof (int64_t) override; - void add_original_clause (int64_t, bool, const vector &, + void add_original_clause (int64_t, bool, const std::vector &, bool = false) override {} // skip - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; + void add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; - void delete_clause (int64_t, bool, const vector &) override; + void delete_clause (int64_t, bool, const std::vector &) override; - void finalize_clause (int64_t, const vector &) override {} // skip + void finalize_clause (int64_t, const std::vector &) override { + } // skip void report_status (int, int64_t) override {} // skip diff --git a/src/sat/cadical/module.make b/src/sat/cadical/module.make index 74f5d23cb..52fbb3bed 100644 --- a/src/sat/cadical/module.make +++ b/src/sat/cadical/module.make @@ -4,6 +4,7 @@ src/sat/cadical/cadical_analyze.cpp \ src/sat/cadical/cadical_arena.cpp \ src/sat/cadical/cadical_assume.cpp \ src/sat/cadical/cadical_averages.cpp \ +src/sat/cadical/cadical_backbone.cpp \ src/sat/cadical/cadical_backtrack.cpp \ src/sat/cadical/cadical_backward.cpp \ src/sat/cadical/cadical_bins.cpp \ @@ -87,5 +88,7 @@ src/sat/cadical/cadical_veripbtracer.cpp \ src/sat/cadical/cadical_version.cpp \ src/sat/cadical/cadical_vivify.cpp \ src/sat/cadical/cadical_walk.cpp \ +src/sat/cadical/cadical_walk_full_occs.cpp \ +src/sat/cadical/cadical_warmup.cpp \ src/sat/cadical/cadical_watch.cpp \ src/sat/cadical/cadical_kitten.c diff --git a/src/sat/cadical/options.hpp b/src/sat/cadical/options.hpp index 1527da2e1..442c2d8e3 100644 --- a/src/sat/cadical/options.hpp +++ b/src/sat/cadical/options.hpp @@ -29,6 +29,11 @@ OPTION( arena, 1, 0, 1,0,0,1, "allocate clauses in arena") \ OPTION( arenacompact, 1, 0, 1,0,0,1, "keep clauses compact") \ OPTION( arenasort, 1, 0, 1,0,0,1, "sort clauses in arena") \ OPTION( arenatype, 3, 1, 3,0,0,1, "1=clause, 2=var, 3=queue") \ +OPTION( backbone, 1, 0, 2,0,0,1, "binary clause backbone") \ +OPTION( backboneeffort, 20, 0,1e5,0,0,1, "binary effort in per mile") \ +OPTION( backbonemaxrounds,1e3, 0,1e5,0,0,1, "backbone rounds limit") \ +OPTION( backbonerounds, 100, 0,1e5,0,0,1, "backbone rounds limit") \ +OPTION( backbonethresh, 5, 0,1e9,1,0,1, "delay if ticks smaller thresh*clauses") \ OPTION( binary, 1, 0, 1,0,0,1, "use binary proof format") \ OPTION( block, 0, 0, 1,0,1,1, "blocked clause elimination") \ OPTION( blockmaxclslim, 1e5, 1,2e9,2,0,1, "maximum clause size") \ @@ -60,7 +65,7 @@ OPTION( conditionint, 1e4, 1,2e9,0,0,1, "initial conflict interval") \ OPTION( conditionmaxeff, 1e7, 0,2e9,1,0,1, "maximum condition efficiency") \ OPTION( conditionmaxrat, 100, 1,2e9,1,0,1, "maximum clause variable ratio") \ OPTION( conditionmineff, 0, 0,2e9,1,0,1, "minimum condition efficiency") \ -OPTION( congruence, 1, 0, 1,0,0,1, "congruence closure") \ +OPTION( congruence, 1, 0, 1,0,1,1, "congruence closure") \ OPTION( congruenceand, 1, 0, 1,0,0,1, "extract AND gates") \ OPTION( congruenceandarity,1e6,2,5e7,0,0,1, "AND gate arity limit") \ OPTION( congruencebinaries,1, 0, 1,0,0,1, "extract binary and strengthen ternary clauses") \ @@ -113,12 +118,14 @@ OPTION( ematrailslow, 1e5, 1,2e9,0,0,1, "window slow trail") \ OPTION( exteagerreasons, 1, 0, 1,0,0,1, "eagerly ask for all reasons (0: only when needed)") \ OPTION( exteagerrecalc, 1, 0, 1,0,0,1, "after eagerly asking for reasons recalculate all levels (0: trust the external tool)") \ OPTION( externallrat, 0, 0, 1,0,0,1, "external lrat") \ -OPTION( factor, 1, 0, 1,0,1,1, "bounded variable addition") \ +OPTION( factor, 0, 0, 1,0,1,1, "bounded variable addition") \ OPTION( factorcandrounds, 2, 0,2e9,0,0,1, "candidates reduction rounds") \ +OPTION( factordelay, 4, 0, 12,0,0,1, "delay bounded variable addition between eliminations") \ OPTION( factoreffort, 50, 0,1e6,0,0,1, "relative effort per mille") \ OPTION( factoriniticks, 300, 1,1e6,0,0,1, "initial effort in millions") \ OPTION( factorsize, 5, 2,2e9,0,0,1, "clause size limit") \ OPTION( factorthresh, 7, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ +OPTION( factorunbump, 1, 0, 1,0,1,1, "extension variable with lowest importance [1: as in kissat]") \ OPTION( fastelim, 1, 0, 1,0,1,1, "fast BVE during preprocessing") \ OPTION( fastelimbound, 8, 1,1e3,1,0,1, "fast BVE bound during preprocessing") \ OPTION( fastelimclslim, 1e2, 2,2e9,2,0,1, "fast BVE resolvent size limit") \ @@ -130,8 +137,9 @@ OPTION( flushint, 1e5, 1,2e9,0,0,1, "initial limit") \ OPTION( forcephase, 0, 0, 1,0,0,1, "always use initial phase") \ OPTION( frat, 0, 0, 2,0,0,1, "1=frat(lrat), 2=frat(drat)") \ OPTION( idrup, 0, 0, 1,0,0,1, "incremental proof format") \ -OPTION( ilb, 0, 0, 1,0,0,1, "ILB (incremental lazy backtrack)") \ -OPTION( ilbassumptions, 0, 0, 1,0,0,1, "trail reuse for assumptions (ILB-like)") \ +OPTION( ilb, 0, 0, 2,0,0,1, "ILB (incremental lazy backtrack) (0: no, 1: assumptions only, 2: everything)") \ +OPTION( incdecay, 1, 0, 4,0,0,1, "decay clauses when doing incremental clauses" ) \ +OPTION( incdecayint, 1e6, 1,2e9,0,0,1, "decay interval when doing incremental clauses" ) \ OPTION( inprobeint, 100, 1,2e9,0,0,1, "inprobing interval" ) \ OPTION( inprobing, 1, 0, 1,0,1,1, "enable probe inprocessing") \ OPTION( inprocessing, 1, 0, 1,0,1,1, "enable general inprocessing") \ @@ -143,7 +151,10 @@ OPTION( lidrup, 0, 0, 1,0,0,1, "linear incremental proof format") \ LOGOPT( log, 0, 0, 1,0,0,0, "enable logging") \ LOGOPT( logsort, 0, 0, 1,0,0,0, "sort logged clauses") \ OPTION( lrat, 0, 0, 1,0,0,1, "use LRAT proof format") \ -OPTION( lucky, 1, 0, 1,0,0,1, "search for lucky phases") \ +OPTION( lucky, 1, 0, 1,0,0,1, "lucky phases") \ +OPTION( luckyassumptions, 1, 0, 1,0,0,1, "lucky phases with assumptions") \ +OPTION( luckyearly, 1, 0, 1,0,0,1, "lucky phases before preprocessing") \ +OPTION( luckylate, 1, 0, 1,0,0,1, "lucky phases after preprocessing") \ OPTION( minimize, 1, 0, 1,0,0,1, "minimize learned clauses") \ OPTION( minimizedepth, 1e3, 0,1e3,0,0,1, "minimization depth") \ OPTION( minimizeticks, 1, 0, 1,0,0,1, "increment ticks in minimization") \ @@ -158,6 +169,12 @@ OPTION( probethresh, 0, 0,100,1,0,1, "delay if ticks smaller thresh*claus OPTION( profile, 2, 0, 4,0,0,0, "profiling level") \ QUTOPT( quiet, 0, 0, 1,0,0,0, "disable all messages") \ OPTION( radixsortlim, 32, 0,2e9,0,0,1, "radix sort limit") \ +OPTION( randec, 0, 0, 1,0,0,1, "random decisions") \ +OPTION( randecfocused, 1, 0, 1,0,0,1, "random decisions in focused mode") \ +OPTION( randecinit, 1e3, 2,2e9,0,0,1, "inital random decision interval") \ +OPTION( randecint, 500, 0,2e9,0,0,1, "random conflict length") \ +OPTION( randeclength, 10, 1,1e9,0,0,1, "length random decisions phases") \ +OPTION( randecstable, 0, 0, 1,0,0,1, "random decisions in stable mode") \ OPTION( realtime, 0, 0, 1,0,0,0, "real instead of process time") \ OPTION( recomputetier, 1, 0, 1,0,0,1, "recompute tiers") \ OPTION( reduce, 1, 0, 1,0,0,1, "reduce useless clauses") \ @@ -167,16 +184,18 @@ OPTION( reduceopt, 1, 0, 2,0,0,1, "0=prct,1=sqrt,2=max") \ OPTION( reducetarget, 75, 10,1e2,0,0,1, "reduce fraction in percent") \ OPTION( reducetier1glue, 2, 1,2e9,0,0,1, "glue of kept learned clauses") \ OPTION( reducetier2glue, 6, 1,2e9,0,0,1, "glue of tier two clauses") \ -OPTION( reluctant, 1024, 0,2e9,0,0,1, "reluctant doubling period") \ -OPTION( reluctantmax,1048576, 0,2e9,0,0,1, "reluctant doubling period") \ -OPTION( rephase, 1, 0, 1,0,0,1, "enable resetting phase") \ +OPTION( reluctant, 1, 0, 1,0,0,1, "stable reluctant doubling restarts") \ +OPTION( reluctantint, 1024, 0,2e9,0,0,1, "reluctant doubling period") \ +OPTION( reluctantmax,1048576, 0,2e9,0,0,1, "maximum reluctant doubling period") \ +OPTION( rephase, 1, 0, 2,0,0,1, "enable resetting phase (0=no,1=always,2=stable-only)") \ OPTION( rephaseint, 1e3, 1,2e9,0,0,1, "rephase interval") \ OPTION( report,reportdefault, 0, 1,0,0,1, "enable reporting") \ OPTION( reportall, 0, 0, 1,0,0,1, "report even if not successful") \ OPTION( reportsolve, 0, 0, 1,0,0,1, "use solving not process time") \ OPTION( restart, 1, 0, 1,0,0,1, "enable restarts") \ OPTION( restartint, 2, 1,2e9,0,0,1, "restart interval") \ -OPTION( restartmargin, 10, 0,1e2,0,0,1, "slow fast margin in percent") \ +OPTION( restartmarginfocused,10,0,25,0,0,1, "focused slow fast margin in percent") \ +OPTION( restartmarginstable ,25,0,25,0,0,1, "stable slow fast margin in percent") \ OPTION( restartreusetrail, 1, 0, 1,0,0,1, "enable trail reuse") \ OPTION( restoreall, 0, 0, 2,0,0,1, "restore all clauses (2=really)") \ OPTION( restoreflush, 0, 0, 1,0,0,1, "remove satisfied clauses") \ @@ -184,7 +203,7 @@ OPTION( reverse, 0, 0, 1,0,0,1, "reverse variable ordering") \ OPTION( score, 1, 0, 1,0,0,1, "use EVSIDS scores") \ OPTION( scorefactor, 950,500,1e3,0,0,1, "score factor per mille") \ OPTION( seed, 0, 0,2e9,0,0,1, "random seed") \ -OPTION( shrink, 3, 0, 3,0,0,1, "shrink conflict clause (1=only with binary, 2=minimize when pulling, 3=full)") \ +OPTION( shrink, 3, 0, 3,0,0,1, "shrink conflict clause (1=binary-only,2=minimize-on-pulling,3=full)") \ OPTION( shrinkreap, 1, 0, 1,0,0,1, "use a reap for shrinking") \ OPTION( shuffle, 0, 0, 1,0,0,1, "shuffle variables") \ OPTION( shufflequeue, 1, 0, 1,0,0,1, "shuffle variable queue") \ @@ -194,6 +213,7 @@ OPTION( stabilize, 1, 0, 1,0,0,1, "enable stabilizing phases") \ OPTION( stabilizeinit, 1e3, 1,2e9,0,0,1, "stabilizing interval") \ OPTION( stabilizeonly, 0, 0, 1,0,0,1, "only stabilizing phases") \ OPTION( stats, 0, 0, 1,0,0,1, "print all statistics at the end of the run") \ +OPTION( stubbornIOfocused, 0, 0, 1,0,0,1, "force phases to I/O in focused mode every once in a while (requires rephase==2)") \ OPTION( subsume, 1, 0, 1,0,1,1, "enable clause subsumption") \ OPTION( subsumebinlim, 1e4, 0,2e9,1,0,1, "watch list length limit") \ OPTION( subsumeclslim, 1e2, 0,2e9,2,0,1, "clause length limit") \ @@ -202,7 +222,7 @@ OPTION( subsumelimited, 1, 0, 1,0,0,1, "limit subsumption checks") \ OPTION( subsumemaxeff, 1e8, 0,2e9,1,0,1, "maximum subsuming efficiency") \ OPTION( subsumemineff, 0, 0,2e9,1,0,1, "minimum subsuming efficiency") \ OPTION( subsumeocclim, 1e2, 0,2e9,1,0,1, "watch list length limit") \ -OPTION( subsumestr, 1, 0, 1,0,0,1, "subsume strenghten") \ +OPTION( subsumestr, 1, 0, 1,0,0,1, "subsume strengthen") \ OPTION( sweep, 1, 0, 1,0,1,1, "enable SAT sweeping") \ OPTION( sweepclauses, 1024, 0,2e9,1,0,1, "environment clauses") \ OPTION( sweepcomplete, 0, 0, 1,0,0,1, "run SAT sweeping to completion") \ @@ -225,37 +245,41 @@ OPTION( ternaryocclim, 1e2, 1,2e9,2,0,1, "ternary occurrence limit") \ OPTION( ternaryrounds, 2, 1, 16,1,0,1, "maximum ternary rounds") \ OPTION( ternarythresh, 6, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ OPTION( tier1limit, 50, 0,100,0,0,1, "limit of tier1 usage in percentage") \ +OPTION( tier1minglue, 0, 0,100,0,0,1, "lowest tier1 limit") \ OPTION( tier2limit, 90, 0,100,0,0,1, "limit of tier2 usage in percentage") \ +OPTION( tier2minglue, 0, 0,100,0,0,1, "lowest tier2 limit") \ OPTION( transred, 1, 0, 1,0,1,1, "transitive reduction of BIG") \ OPTION( transredeffort, 1e2, 1,1e5,1,0,1, "relative efficiency per mille") \ OPTION( transredmaxeff, 1e8, 0,2e9,1,0,1, "maximum efficiency") \ OPTION( transredmineff, 0, 0,2e9,1,0,1, "minimum efficiency") \ QUTOPT( verbose, 0, 0, 3,0,0,0, "more verbose messages") \ -OPTION( veripb, 0, 0, 4,0,0,1, "odd=checkdeletions, > 2=drat") \ +OPTION( veripb, 0, 0, 4,0,0,1, "odd=check-deletions, >2 drat") \ OPTION( vivify, 1, 0, 1,0,1,1, "vivification") \ OPTION( vivifycalctier, 0, 0, 1,0,0,1, "recalculate tier limits") \ OPTION( vivifydemote, 0, 0, 1,0,1,1, "demote irredundant or delete directly") \ OPTION( vivifyeffort, 50, 0,1e5,1,0,1, "overall efficiency per mille") \ OPTION( vivifyflush, 1, 0, 1,1,0,1, "flush subsumed before vivification rounds") \ OPTION( vivifyinst, 1, 0, 1,0,0,1, "instantiate last literal when vivify") \ -OPTION( vivifyirred, 1, 0, 1,0,1,1, "vivification irred") \ +OPTION( vivifyirred, 1, 0, 1,0,0,1, "vivification of irredundant clauses") \ OPTION( vivifyirredeff, 3, 1,100,1,0,1, "irredundant efficiency per mille") \ OPTION( vivifyonce, 0, 0, 2,0,0,1, "vivify once: 1=red, 2=red+irr") \ OPTION( vivifyretry, 0, 0, 5,0,0,1, "re-vivify clause if vivify was successful") \ OPTION( vivifyschedmax, 5e3, 10,2e9,0,0,1, "maximum schedule size") \ OPTION( vivifythresh, 20, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ -OPTION( vivifytier1, 1, 0, 1,0,1,1, "vivification tier1") \ +OPTION( vivifytier1, 1, 0, 1,0,0,1, "vivification tier1") \ OPTION( vivifytier1eff, 4, 0,100,1,0,1, "relative tier1 effort") \ -OPTION( vivifytier2, 1, 0, 1,0,1,1, "vivification tier2") \ +OPTION( vivifytier2, 1, 0, 1,0,0,1, "vivification tier2") \ OPTION( vivifytier2eff, 2, 1,100,1,0,1, "relative tier2 effort") \ -OPTION( vivifytier3, 1, 0, 1,0,1,1, "vivification tier3") \ +OPTION( vivifytier3, 1, 0, 1,0,0,1, "vivification tier3") \ OPTION( vivifytier3eff, 1, 1,100,1,0,1, "relative tier3 effort") \ OPTION( walk, 1, 0, 1,0,0,1, "enable random walks") \ -OPTION( walkeffort, 20, 1,1e5,1,0,1, "relative efficiency per mille") \ -OPTION( walkmaxeff, 1e7, 0,2e9,1,0,1, "maximum efficiency") \ +OPTION( walkeffort, 80, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( walkfullocc, 0, 0, 1,1,0,1, "use Kissat's full occurrences instead of the single watched") \ +OPTION( walkmaxeff, 1e7, 0,2e9,1,0,1, "maximum efficiency (in 1e3 ticks)") \ OPTION( walkmineff, 0, 0,1e7,1,0,1, "minimum efficiency") \ OPTION( walknonstable, 1, 0, 1,0,0,1, "walk in non-stabilizing phase") \ OPTION( walkredundant, 0, 0, 1,0,0,1, "walk redundant clauses too") \ +OPTION( warmup, 1, 0, 1,0,0,1, "warmup before walk using propagation") \ // Note, keep an empty line right before this line because of the last '\'! // Also keep those single spaces after 'OPTION(' for proper sorting. diff --git a/src/sat/cadical/parse.hpp b/src/sat/cadical/parse.hpp index a37117f43..0e5feeb4a 100644 --- a/src/sat/cadical/parse.hpp +++ b/src/sat/cadical/parse.hpp @@ -4,6 +4,7 @@ #include "global.h" #include +#include #include ABC_NAMESPACE_CXX_HEADER_START @@ -34,6 +35,8 @@ class Parser { const char *parse_string (const char *str, char prev); const char *parse_positive_int (int &ch, int &res, const char *name); + const char *parse_positive_uint64_t (int &ch, uint64_t &res, + const char *name); const char *parse_lit (int &ch, int &lit, int &vars, int strict); const char *parse_dimacs_non_profiled (int &vars, int strict); const char *parse_solution_non_profiled (); diff --git a/src/sat/cadical/phases.hpp b/src/sat/cadical/phases.hpp index 98e54d439..08a1aed80 100644 --- a/src/sat/cadical/phases.hpp +++ b/src/sat/cadical/phases.hpp @@ -3,18 +3,19 @@ #include "global.h" +#include + ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { struct Phases { - vector best; // The current largest trail phase. - vector forced; // Forced through 'phase'. - vector min; // The current minimum unsatisfied phase. - vector prev; // Previous during local search. - vector saved; // The actual saved phase. - vector target; // The current target phase. + std::vector best; // The current largest trail phase. + std::vector forced; // Forced through 'phase'. + std::vector prev; // Previous during local search. + std::vector saved; // The actual saved phase. + std::vector target; // The current target phase. }; } // namespace CaDiCaL diff --git a/src/sat/cadical/profile.hpp b/src/sat/cadical/profile.hpp index f3799389b..c4fc2c219 100644 --- a/src/sat/cadical/profile.hpp +++ b/src/sat/cadical/profile.hpp @@ -51,6 +51,7 @@ struct Internal; PROFILE (analyze, 3) \ MROFILE (analyzestable, 4) \ MROFILE (analyzeunstable, 4) \ + PROFILE (backbone, 2) \ PROFILE (backward, 3) \ PROFILE (block, 2) \ PROFILE (bump, 4) \ @@ -107,7 +108,13 @@ struct Internal; PROFILE (transred, 3) \ PROFILE (unstable, 2) \ PROFILE (vivify, 2) \ - PROFILE (walk, 2) + PROFILE (walk, 2) \ + PROFILE (walkpick, 3) \ + PROFILE (walkbreak, 4) \ + PROFILE (walkflip, 3) \ + PROFILE (walkflipbroken, 4) \ + PROFILE (walkflipWL, 4) \ + PROFILE (warmup, 3) /*------------------------------------------------------------------------*/ @@ -173,11 +180,11 @@ struct Profiles { do { \ NON_CADICAL_QUIET_PROFILE_CODE (const double N = time (); \ const int L = internal->opts.profile;) \ - if (!preprocessing && !lookingahead) { \ + if (!internal->preprocessing && !internal->lookingahead) { \ NON_CADICAL_QUIET_PROFILE_CODE ( \ - if (stable && internal->profiles.stable.level <= L) \ + if (internal->stable && internal->profiles.stable.level <= L) \ internal->stop_profiling (internal->profiles.stable, N); \ - if (!stable && internal->profiles.unstable.level <= L) \ + if (!internal->stable && internal->profiles.unstable.level <= L) \ internal->stop_profiling (internal->profiles.unstable, N); \ if (internal->profiles.search.level <= L) \ internal->stop_profiling (internal->profiles.search, N);) \ @@ -197,20 +204,21 @@ struct Profiles { #define STOP_SIMPLIFIER(S, M) \ do { \ NON_CADICAL_QUIET_PROFILE_CODE ( \ - const double N = time (); const int L = internal->opts.profile; \ + const double N = internal->time (); \ + const int L = internal->opts.profile; \ if (internal->profiles.S.level <= L) \ internal->stop_profiling (internal->profiles.S, N); \ if (internal->profiles.simplify.level <= L) \ internal->stop_profiling (internal->profiles.simplify, N);) \ reset_mode (M); \ reset_mode (SIMPLIFY); \ - if (!preprocessing && !lookingahead) { \ + if (!internal->preprocessing && !internal->lookingahead) { \ NON_CADICAL_QUIET_PROFILE_CODE ( \ if (internal->profiles.search.level <= L) \ internal->start_profiling (internal->profiles.search, N); \ - if (stable && internal->profiles.stable.level <= L) \ + if (internal->stable && internal->profiles.stable.level <= L) \ internal->start_profiling (internal->profiles.stable, N); \ - if (!stable && internal->profiles.unstable.level <= L) \ + if (!internal->stable && internal->profiles.unstable.level <= L) \ internal->start_profiling (internal->profiles.unstable, N);) \ set_mode (SEARCH); \ } \ @@ -221,17 +229,18 @@ struct Profiles { #define START_INNER_WALK() \ do { \ - require_mode (SEARCH); \ - CADICAL_assert (!preprocessing); \ + require_mode (Mode::SEARCH); \ + CADICAL_assert (!internal->preprocessing); \ NON_CADICAL_QUIET_PROFILE_CODE ( \ - const double N = time (); const int L = internal->opts.profile; \ - if (stable && internal->profiles.stable.level <= L) \ + const double N = internal->time (); \ + const int L = internal->opts.profile; \ + if (internal->stable && internal->profiles.stable.level <= L) \ internal->stop_profiling (internal->profiles.stable, N); \ - if (!stable && internal->profiles.unstable.level <= L) \ + if (!internal->stable && internal->profiles.unstable.level <= L) \ internal->stop_profiling (internal->profiles.unstable, N); \ if (internal->profiles.walk.level <= L) \ internal->start_profiling (internal->profiles.walk, N);) \ - set_mode (WALK); \ + set_mode (Mode::WALK); \ } while (0) /*------------------------------------------------------------------------*/ @@ -239,16 +248,16 @@ struct Profiles { #define STOP_INNER_WALK() \ do { \ - require_mode (SEARCH); \ - CADICAL_assert (!preprocessing); \ + require_mode (Mode::SEARCH); \ + CADICAL_assert (!internal->preprocessing); \ reset_mode (WALK); \ NON_CADICAL_QUIET_PROFILE_CODE ( \ const double N = time (); const int L = internal->opts.profile; \ if (internal->profiles.walk.level <= L) \ internal->stop_profiling (internal->profiles.walk, N); \ - if (stable && internal->profiles.stable.level <= L) \ + if (internal->stable && internal->profiles.stable.level <= L) \ internal->start_profiling (internal->profiles.stable, N); \ - if (!stable && internal->profiles.unstable.level <= L) \ + if (!internal->stable && internal->profiles.unstable.level <= L) \ internal->start_profiling (internal->profiles.unstable, N); \ internal->profiles.walk.started = (N);) \ } while (0) @@ -258,10 +267,10 @@ struct Profiles { #define START_OUTER_WALK() \ do { \ - require_mode (SEARCH); \ - CADICAL_assert (!preprocessing); \ + require_mode (Mode::SEARCH); \ + CADICAL_assert (!internal->preprocessing); \ NON_CADICAL_QUIET_PROFILE_CODE (START (walk);) \ - set_mode (WALK); \ + set_mode (Mode::WALK); \ } while (0) /*------------------------------------------------------------------------*/ @@ -269,8 +278,8 @@ struct Profiles { #define STOP_OUTER_WALK() \ do { \ - require_mode (SEARCH); \ - CADICAL_assert (!preprocessing); \ + require_mode (Mode::SEARCH); \ + CADICAL_assert (!internal->preprocessing); \ reset_mode (WALK); \ NON_CADICAL_QUIET_PROFILE_CODE (STOP (walk);) \ } while (0) diff --git a/src/sat/cadical/proof.hpp b/src/sat/cadical/proof.hpp index 69be6b080..b97e57533 100644 --- a/src/sat/cadical/proof.hpp +++ b/src/sat/cadical/proof.hpp @@ -3,6 +3,9 @@ #include "global.h" +#include "tracer.hpp" +#include + ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -23,19 +26,20 @@ class Proof { Internal *internal; - vector clause; // of external literals - vector proof_chain; // LRAT style proof chain of clause - int64_t clause_id; // id of added clause + std::vector clause; // of external literals + std::vector proof_chain; // LRAT style proof chain of clause + int64_t clause_id; // id of added clause bool redundant; + int witness; // the 'tracers' - vector tracers; // tracers (ie checker) - vector file_tracers; // file tracers (ie LRAT tracer) + std::vector tracers; // tracers (ie checker) + std::vector 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 &); // ditto + void add_literals (const std::vector &); // ditto void add_original_clause ( bool restore = false); // notify observers of original clauses @@ -57,35 +61,42 @@ public: void disconnect (Tracer *t); // Add original clauses to the proof (for online proof checking). // - void add_original_clause (int64_t, bool, const vector &); + void add_original_clause (int64_t, bool, const std::vector &); - void add_assumption_clause (int64_t, const vector &, - const vector &); - void add_assumption_clause (int64_t, int, const vector &); + void add_assumption_clause (int64_t, const std::vector &, + const std::vector &); + void add_assumption_clause (int64_t, int, const std::vector &); void add_assumption (int); - void add_constraint (const vector &); + void add_constraint (const std::vector &); 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 &, + void add_external_original_clause (int64_t, bool, + const std::vector &, bool restore = false); - void delete_external_original_clause (int64_t, bool, const vector &); + void delete_external_original_clause (int64_t, bool, + const std::vector &); // Add derived (such as learned) clauses to the proof. // - void add_derived_empty_clause (int64_t, const vector &); - void add_derived_unit_clause (int64_t, int unit, const vector &); - void add_derived_clause (Clause *c, const vector &); - void add_derived_clause (int64_t, bool, const vector &, - const vector &); + void add_derived_empty_clause (int64_t, const std::vector &); + void add_derived_unit_clause (int64_t, int unit, + const std::vector &); + void add_derived_clause (Clause *c, const std::vector &); + void add_derived_clause (int64_t, bool, const std::vector &, + const std::vector &); + void add_derived_rat_clause (int64_t, bool, int, const std::vector &, + const std::vector &); + void add_derived_rat_clause (Clause *c, int w, + const std::vector &); // 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 &); - void weaken_minus (int64_t, const vector &); - void weaken_plus (int64_t, const vector &); + void delete_clause (int64_t, bool, const std::vector &); + void weaken_minus (int64_t, const std::vector &); + void weaken_plus (int64_t, const std::vector &); void delete_unit_clause (int64_t id, const int lit); void delete_clause (Clause *); void weaken_minus (Clause *); @@ -94,21 +105,21 @@ public: void finalize_unit (int64_t, int); void finalize_external_unit (int64_t, int); - void finalize_clause (int64_t, const vector &c); + void finalize_clause (int64_t, const std::vector &c); void finalize_clause (Clause *); void report_status (int, int64_t); void begin_proof (int64_t); - void conclude_unsat (ConclusionType, const vector &); - void conclude_sat (const vector &model); - void conclude_unknown (const vector &trace); + void conclude_unsat (ConclusionType, const std::vector &); + void conclude_sat (const std::vector &model); + void conclude_unknown (const std::vector &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 &); - void otfs_strengthen_clause (Clause *, const vector &, - const vector &); + void strengthen_clause (Clause *, int, const std::vector &); + void otfs_strengthen_clause (Clause *, const std::vector &, + const std::vector &); void flush (); }; diff --git a/src/sat/cadical/queue.hpp b/src/sat/cadical/queue.hpp index 14e1f2da7..72b5561ac 100644 --- a/src/sat/cadical/queue.hpp +++ b/src/sat/cadical/queue.hpp @@ -61,6 +61,16 @@ struct Queue { last = idx; l.next = 0; } + + inline void bury (Links &links, int idx) { + Link &l = links[idx]; + if ((l.next = first)) + links[first].prev = idx; + else + last = idx; + first = idx; + l.prev = 0; + } }; } // namespace CaDiCaL diff --git a/src/sat/cadical/range.hpp b/src/sat/cadical/range.hpp index 7049dab42..55a95ed66 100644 --- a/src/sat/cadical/range.hpp +++ b/src/sat/cadical/range.hpp @@ -52,6 +52,7 @@ struct Clause; class Range { static unsigned inc (unsigned u) { return u + 1u; } + static unsigned dec (unsigned u) { return u - 1u; } class iterator { int idx; @@ -63,11 +64,36 @@ class Range { return a.idx != b.idx; } }; + + // Reverse iterator for iterating from max_var down to 1 + class reverse_iterator { + int idx; + + public: + reverse_iterator (int i) : idx (i) {} + void operator++ () { idx = dec (idx); } + const int &operator* () const { return idx; } + friend bool operator!= (const reverse_iterator &a, + const reverse_iterator &b) { + return a.idx != b.idx; + } + }; + int &n; public: + // forward iterator iterator begin () const { return CADICAL_assert (n >= 0), iterator (inc (0)); } iterator end () const { return CADICAL_assert (n >= 0), iterator (inc (n)); } + + // Reverse iteration methods + reverse_iterator rbegin () const { + return CADICAL_assert (n >= 0), reverse_iterator (n); + } + reverse_iterator rend () const { + return CADICAL_assert (n >= 0), reverse_iterator (0); + } + Range (int &m) : n (m) { CADICAL_assert (m >= 0); } }; diff --git a/src/sat/cadical/stats.hpp b/src/sat/cadical/stats.hpp index 4a6afa9c9..92a1ca0f3 100644 --- a/src/sat/cadical/stats.hpp +++ b/src/sat/cadical/stats.hpp @@ -21,6 +21,7 @@ struct Stats { int64_t conflicts = 0; // generated conflicts in 'propagate' int64_t decisions = 0; // number of decisions in 'decide' + int64_t searches = 0; // number of calls to solver 'solve' struct { int64_t cover = 0; // propagated during covered clause elimination @@ -29,16 +30,23 @@ struct Stats { int64_t search = 0; // propagated literals during search int64_t transred = 0; // propagated during transitive reduction int64_t vivify = 0; // propagated during vivification - int64_t walk = 0; // propagated during local search + int64_t backbone = 0; // propagated during backbones } propagations; struct { int64_t search[2] = {0}; + int64_t backbone = 0; int64_t factor = 0; int64_t probe = 0; int64_t sweep = 0; int64_t ternary = 0; int64_t vivify = 0; + int64_t walk = 0; + int64_t walkflip = 0; // ticks added to approximate walk + int64_t walkflipbroken = 0; // ticks added to approximate walk + int64_t walkflipWL = 0; // ticks added to approximate walk + int64_t walkbreak = 0; // ticks added to approximate walk + int64_t walkpick = 0; // ticks added to approximate walk } ticks; struct { @@ -120,11 +128,20 @@ struct Stats { int64_t walk = 0; // phases improved through random walked } rephased; + struct { + int64_t decision = 0; + int64_t dummydecision = 0; + int64_t conflicts = 0; + int64_t propagated = 0; + int64_t count = 0; + } warmup; + struct { int64_t count = 0; int64_t broken = 0; int64_t flips = 0; int64_t minimum = 0; + int64_t improved = 0; } walk; struct { @@ -232,21 +249,28 @@ struct Stats { int64_t decompositions = 0; // number of SCC + ELS int64_t vivifications = 0; // number of vivifications int64_t vivifychecks = 0; // checked clauses during vivification - int64_t vivifydecs = 0; // vivification decisions - int64_t vivifyreused = 0; // reused vivification decisions - int64_t vivifysched = 0; // scheduled clauses for vivification - int64_t vivifysubs = 0; // subsumed clauses during vivification - int64_t vivifysubred = 0; // subsumed clauses during vivification - int64_t vivifysubirr = 0; // subsumed clauses during vivification - int64_t vivifystrs = 0; // strengthened clauses during vivification - int64_t vivifystrirr = 0; // strengthened irredundant clause - int64_t vivifystred1 = 0; // strengthened redundant clause (1) - int64_t vivifystred2 = 0; // strengthened redundant clause (2) - int64_t vivifystred3 = 0; // strengthened redundant clause (3) - int64_t vivifyunits = 0; // units during vivification - int64_t vivifyimplied = 0; // implied during vivification - int64_t vivifyinst = 0; // instantiation during vivification - int64_t vivifydemote = 0; // demoting during vivification + int64_t vivifiedirred = + 0; // irredundant vivified clauses during vivification + int64_t vivifiedtier1 = 0; // tier-1 vivified clauses during vivification + int64_t vivifiedtier2 = 0; // tier-2 vivified clauses during vivification + int64_t vivifiedtier3 = 0; // tier-3 vivified clauses during vivification + int64_t vivifydecs = 0; // vivification decisions + int64_t vivifyflushed = + 0; // subsumed clauses during sorting in vivification + int64_t vivifyreused = 0; // reused vivification decisions + int64_t vivifysched = 0; // scheduled clauses for vivification + int64_t vivifysubs = 0; // subsumed clauses during vivification + int64_t vivifysubred = 0; // subsumed clauses during vivification + int64_t vivifysubirr = 0; // subsumed clauses during vivification + int64_t vivifystrs = 0; // strengthened clauses during vivification + int64_t vivifystrirr = 0; // strengthened irredundant clause + int64_t vivifystred1 = 0; // strengthened redundant clause (1) + int64_t vivifystred2 = 0; // strengthened redundant clause (2) + int64_t vivifystred3 = 0; // strengthened redundant clause (3) + int64_t vivifyunits = 0; // units during vivification + int64_t vivifyimplied = 0; // implied during vivification + int64_t vivifyinst = 0; // instantiation during vivification + int64_t vivifydemote = 0; // demoting during vivification int64_t transreds = 0; int64_t transitive = 0; struct { @@ -339,13 +363,24 @@ struct Stats { int64_t unused = 0; // number of unused variables int64_t active = 0; // number of active variables int64_t inactive = 0; // number of inactive variables + + int64_t incremental_decay = 0; + struct { + int64_t random_decisions = 0; // number of random decisions + int64_t random_decision_phases = + 0; // number of phases of random decision + } randec; + std::vector bump_used = {0, 0}; std::vector> used; // used clauses in focused mode struct { int64_t gates = 0; + int64_t and_gates = 0; int64_t ands = 0; + int64_t ite_gates = 0; int64_t ites = 0; + int64_t xor_gates = 0; int64_t xors = 0; int64_t units = 0; int64_t congruent = 0; @@ -353,6 +388,8 @@ struct Stats { int64_t unary_and = 0; int64_t unaries = 0; int64_t rewritten_ands = 0; + int64_t rewritten_xors = 0; + int64_t rewritten_ites = 0; int64_t simplified = 0; int64_t simplified_ands = 0; int64_t simplified_xors = 0; @@ -362,6 +399,13 @@ struct Stats { int64_t unary_ites = 0; } congruence; + struct { + int64_t rounds = 0; + int64_t units = 0; + int64_t phases = 0; + int64_t probes = 0; + } backbone; + Stats (); void print (Internal *); diff --git a/src/sat/cadical/terminal.hpp b/src/sat/cadical/terminal.hpp index a99346b7c..661f692a6 100644 --- a/src/sat/cadical/terminal.hpp +++ b/src/sat/cadical/terminal.hpp @@ -30,8 +30,8 @@ class Terminal { } void code (const char *str) { - if (!use_colors) - return; + // if (!use_colors) + // return; if (!connected) return; escape (); diff --git a/src/sat/cadical/tracer.hpp b/src/sat/cadical/tracer.hpp index d1a9f1137..b806cd8ff 100644 --- a/src/sat/cadical/tracer.hpp +++ b/src/sat/cadical/tracer.hpp @@ -40,9 +40,11 @@ public: // Notify the observer that a new clause has been derived. // Includes ID and whether the clause is redundant or irredundant // If antecedents are derived they will be included here. - // Arguments: ID, redundant, clause, antecedents + // If witness != 0 it is a RAT clause. + // Arguments: ID, redundant, clause, witness, antecedents // - virtual void add_derived_clause (int64_t, bool, const std::vector &, + virtual void add_derived_clause (int64_t, bool, int, + const std::vector &, const std::vector &) {} // Notify the observer that a clause is deleted. @@ -143,6 +145,13 @@ public: // will give the current trail as a vector. // virtual void conclude_unknown (const std::vector &) {} + + // Notify the observer that two literals are equivalent + // + // You receive literals, not variables. You can also get notified + // multiple times. You can also get notified of BVA variables, aka + // variables you did not declare. + virtual void notify_equivalence (int, int) {} }; /*--------------------------------------------------------------------------*/ diff --git a/src/sat/cadical/util.hpp b/src/sat/cadical/util.hpp index 3882c6ede..a048d1087 100644 --- a/src/sat/cadical/util.hpp +++ b/src/sat/cadical/util.hpp @@ -120,6 +120,20 @@ template static void enlarge_zero (vector &v, size_t N) { enlarge_init (v, N, (const T &) 0); } +// double the capacity until it fits. This is different from reserve +// that will allocate exactly the size requested, meaning that the +// amortized complexity is lost. +template static void reserve_at_least (vector &v, size_t N) { + if (N < v.capacity ()) + return; + size_t new_size = v.size (); + if (!new_size) + new_size = N; + while (new_size < N) + new_size *= 2; + v.reserve (new_size); +} + // Clean-up class for bad_alloc error safety. template struct DeferDeleteArray { diff --git a/src/sat/cadical/veripbtracer.hpp b/src/sat/cadical/veripbtracer.hpp index b9c47362f..1c9560ee4 100644 --- a/src/sat/cadical/veripbtracer.hpp +++ b/src/sat/cadical/veripbtracer.hpp @@ -3,9 +3,10 @@ #include "global.h" -ABC_NAMESPACE_CXX_HEADER_START +#include "file.hpp" +#include "tracer.hpp" -class FileTracer; +ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -54,18 +55,20 @@ class VeripbTracer : public FileTracer { #ifndef CADICAL_QUIET int64_t added, deleted; #endif - vector delete_ids; + std::vector delete_ids; void put_binary_zero (); void put_binary_lit (int external_lit); void put_binary_id (int64_t id, bool = false); // support veriPB + void veripb_add_derived_clause (int64_t, bool redundant, int witness, + const std::vector &clause); void veripb_add_derived_clause (int64_t, bool redundant, - const vector &clause, - const vector &chain); + const std::vector &clause, + const std::vector &chain); void veripb_add_derived_clause (int64_t, bool redundant, - const vector &clause); + const std::vector &clause); void veripb_begin_proof (int64_t reserved_ids); void veripb_delete_clause (int64_t id, bool redundant); void veripb_report_status (bool unsat, int64_t conflict_id); @@ -79,18 +82,19 @@ public: void connect_internal (Internal *i) override; void begin_proof (int64_t) override; - void add_original_clause (int64_t, bool, const vector &, + void add_original_clause (int64_t, bool, const std::vector &, bool = false) override {} // skip - void add_derived_clause (int64_t, bool, const vector &, - const vector &) override; + void add_derived_clause (int64_t, bool, int, const std::vector &, + const std::vector &) override; - void delete_clause (int64_t, bool, const vector &) override; - void finalize_clause (int64_t, const vector &) override {} // skip + void delete_clause (int64_t, bool, const std::vector &) override; + void finalize_clause (int64_t, const std::vector &) override { + } // skip void report_status (int, int64_t) override; - void weaken_minus (int64_t, const vector &) override; + void weaken_minus (int64_t, const std::vector &) override; void strengthen (int64_t) override; #ifndef CADICAL_QUIET diff --git a/src/sat/cadical/vivify.hpp b/src/sat/cadical/vivify.hpp index a5c6c0e10..58d2047ee 100644 --- a/src/sat/cadical/vivify.hpp +++ b/src/sat/cadical/vivify.hpp @@ -30,9 +30,7 @@ struct vivify_ref { // be able to iterate over them, but we provide the reference to them to // make sure that you do need to remember the order. struct Vivifier { - std::array, 4> refs_schedules; - std::vector &refs_schedule_tier1, &refs_schedule_tier2, - &refs_schedule_tier3, &refs_schedule_irred; + std::vector refs_schedule; std::array, 4> schedules; std::vector &schedule_tier1, &schedule_tier2, &schedule_tier3, &schedule_irred; @@ -44,21 +42,11 @@ struct Vivifier { int64_t ticks; std::vector> lrat_stack; Vivifier (Vivify_Mode mode_tier) - : refs_schedule_tier1 (refs_schedules[0]), - refs_schedule_tier2 (refs_schedules[1]), - refs_schedule_tier3 (refs_schedules[2]), - refs_schedule_irred (refs_schedules[3]), - schedule_tier1 (schedules[0]), schedule_tier2 (schedules[1]), + : schedule_tier1 (schedules[0]), schedule_tier2 (schedules[1]), schedule_tier3 (schedules[2]), schedule_irred (schedules[3]), tier (mode_tier) {} - void erase () { - erase_vector (refs_schedule_tier1); - erase_vector (refs_schedule_tier2); - erase_vector (refs_schedule_tier3); - erase_vector (refs_schedule_irred); - erase_vector (sorted); - } + void erase () { erase_vector (sorted); } }; } // namespace CaDiCaL diff --git a/src/sat/cadical/walk.hpp b/src/sat/cadical/walk.hpp new file mode 100644 index 000000000..cb71c15be --- /dev/null +++ b/src/sat/cadical/walk.hpp @@ -0,0 +1,97 @@ +#ifndef _walk_hpp_INCLUDED +#define _walk_hpp_INCLUDED + +#include "global.h" + +#include "clause.hpp" + +ABC_NAMESPACE_CXX_HEADER_START + +namespace CaDiCaL { +struct TaggedBinary { + int lit, other; +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) + Clause *d; +#endif + TaggedBinary () + : lit (0), other (0) +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) + , + d (nullptr) +#endif + { + CADICAL_assert (false); + }; + + TaggedBinary (Clause *c, int clit, int cother) + : lit (clit), other (cother) +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) + , + d (c) +#endif + { +#ifdef LOGGING + CADICAL_assert (c->literals[0] == lit || c->literals[1] == lit); + CADICAL_assert (c->literals[0] == other || c->literals[1] == other); +#endif + +#ifndef LOGGING + (void) c; +#endif + } + + TaggedBinary (Clause *c) { + CADICAL_assert (c->size == 2); + lit = c->literals[0]; + other = c->literals[1]; +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) + d = c; +#else + (void) c; +#endif + } +}; + +union clause_or_binary_raw { + Clause *clause; + TaggedBinary b; + clause_or_binary_raw () : clause (nullptr) {} +}; + +// We experimented with using +// +// using ClauseOrBinary = std::variant ; +// +// instead of hand-rolling our own below, but the performance cost on +// vlsat2_144_7585.cnf.xz with a conflict limit of 2M conflicts was a +// factor 4 with: +// +// c 12.76 6.96% walkflipbroken +// +// vs +// +// c 49.86 22.63 % walkflipbroken +// +// And this is without doing any but stuffing to make the structure +// fit into 64 bits. +struct ClauseOrBinary { + bool binary; + clause_or_binary_raw tagged; + ClauseOrBinary () : binary (false) { tagged.clause = nullptr; } + ClauseOrBinary (Clause *c) : binary (false) { tagged.clause = c; } + ClauseOrBinary (TaggedBinary &&c) : binary (true) { tagged.b = c; } + bool is_binary () const { return binary; } + Clause *clause () const { + CADICAL_assert (!binary); + return tagged.clause; + } + TaggedBinary &tagged_binary () { + CADICAL_assert (binary); + return tagged.b; + } +}; +} // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END + +#endif diff --git a/src/sat/cadical/watch.hpp b/src/sat/cadical/watch.hpp index 1acab094b..e854a7df5 100644 --- a/src/sat/cadical/watch.hpp +++ b/src/sat/cadical/watch.hpp @@ -6,6 +6,8 @@ #include #include +#include "clause.hpp" + ABC_NAMESPACE_CXX_HEADER_START namespace CaDiCaL { @@ -37,6 +39,9 @@ struct Watch { int size; Watch (int b, Clause *c) : clause (c), blit (b), size (c->size) {} + Watch (bool, int b, Clause *c) : clause (c), blit (b), size (2) { + CADICAL_assert (c->size == 2); + } Watch () {} bool binary () const { return size == 2; } From c0e252846e1f67855c3eb5fb6ae909d8c65fe1af Mon Sep 17 00:00:00 2001 From: Marcus Comstedt Date: Wed, 24 Dec 2025 10:30:38 +0100 Subject: [PATCH 08/36] Fix memory corruption in &mfs. --- src/opt/sfm/sfmCnf.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/opt/sfm/sfmCnf.c b/src/opt/sfm/sfmCnf.c index 7c917fa7e..93dd976c8 100644 --- a/src/opt/sfm/sfmCnf.c +++ b/src/opt/sfm/sfmCnf.c @@ -111,8 +111,10 @@ int Sfm_TruthToCnf( word Truth, word * pTruth, int nVars, Vec_Int_t * vCover, Ve int i, k, c, RetValue, Literal, Cube, nCubes = 0; assert( nVars > 0 ); - Abc_TtFlipVar5( &Truth, nVars ); - Abc_TtFlipVar5( pTruth, nVars ); + if ( nVars <= 6) + Abc_TtFlipVar5( &Truth, nVars ); + else + Abc_TtFlipVar5( pTruth, nVars ); for ( c = 0; c < 2; c ++ ) { if ( nVars <= 6 ) @@ -148,7 +150,8 @@ int Sfm_TruthToCnf( word Truth, word * pTruth, int nVars, Vec_Int_t * vCover, Ve Vec_StrPush( vCnf, (char)-1 ); } } - Abc_TtFlipVar5( pTruth, nVars ); + if (nVars > 6) + Abc_TtFlipVar5( pTruth, nVars ); return nCubes; } From b822d47fcf31dd72233619bd4ee0264fa4419e16 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 13:12:10 -0800 Subject: [PATCH 09/36] Updating cofactoring procedure. --- src/aig/gia/giaDup.c | 57 +++++++++++++++++++++++++++++++++++++++++++- src/base/abci/abc.c | 29 ++++++++++++++++------ 2 files changed, 78 insertions(+), 8 deletions(-) diff --git a/src/aig/gia/giaDup.c b/src/aig/gia/giaDup.c index 2b4a0199f..ed0e682f4 100644 --- a/src/aig/gia/giaDup.c +++ b/src/aig/gia/giaDup.c @@ -6163,6 +6163,62 @@ Gia_Man_t * Gia_ManDupCofs( Gia_Man_t * p, Vec_Int_t * vVarNums ) Gia_ManStop( pTemp ); return pNew; } +Gia_Man_t * Gia_ManDupUnCofs( Gia_Man_t * p, Vec_Int_t * vVarNums ) +{ + Gia_Man_t * pNew, * pTemp; Gia_Obj_t * pObj; + Vec_Int_t * vOutLits, * vVarLits, * vTemp; + int i, v, g, nVars = Vec_IntSize(vVarNums); + int nMints = 1 << nVars; + int nOrigCos; + assert( Gia_ManRegNum(p) == 0 ); + assert( nMints > 0 ); + assert( Gia_ManCoNum(p) % nMints == 0 ); + nOrigCos = Gia_ManCoNum(p) / nMints; + vVarLits = Vec_IntStartFull( nVars ); + pNew = Gia_ManStart( Gia_ManObjNum(p) + Gia_ManCoNum(p) ); + pNew->pName = Abc_UtilStrsav( p->pName ); + Gia_ManFillValue( p ); + Gia_ManConst0(p)->Value = 0; + Gia_ManForEachCi( p, pObj, i ) + { + pObj->Value = Gia_ManAppendCi( pNew ); + if ( (v = Vec_IntFind( vVarNums, i )) >= 0 ) + Vec_IntWriteEntry( vVarLits, v, pObj->Value ); + } + Vec_IntForEachEntry( vVarLits, g, i ) + assert( g >= 0 ); + Gia_ManHashAlloc( pNew ); + Gia_ManForEachAnd( p, pObj, i ) + pObj->Value = Gia_ManHashAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); + vOutLits = Vec_IntAlloc( Gia_ManCoNum(p) ); + Gia_ManForEachCo( p, pObj, i ) + Vec_IntPush( vOutLits, Gia_ObjFanin0Copy(pObj) ); + vTemp = Vec_IntAlloc( nMints ); + for ( i = 0; i < nOrigCos; i++ ) + { + Vec_IntFill( vTemp, nMints, 0 ); + for ( g = 0; g < nMints; g++ ) + Vec_IntWriteEntry( vTemp, g, Vec_IntEntry(vOutLits, g * nOrigCos + i) ); + for ( v = 0; v < nVars; v++ ) + { + int Stride = 1 << v; + for ( g = 0; g < nMints; g += 2 * Stride ) + { + int iLit0 = Vec_IntEntry( vTemp, g ); + int iLit1 = Vec_IntEntry( vTemp, g + Stride ); + int iLitR = Gia_ManHashMux( pNew, Vec_IntEntry( vVarLits, v ), iLit1, iLit0 ); + Vec_IntWriteEntry( vTemp, g, iLitR ); + } + } + Gia_ManAppendCo( pNew, Vec_IntEntry( vTemp, 0 ) ); + } + Vec_IntFree( vTemp ); + Vec_IntFree( vOutLits ); + Vec_IntFree( vVarLits ); + pNew = Gia_ManCleanup( pTemp = pNew ); + Gia_ManStop( pTemp ); + return pNew; +} /**Function************************************************************* @@ -6673,4 +6729,3 @@ Gia_Man_t * Gia_ManDupExtractMffc( Gia_Man_t * p, Vec_Int_t * vLits, Vec_Int_t * ABC_NAMESPACE_IMPL_END - diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 33323687f..24994dbab 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -36623,10 +36623,11 @@ usage: int Abc_CommandAbc9Cofs( Abc_Frame_t * pAbc, int argc, char ** argv ) { extern Gia_Man_t * Gia_ManDupCofs( Gia_Man_t * p, Vec_Int_t * vVarNums ); + extern Gia_Man_t * Gia_ManDupUnCofs( Gia_Man_t * p, Vec_Int_t * vVarNums ); Gia_Man_t * pTemp; Vec_Int_t * vVars = NULL; - int c, iVar = 0, nVars = 0, fVerbose = 0; + int c, iVar = 0, nVars = 0, fLastVars = 0, fVerbose = 0, fUndo = 0; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "VNvh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "VNluvh" ) ) != EOF ) { switch ( c ) { @@ -36652,6 +36653,12 @@ int Abc_CommandAbc9Cofs( Abc_Frame_t * pAbc, int argc, char ** argv ) if ( nVars < 0 ) goto usage; break; + case 'l': + fLastVars ^= 1; + break; + case 'u': + fUndo ^= 1; + break; case 'v': fVerbose ^= 1; break; @@ -36672,8 +36679,14 @@ int Abc_CommandAbc9Cofs( Abc_Frame_t * pAbc, int argc, char ** argv ) Vec_IntPush( vVars, iVar ); } else if ( nVars ) { - Abc_Print( 0, "Cofactoring the first %d inputs.\n", nVars ); - vVars = Vec_IntStartNatural( nVars ); + Abc_Print( 0, "Cofactoring the %s %d inputs.\n", fLastVars ? "last":"first", nVars ); + if ( fLastVars) { + vVars = Vec_IntAlloc(nVars); + for ( int v = 0; v < nVars; v++ ) + Vec_IntPush( vVars, Gia_ManCiNum(pAbc->pGia)-nVars+v ); + } + else + vVars = Vec_IntStartNatural( nVars ); } else if ( globalUtilOptind < argc ) { vVars = Vec_IntAlloc( argc ); @@ -36684,16 +36697,18 @@ int Abc_CommandAbc9Cofs( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_Print( -1, "One of the parameters, -V or -L , should be set on the command line.\n" ); goto usage; } - pTemp = Gia_ManDupCofs( pAbc->pGia, vVars ); + pTemp = fUndo ? Gia_ManDupUnCofs( pAbc->pGia, vVars ) : Gia_ManDupCofs( pAbc->pGia, vVars ); Abc_FrameUpdateGia( pAbc, pTemp ); Vec_IntFree( vVars ); return 0; usage: - Abc_Print( -2, "usage: &cofs [-VN num] [-vh]\n" ); - Abc_Print( -2, "\t derives cofactors w.r.t the set of variables\n" ); + Abc_Print( -2, "usage: &cofs [-VN num] [-luvh]\n" ); + Abc_Print( -2, "\t derives cofactors or reconstructs them w.r.t the set of variables\n" ); Abc_Print( -2, "\t-V num : the zero-based ID of one variable to cofactor [default = %d]\n", iVar ); Abc_Print( -2, "\t-N num : cofactoring the given number of first input variables [default = %d]\n", nVars ); + Abc_Print( -2, "\t-l : toggle cofactoring last variables instead [default = %s]\n", fLastVars? "yes": "no" ); + Abc_Print( -2, "\t-u : undo cofactoring (recombine cofactors using muxes) [default = %s]\n", fUndo? "yes": "no" ); Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); Abc_Print( -2, "\t-h : print the command usage\n"); return 1; From 3d32b8b2adc0a354bea2cad904c511dfe4dc7af5 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 13:20:26 -0800 Subject: [PATCH 10/36] Updating "lutexact -c" to fix the change in Cadical after upgrade. --- src/sat/bmc/bmcMaj7.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sat/bmc/bmcMaj7.c b/src/sat/bmc/bmcMaj7.c index 9fd6da2fd..b957e8112 100644 --- a/src/sat/bmc/bmcMaj7.c +++ b/src/sat/bmc/bmcMaj7.c @@ -291,7 +291,8 @@ static Exa7_Man_t * Exa7_ManAlloc( Bmc_EsPar_t * pPars, word * pTruth ) p->pSat = cadical_solver_new(); p->nVarAlloc = Exa7_ManVarReserve( p ); assert( p->nVarAlloc >= p->iVar ); - cadical_solver_setnvars( p->pSat, p->nVarAlloc ); + //cadical_solver_setnvars( p->pSat, p->nVarAlloc ); + cadical_solver_setnvars( p->pSat, p->nVarAlloc+(p->nLutSize+1)*p->nNodes*(1 << p->nVars) ); if ( pPars->RuntimeLim ) Exa7_CadicalSetRuntimeLimit( p->pSat, pPars->RuntimeLim ); return p; @@ -585,7 +586,7 @@ static int Exa7_ManAddCnf( Exa7_Man_t * p, int iMint ) for ( i = 0; i < p->nVars; i++ ) p->VarVals[i] = (iMint >> i) & 1; // sat_solver_setnvars( p->pSat, p->iVar + (p->nLutSize+1)*p->nNodes ); - cadical_solver_setnvars( p->pSat, p->iVar + (p->nLutSize+1)*p->nNodes ); + //cadical_solver_setnvars( p->pSat, p->iVar + (p->nLutSize+1)*p->nNodes ); //printf( "Adding clauses for minterm %d with value %d.\n", iMint, Value ); for ( i = p->nVars; i < p->nObjs; i++ ) { From bd7fb12e183188c7bb8afdc2ac12fc1fc4ba7755 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 13:37:06 -0800 Subject: [PATCH 11/36] Upgrading "lutexact -c" to be like "lutexact -k". --- src/sat/bmc/bmcMaj7.c | 148 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 144 insertions(+), 4 deletions(-) diff --git a/src/sat/bmc/bmcMaj7.c b/src/sat/bmc/bmcMaj7.c index b957e8112..167216586 100644 --- a/src/sat/bmc/bmcMaj7.c +++ b/src/sat/bmc/bmcMaj7.c @@ -405,6 +405,77 @@ static void Exa7_ManPrintSolution( Exa7_Man_t * p, int fCompl ) } } +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static Vec_Wrd_t * Exa7_ManSaveTruthTables( Exa7_Man_t * p, int fCompl ) +{ + int i, k, nWordsNode, nMintsNode; + assert( p->nLutSize <= 8 ); + nMintsNode = 1 << p->nLutSize; + nWordsNode = (p->nLutSize <= 6) ? 1 : (p->nLutSize == 7 ? 2 : 4); + Vec_Wrd_t * vTruths = Vec_WrdStart( p->nObjs * nWordsNode ); + for ( i = p->nVars; i < p->nObjs; i++ ) + { + word Truth[4] = {0, 0, 0, 0}; + int iVarStart = 1 + p->LutMask*(i - p->nVars); + for ( k = 0; k < p->LutMask; k++ ) + { + if ( cadical_solver_get_var_value(p->pSat, iVarStart + k) ) + { + int bit = k + 1; // minterm index (minterm 0 is fixed to 0) + int w = bit >> 6; + int b = bit & 63; + Truth[w] |= ((word)1 << b); + } + } + // complement the output fully if needed (including minterm 0) + if ( i == p->nObjs - 1 && fCompl ) + { + for ( int w = 0; w < nWordsNode; w++ ) + { + word Mask; + int rem = nMintsNode - w * 64; + if ( rem <= 0 ) + Mask = 0; + else if ( rem >= 64 ) + Mask = ~(word)0; + else + Mask = (((word)1) << rem) - 1; + Truth[w] = (~Truth[w]) & Mask; + } + } + if ( p->nLutSize < 6 ) + Truth[0] = Abc_Tt6Stretch( Truth[0], p->nLutSize ); + for ( int w = 0; w < nWordsNode; w++ ) + Vec_WrdWriteEntry( vTruths, i * nWordsNode + w, Truth[w] ); + } + return vTruths; +} +static void Exa7_ManPrintPerm( Exa7_Man_t * p ) +{ + int i, k, iVar; + for ( i = p->nVars; i < p->nObjs; i++ ) + { + if ( i > p->nVars ) + printf( "_" ); + for ( k = p->nLutSize - 1; k >= 0; k-- ) + { + iVar = Exa7_ManFindFanin( p, i, k ); + if ( iVar >= 0 && iVar < p->nVars ) + printf( "%c", 'a'+iVar ); + } + } +} + /**Function************************************************************* Synopsis [] @@ -705,6 +776,9 @@ void Exa7_ManPrint( Exa7_Man_t * p, int i, int iMint, abctime clk ) } int Exa7_ManExactSynthesis( Bmc_EsPar_t * pPars ) { + extern int Exa7_ManExactSynthesisIter( Bmc_EsPar_t * pPars ); + if ( pPars->fMinNodes ) + return Exa7_ManExactSynthesisIter( pPars ); int i, status, Res = 0, iMint = 1; abctime clkTotal = Abc_Clock(); Exa7_Man_t * p; int fCompl = 0; @@ -713,7 +787,7 @@ int Exa7_ManExactSynthesis( Bmc_EsPar_t * pPars ) word * pFun = Abc_TtSymFunGenerate( pPars->pSymStr, pPars->nVars ); pPars->pTtStr = ABC_CALLOC( char, pPars->nVars > 2 ? (1 << (pPars->nVars-2)) + 1 : 2 ); Extra_PrintHexadecimalString( pPars->pTtStr, (unsigned *)pFun, pPars->nVars ); - if ( !pPars->fSilent ) printf( "Generated symmetric function: %s\n", pPars->pTtStr ); + if ( !pPars->fSilent && pPars->nVars <= 7 ) printf( "Generated symmetric function: %s\n", pPars->pTtStr ); ABC_FREE( pFun ); } if ( pPars->pTtStr ) @@ -745,21 +819,87 @@ int Exa7_ManExactSynthesis( Bmc_EsPar_t * pPars ) if ( pPars->fVerbose && status != CADICAL_UNDEC ) Exa7_ManPrint( p, i, iMint, Abc_Clock() - clkTotal ); if ( iMint == -1 ) - Exa7_ManPrintSolution( p, fCompl ), Res = 1; + { + Exa7_ManPrintSolution( p, fCompl ); + printf( "The variable permutation is \"" ); + Exa7_ManPrintPerm( p ); + printf( "\".\n" ); + if ( pPars->fDumpBlif ) + Exa7_ManDumpBlif( p, fCompl ); + if ( p->pPars->fGenTruths ) { + if ( p->pPars->vTruths ) + Vec_WrdFreeP( &p->pPars->vTruths ); + p->pPars->vTruths = Exa7_ManSaveTruthTables( p, fCompl ); + } + Res = 1; + } else if ( status == CADICAL_UNDEC ) printf( "The solver timed out after %d sec.\n", pPars->RuntimeLim ); else if ( !p->pPars->fSilent ) printf( "The problem has no solution.\n" ), Res = 2; if ( !pPars->fSilent && (p->nUsed[0] || p->nUsed[1]) ) printf( "Added = %d. Tried = %d. ", p->nUsed[1], p->nUsed[0] ); if ( !pPars->fSilent ) Abc_PrintTime( 1, "Total runtime", Abc_Clock() - clkTotal ); - if ( iMint == -1 && pPars->fDumpBlif ) - Exa7_ManDumpBlif( p, fCompl ); if ( pPars->pSymStr ) ABC_FREE( pPars->pTtStr ); Exa7_ManFree( p ); return Res; } +int Exa7_ManExactSynthesisIter( Bmc_EsPar_t * pPars ) +{ + pPars->fMinNodes = 0; + int nNodeMin = (pPars->nVars-2)/(pPars->nLutSize-1) + 1; + int nNodeMax = pPars->nNodes, Result = 0; + int fGenPerm = pPars->pPermStr == NULL; + for ( int n = nNodeMin; n <= nNodeMax; n++ ) { + if ( !pPars->fSilent ) printf( "\nTrying M = %d:\n", n ); + pPars->nNodes = n; + if ( !pPars->fUsePerm && fGenPerm ) { + Vec_Str_t * vStr = Vec_StrAlloc( 100 ); + for ( int v = 0; v < pPars->nLutSize; v++ ) + Vec_StrPush( vStr, 'a'+v ); + int nDupVars = Abc_MaxInt(0, (pPars->nLutSize-1) - (pPars->nVars-pPars->nLutSize)); + Vec_StrPush( vStr, '_' ); + for ( int v = 0; v < nDupVars; v++ ) + Vec_StrPush( vStr, 'a'+v ); + for ( int v = 0; v < pPars->nLutSize-1-nDupVars; v++ ) + Vec_StrPush( vStr, '*' ); + for ( int m = 2; m < pPars->nNodes; m++ ) { + Vec_StrPush( vStr, '_' ); + for ( int v = 0; v < pPars->nLutSize-1; v++ ) + Vec_StrPush( vStr, '*' ); + } + Vec_StrPush( vStr, '\0' ); + ABC_FREE( pPars->pPermStr ); + pPars->pPermStr = Vec_StrReleaseArray(vStr); + Vec_StrFree( vStr ); + } + else if ( pPars->fUsePerm && fGenPerm ) { + Vec_Str_t * vStr = Vec_StrAlloc( 100 ); + for ( int v = 0; v < pPars->nLutSize; v++ ) + Vec_StrPush( vStr, 'a'+v ); + for ( int m = 1; m < pPars->nNodes; m++ ) { + Vec_StrPush( vStr, '_' ); + if ( m & 1 ) + for ( int v = 0; v < pPars->nLutSize-1; v++ ) + Vec_StrPush( vStr, 'a'+(pPars->nVars-(pPars->nLutSize-1-v)) ); + else + for ( int v = 0; v < pPars->nLutSize-1; v++ ) + Vec_StrPush( vStr, 'a'+v ); + } + Vec_StrPush( vStr, '\0' ); + ABC_FREE( pPars->pPermStr ); + pPars->pPermStr = Vec_StrReleaseArray(vStr); + Vec_StrFree( vStr ); + } + Result = Exa7_ManExactSynthesis(pPars); + fflush( stdout ); + if ( Result == 1 ) + break; + } + return Result; +} + //////////////////////////////////////////////////////////////////////// /// END OF FILE /// From 050bab831470eb4bffc65e56dee6300fd98674b1 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 14:29:41 -0800 Subject: [PATCH 12/36] Adding missing names in "undc". --- src/aig/gia/giaDup.c | 53 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/aig/gia/giaDup.c b/src/aig/gia/giaDup.c index ed0e682f4..c393f9519 100644 --- a/src/aig/gia/giaDup.c +++ b/src/aig/gia/giaDup.c @@ -3586,6 +3586,8 @@ Gia_Man_t * Gia_ManDupZeroUndc( Gia_Man_t * p, char * pInit, int nNewPis, int fG // map X-valued flops into new PIs assert( (int)strlen(pInit) == Gia_ManRegNum(p) ); pPiLits = ABC_FALLOC( int, Gia_ManRegNum(p) ); + for ( i = 0; i < Gia_ManRegNum(p); i++ ) + pPiLits[i] = -1; for ( i = 0; i < Gia_ManRegNum(p); i++ ) if ( pInit[i] == 'x' || pInit[i] == 'X' ) pPiLits[i] = CountPis++; @@ -3625,7 +3627,6 @@ Gia_Man_t * Gia_ManDupZeroUndc( Gia_Man_t * p, char * pInit, int nNewPis, int fG assert( 0 ); } Gia_ManCleanMark0( p ); - ABC_FREE( pPiLits ); // build internal nodes Gia_ManForEachAnd( p, pObj, i ) pObj->Value = Gia_ManAppendAnd( pNew, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj) ); @@ -3644,6 +3645,56 @@ Gia_Man_t * Gia_ManDupZeroUndc( Gia_Man_t * p, char * pInit, int nNewPis, int fG Gia_ManSetRegNum( pNew, Gia_ManRegNum(p) + (int)(CountPis > Gia_ManPiNum(p)) ); if ( fVerbose ) printf( "Converted %d 1-valued FFs and %d DC-valued FFs.\n", Count1, CountPis-Gia_ManPiNum(p) ); + // propagate CI names if present and extend with init-value drivers + if ( p->vNamesIn ) + { + int nOldPis = Gia_ManPiNum(p); + int nNewInitPis = CountPis - nOldPis; + int hasResetCi = CountPis > nOldPis; + Vec_Ptr_t * vNamesIn = Vec_PtrAlloc( Gia_ManCiNum(pNew) ); + // original PIs + for ( i = 0; i < nOldPis && i < Vec_PtrSize(p->vNamesIn); i++ ) + Vec_PtrPush( vNamesIn, Abc_UtilStrsav( (char *)Vec_PtrEntry(p->vNamesIn, i) ) ); + // new PIs for X-init flops + for ( i = 0; i < nNewInitPis; i++ ) + Vec_PtrPush( vNamesIn, NULL ); + // extra user-added PIs (nNewPis) + for ( i = 0; i < nNewPis; i++ ) + Vec_PtrPush( vNamesIn, NULL ); + // flop outputs (ROs) + for ( i = 0; i < Gia_ManRegNum(p); i++ ) + { + int idxOld = nOldPis + i; + char * pNameRo = (idxOld < Vec_PtrSize(p->vNamesIn)) ? (char *)Vec_PtrEntry(p->vNamesIn, idxOld) : NULL; + Vec_PtrPush( vNamesIn, pNameRo ? Abc_UtilStrsav(pNameRo) : NULL ); + } + // reset CI name, if any + if ( hasResetCi ) + Vec_PtrPush( vNamesIn, Abc_UtilStrsav( "init_reset" ) ); + // fill in names for new init PIs using corresponding flop names + for ( i = 0; i < Gia_ManRegNum(p); i++ ) + { + if ( pPiLits[i] == -1 ) + continue; + int idxOldRo = nOldPis + i; + char * pBase = (idxOldRo < Vec_PtrSize(p->vNamesIn)) ? (char *)Vec_PtrEntry(p->vNamesIn, idxOldRo) : NULL; + char Buffer[100]; + if ( pBase && strlen(pBase) < sizeof(Buffer)-12 ) + sprintf( Buffer, "%s_init_val", pBase ); + else + sprintf( Buffer, "init_val_%d", i ); + Vec_PtrWriteEntry( vNamesIn, pPiLits[i], Abc_UtilStrsav(Buffer) ); + } + pNew->vNamesIn = vNamesIn; + } + if ( p->vNamesOut ) + { + Vec_Ptr_t * vNamesOut = Vec_PtrAlloc( Vec_PtrSize(p->vNamesOut) ); + for ( i = 0; i < Vec_PtrSize(p->vNamesOut); i++ ) + Vec_PtrPush( vNamesOut, Abc_UtilStrsav( (char *)Vec_PtrEntry(p->vNamesOut, i) ) ); + pNew->vNamesOut = vNamesOut; + } + ABC_FREE( pPiLits ); return pNew; } From 6ff6a382df4bec5009e8256d6851f6e229f76d36 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 14:31:42 -0800 Subject: [PATCH 13/36] Extending %yosys to handle asynch and uninitilized flops. --- src/base/wln/wlnCom.c | 18 +++++++++++------- src/base/wln/wlnRtl.c | 9 +++++---- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/base/wln/wlnCom.c b/src/base/wln/wlnCom.c index 5f7e57423..6ba90a804 100644 --- a/src/base/wln/wlnCom.c +++ b/src/base/wln/wlnCom.c @@ -93,7 +93,7 @@ void Wln_End( Abc_Frame_t * pAbc ) int Abc_CommandYosys( Abc_Frame_t * pAbc, int argc, char ** argv ) { extern Abc_Ntk_t * Wln_ReadMappedSystemVerilog( char * pFileName, char * pTopModule, char * pDefines, char * pLibrary, int fVerbose ); - extern Gia_Man_t * Wln_BlastSystemVerilog( char * pFileName, char * pTopModule, char * pDefines, int fSkipStrash, int fInvert, int fTechMap, int fLibInDir, int fVerbose ); + extern Gia_Man_t * Wln_BlastSystemVerilog( char * pFileName, char * pTopModule, char * pDefines, int fSkipStrash, int fInvert, int fTechMap, int fLibInDir, int fSetUndef, int fVerbose ); extern Rtl_Lib_t * Wln_ReadSystemVerilog( char * pFileName, char * pTopModule, char * pDefines, int fCollapse, int fVerbose ); FILE * pFile; @@ -107,9 +107,10 @@ int Abc_CommandYosys( Abc_Frame_t * pAbc, int argc, char ** argv ) int fLibInDir = 0; int fSkipStrash = 0; int fCollapse = 0; + int fSetUndef = 0; int c, fVerbose = 0; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "TDLbismlcvh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "TDLbisumlcvh" ) ) != EOF ) { switch ( c ) { @@ -158,6 +159,9 @@ int Abc_CommandYosys( Abc_Frame_t * pAbc, int argc, char ** argv ) case 'c': fCollapse ^= 1; break; + case 'u': + fSetUndef ^= 1; + break; case 'v': fVerbose ^= 1; break; @@ -203,11 +207,11 @@ int Abc_CommandYosys( Abc_Frame_t * pAbc, int argc, char ** argv ) { Gia_Man_t * pNew = NULL; if ( !strcmp( Extra_FileNameExtension(pFileName), "v" ) ) - pNew = Wln_BlastSystemVerilog( pFileName, pTopModule, pDefines, fSkipStrash, fInvert, fTechMap, fLibInDir, fVerbose ); + pNew = Wln_BlastSystemVerilog( pFileName, pTopModule, pDefines, fSkipStrash, fInvert, fTechMap, fLibInDir, fSetUndef, fVerbose ); else if ( !strcmp( Extra_FileNameExtension(pFileName), "sv" ) ) - pNew = Wln_BlastSystemVerilog( pFileName, pTopModule, pDefines, fSkipStrash, fInvert, fTechMap, fLibInDir, fVerbose ); + pNew = Wln_BlastSystemVerilog( pFileName, pTopModule, pDefines, fSkipStrash, fInvert, fTechMap, fLibInDir, fSetUndef, fVerbose ); else if ( !strcmp( Extra_FileNameExtension(pFileName), "rtlil" ) ) - pNew = Wln_BlastSystemVerilog( pFileName, pTopModule, pDefines, fSkipStrash, fInvert, fTechMap, fLibInDir, fVerbose ); + pNew = Wln_BlastSystemVerilog( pFileName, pTopModule, pDefines, fSkipStrash, fInvert, fTechMap, fLibInDir, fSetUndef, fVerbose ); else { printf( "Abc_CommandYosys(): Unknown file extension.\n" ); @@ -233,7 +237,7 @@ int Abc_CommandYosys( Abc_Frame_t * pAbc, int argc, char ** argv ) } return 0; usage: - Abc_Print( -2, "usage: %%yosys [-T ] [-D ] [-L ] [-bismlcvh] \n" ); + Abc_Print( -2, "usage: %%yosys [-T ] [-D ] [-L ] [-bisumlcvh] \n" ); Abc_Print( -2, "\t reads Verilog or SystemVerilog using Yosys\n" ); Abc_Print( -2, "\t-T : specify the top module name (default uses \"-auto-top\")\n" ); Abc_Print( -2, "\t-D : specify defines to be used by Yosys (default \"not used\")\n" ); @@ -244,6 +248,7 @@ usage: Abc_Print( -2, "\t-m : toggle using \"techmap\" to blast operators [default = %s]\n", fTechMap? "yes": "no" ); Abc_Print( -2, "\t-l : toggle looking for \"techmap.v\" in the current directory [default = %s]\n", fLibInDir? "yes": "no" ); Abc_Print( -2, "\t-c : toggle collapsing design hierarchy using Yosys [default = %s]\n", fCollapse? "yes": "no" ); + Abc_Print( -2, "\t-u : toggle replacing undefined/reset-X with zero using Yosys setundef [default = %s]\n", fSetUndef? "yes": "no" ); Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); Abc_Print( -2, "\t-h : print the command usage\n"); return 1; @@ -575,4 +580,3 @@ usage: ABC_NAMESPACE_IMPL_END - diff --git a/src/base/wln/wlnRtl.c b/src/base/wln/wlnRtl.c index 05e8be4d0..5b9f05c45 100644 --- a/src/base/wln/wlnRtl.c +++ b/src/base/wln/wlnRtl.c @@ -171,14 +171,14 @@ Rtl_Lib_t * Wln_ReadSystemVerilog( char * pFileName, char * pTopModule, char * p unlink( pFileTemp ); return pNtk; } -Gia_Man_t * Wln_BlastSystemVerilog( char * pFileName, char * pTopModule, char * pDefines, int fSkipStrash, int fInvert, int fTechMap, int fLibInDir, int fVerbose ) +Gia_Man_t * Wln_BlastSystemVerilog( char * pFileName, char * pTopModule, char * pDefines, int fSkipStrash, int fInvert, int fTechMap, int fLibInDir, int fSetUndef, int fVerbose ) { Gia_Man_t * pGia = NULL; char Command[1000]; char * pFileTemp = "_temp_.aig"; int fRtlil = strstr(pFileName, ".rtl") != NULL; int fSVlog = strstr(pFileName, ".sv") != NULL; - sprintf( Command, "%s -qp \"%s %s%s %s%s; hierarchy %s%s; flatten; proc; memory -nomap; memory_map; %saigmap; write_aiger -symbols %s\"", + sprintf( Command, "%s -qp \"%s %s%s %s%s; hierarchy %s%s; flatten; proc; opt; async2sync; opt; setundef -undriven -zero; %s%smemory -nomap; memory_map; dffunmap; opt_clean; opt_expr; aigmap; write_aiger -symbols %s\"", Wln_GetYosysName(), fRtlil ? "read_rtlil" : "read_verilog", pDefines ? "-D" : "", @@ -187,7 +187,9 @@ Gia_Man_t * Wln_BlastSystemVerilog( char * pFileName, char * pTopModule, char * pFileName, pTopModule ? "-top " : "-auto-top", pTopModule ? pTopModule : "", - fTechMap ? (fLibInDir ? "techmap -map techmap.v; setundef -zero; " : "techmap; setundef -zero; ") : "", pFileTemp ); + fTechMap ? (fLibInDir ? "techmap -map techmap.v; " : "techmap; ") : "", + fSetUndef ? "setundef -init -zero; " : "", + pFileTemp ); if ( fVerbose ) printf( "%s\n", Command ); if ( !Wln_ConvertToRtl(Command, pFileTemp) ) @@ -260,4 +262,3 @@ Abc_Ntk_t * Wln_ReadMappedSystemVerilog( char * pFileName, char * pTopModule, ch ABC_NAMESPACE_IMPL_END - From 87395e54f54cbcf7eee6e7f1d36cced8a175175d Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 14:33:35 -0800 Subject: [PATCH 14/36] Making sure "twoexact" works with functions up to 14 inputs. --- src/base/abci/abc.c | 2 +- src/sat/bmc/bmcMaj7.c | 19 ++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 24994dbab..78d3a2f77 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -10956,7 +10956,7 @@ int Abc_CommandLutExact( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_Print( -1, "Function with %d variales cannot be implemented with %d %d-input LUTs.\n", pPars->nVars, pPars->nNodes, pPars->nLutSize ); return 1; } - if ( pPars->fKissat ) + if ( pPars->fKissat || pPars->fCadical ) { if ( pPars->nVars > 14 ) { diff --git a/src/sat/bmc/bmcMaj7.c b/src/sat/bmc/bmcMaj7.c index 167216586..dc36a6118 100644 --- a/src/sat/bmc/bmcMaj7.c +++ b/src/sat/bmc/bmcMaj7.c @@ -54,7 +54,8 @@ static inline void Exa7_CadicalSetRuntimeLimit( cadical_solver * pSat, int nSeco /// DECLARATIONS /// //////////////////////////////////////////////////////////////////////// -#define MAJ_NOBJS 64 // Const0 + Const1 + nVars + nNodes +#define MAJ_NOBJS 64 // Const0 + Const1 + nVars + nNodes +#define MAJ_MAX_LUT 8 typedef struct Exa7_Man_t_ Exa7_Man_t; struct Exa7_Man_t_ @@ -71,7 +72,7 @@ struct Exa7_Man_t_ Vec_Wrd_t * vInfo; // nVars + nNodes + 1 Vec_Bit_t * vUsed2; // bit masks Vec_Bit_t * vUsed3; // bit masks - int VarMarks[MAJ_NOBJS][6][MAJ_NOBJS]; // variable marks + int VarMarks[MAJ_NOBJS][MAJ_MAX_LUT][MAJ_NOBJS]; // variable marks int VarVals[MAJ_NOBJS]; // values of the first nVars variables Vec_Wec_t * vOutLits; // output vars Vec_Wec_t * vInVars; // input vars @@ -277,6 +278,7 @@ static Exa7_Man_t * Exa7_ManAlloc( Bmc_EsPar_t * pPars, word * pTruth ) p->nVars = pPars->nVars; p->nNodes = pPars->nNodes; p->nLutSize = pPars->nLutSize; + assert( p->nLutSize <= MAJ_MAX_LUT ); p->LutMask = (1 << pPars->nLutSize) - 1; p->nObjs = pPars->nVars + pPars->nNodes; p->nWords = Abc_TtWordNum(pPars->nVars); @@ -334,7 +336,7 @@ static inline int Exa7_ManFindFanin( Exa7_Man_t * p, int i, int k ) static inline int Exa7_ManEval( Exa7_Man_t * p ) { static int Flag = 0; - int i, k, j, iMint; word * pFanins[6]; + int i, k, j, iMint; word * pFanins[MAJ_MAX_LUT]; for ( i = p->nVars; i < p->nObjs; i++ ) { int iVarStart = 1 + p->LutMask*(i - p->nVars); @@ -419,7 +421,7 @@ static void Exa7_ManPrintSolution( Exa7_Man_t * p, int fCompl ) static Vec_Wrd_t * Exa7_ManSaveTruthTables( Exa7_Man_t * p, int fCompl ) { int i, k, nWordsNode, nMintsNode; - assert( p->nLutSize <= 8 ); + assert( p->nLutSize <= MAJ_MAX_LUT ); nMintsNode = 1 << p->nLutSize; nWordsNode = (p->nLutSize <= 6) ? 1 : (p->nLutSize == 7 ? 2 : 4); Vec_Wrd_t * vTruths = Vec_WrdStart( p->nObjs * nWordsNode ); @@ -782,7 +784,9 @@ int Exa7_ManExactSynthesis( Bmc_EsPar_t * pPars ) int i, status, Res = 0, iMint = 1; abctime clkTotal = Abc_Clock(); Exa7_Man_t * p; int fCompl = 0; - word pTruth[64]; + int nTruthWords = Abc_TtWordNum( pPars->nVars ); + word * pTruth = ABC_CALLOC( word, nTruthWords ); + assert( pTruth ); if ( pPars->pSymStr ) { word * pFun = Abc_TtSymFunGenerate( pPars->pSymStr, pPars->nVars ); pPars->pTtStr = ABC_CALLOC( char, pPars->nVars > 2 ? (1 << (pPars->nVars-2)) + 1 : 2 ); @@ -793,8 +797,8 @@ int Exa7_ManExactSynthesis( Bmc_EsPar_t * pPars ) if ( pPars->pTtStr ) Abc_TtReadHex( pTruth, pPars->pTtStr ); else assert( 0 ); - assert( pPars->nVars <= 12 ); - assert( pPars->nLutSize <= 6 ); + assert( pPars->nVars <= 14 ); + assert( pPars->nLutSize <= 8 ); p = Exa7_ManAlloc( pPars, pTruth ); if ( pTruth[0] & 1 ) { fCompl = 1; Abc_TtNot( pTruth, p->nWords ); } status = Exa7_ManAddCnfStart( p, pPars->fOnlyAnd ); @@ -842,6 +846,7 @@ int Exa7_ManExactSynthesis( Bmc_EsPar_t * pPars ) if ( pPars->pSymStr ) ABC_FREE( pPars->pTtStr ); Exa7_ManFree( p ); + ABC_FREE( pTruth ); return Res; } From c0ea0cf4d0cfaf3cd4cc5725e9c3bd2877b194ae Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 15:36:42 -0800 Subject: [PATCH 15/36] Printing counter-examples in "cec" and "&cec". --- src/aig/gia/giaDeep.c | 16 ++++ src/base/abci/abc.c | 19 ++++- src/base/abci/abcVerify.c | 155 +++++++++++++++++++++++++++++++++++++- src/proof/cec/cec.h | 4 +- src/proof/cec/cecCec.c | 110 ++++++++++++++++++++++++++- src/proof/cec/cecCore.c | 4 +- 6 files changed, 300 insertions(+), 8 deletions(-) diff --git a/src/aig/gia/giaDeep.c b/src/aig/gia/giaDeep.c index bdd979eb2..9d2a2c875 100644 --- a/src/aig/gia/giaDeep.c +++ b/src/aig/gia/giaDeep.c @@ -238,6 +238,22 @@ Gia_Man_t * Gia_ManRandSyn( Gia_Man_t * p, unsigned random_seed ) return pRes; } +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Gia_Man_t * Gia_ManDeepSyn2( Gia_Man_t * pGia, int nIters, int nNoImpr, int TimeOut, int nAnds, int Seed, int fUseTwo, int fChoices, int fVerbose ) +{ + return Gia_ManDeepSyn( pGia, nIters, nNoImpr, TimeOut, nAnds, Seed, fUseTwo, fChoices, fVerbose ); +} + //////////////////////////////////////////////////////////////////////// /// END OF FILE /// //////////////////////////////////////////////////////////////////////// diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 78d3a2f77..fca1b8e6b 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -42510,6 +42510,9 @@ int Abc_CommandAbc9Cec( Abc_Frame_t * pAbc, int argc, char ** argv ) return 0; } } + pPars->pNameSpec = pGias[0] ? (pGias[0]->pSpec ? pGias[0]->pSpec : pGias[0]->pName) : NULL; + pPars->pNameImpl = pGias[1] ? (pGias[1]->pSpec ? pGias[1]->pSpec : pGias[1]->pName) : NULL; + pPars->vNamesIn = pGias[0] ? pGias[0]->vNamesIn : NULL; // compute the miter if ( Gia_ManCiNum(pGias[0]) < 6 ) { @@ -54533,9 +54536,10 @@ usage: int Abc_CommandAbc9DeepSyn( Abc_Frame_t * pAbc, int argc, char ** argv ) { extern Gia_Man_t * Gia_ManDeepSyn( Gia_Man_t * pGia, int nIters, int nNoImpr, int TimeOut, int nAnds, int Seed, int fUseTwo, int fChoices, int fVerbose ); - Gia_Man_t * pTemp; int c, nIters = 1, nNoImpr = ABC_INFINITY, TimeOut = 0, nAnds = 0, Seed = 0, fUseTwo = 0, fChoices = 0, fVerbose = 0; + extern Gia_Man_t * Gia_ManDeepSyn2( Gia_Man_t * pGia, int nIters, int nNoImpr, int TimeOut, int nAnds, int Seed, int fUseTwo, int fChoices, int fVerbose ); + Gia_Man_t * pTemp; int c, nIters = 1, nNoImpr = ABC_INFINITY, TimeOut = 0, nAnds = 0, Seed = 0, fUseTwo = 0, fChoices = 0, fOpt = 0, fVerbose = 0; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "IJTAStcvh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "IJTAStcovh" ) ) != EOF ) { switch ( c ) { @@ -54600,6 +54604,9 @@ int Abc_CommandAbc9DeepSyn( Abc_Frame_t * pAbc, int argc, char ** argv ) case 'c': fChoices ^= 1; break; + case 'o': + fOpt ^= 1; + break; case 'v': fVerbose ^= 1; break; @@ -54614,12 +54621,15 @@ int Abc_CommandAbc9DeepSyn( Abc_Frame_t * pAbc, int argc, char ** argv ) Abc_Print( -1, "Abc_CommandAbc9DeepSyn(): There is no AIG.\n" ); return 0; } - pTemp = Gia_ManDeepSyn( pAbc->pGia, nIters, nNoImpr, TimeOut, nAnds, Seed, fUseTwo, fChoices, fVerbose ); + if ( fOpt ) + pTemp = Gia_ManDeepSyn2( pAbc->pGia, nIters, nNoImpr, TimeOut, nAnds, Seed, fUseTwo, fChoices, fVerbose ); + else + pTemp = Gia_ManDeepSyn( pAbc->pGia, nIters, nNoImpr, TimeOut, nAnds, Seed, fUseTwo, fChoices, fVerbose ); Abc_FrameUpdateGia( pAbc, pTemp ); return 0; usage: - Abc_Print( -2, "usage: &deepsyn [-IJTAS ] [-tcvh]\n" ); + Abc_Print( -2, "usage: &deepsyn [-IJTAS ] [-tcovh]\n" ); Abc_Print( -2, "\t performs synthesis\n" ); Abc_Print( -2, "\t-I : the number of iterations [default = %d]\n", nIters ); Abc_Print( -2, "\t-J : the number of steps without improvements [default = %d]\n", nNoImpr ); @@ -54628,6 +54638,7 @@ usage: Abc_Print( -2, "\t-S : user-specified random seed (0 <= num <= 100) [default = %d]\n", Seed ); Abc_Print( -2, "\t-t : toggle using two-input LUTs [default = %s]\n", fUseTwo? "yes": "no" ); Abc_Print( -2, "\t-c : toggle computing structural choices [default = %s]\n", fChoices? "yes": "no" ); + Abc_Print( -2, "\t-o : toggle using optimization [default = %s]\n", fOpt? "yes": "no" ); Abc_Print( -2, "\t-v : toggle printing optimization summary [default = %s]\n", fVerbose? "yes": "no" ); Abc_Print( -2, "\t-h : print the command usage\n"); return 1; diff --git a/src/base/abci/abcVerify.c b/src/base/abci/abcVerify.c index 3ae3e1846..c4744ebe0 100644 --- a/src/base/abci/abcVerify.c +++ b/src/base/abci/abcVerify.c @@ -731,6 +731,147 @@ int * Abc_NtkVerifySimulatePattern( Abc_Ntk_t * pNtk, int * pModel ) return pValues; } +typedef struct Abc_NtkCexWord_t_ +{ + char * pBase; + Vec_Int_t * vBits; +} Abc_NtkCexWord_t; + +static Abc_NtkCexWord_t * Abc_NtkVerifyFindWord( Vec_Ptr_t * vWords, const char * pBase ) +{ + Abc_NtkCexWord_t * pWord; + int i; + Vec_PtrForEachEntry( Abc_NtkCexWord_t *, vWords, pWord, i ) + if ( !strcmp( pWord->pBase, pBase ) ) + return pWord; + pWord = ABC_ALLOC( Abc_NtkCexWord_t, 1 ); + pWord->pBase = ABC_ALLOC( char, strlen(pBase) + 1 ); + strcpy( pWord->pBase, pBase ); + pWord->vBits = Vec_IntAlloc( 0 ); + Vec_PtrPush( vWords, pWord ); + return pWord; +} + +static void Abc_NtkVerifyFreeWords( Vec_Ptr_t * vWords ) +{ + Abc_NtkCexWord_t * pWord; + int i; + Vec_PtrForEachEntry( Abc_NtkCexWord_t *, vWords, pWord, i ) + { + ABC_FREE( pWord->pBase ); + Vec_IntFree( pWord->vBits ); + ABC_FREE( pWord ); + } + Vec_PtrFree( vWords ); +} + +static int Abc_NtkVerifyParseBitName( const char * pName, char ** ppBase, int * pIndex ) +{ + const char * pOpen; + const char * pClose; + char * pEnd; + long Index; + *ppBase = NULL; + pClose = strrchr( pName, ']' ); + pOpen = pClose ? strrchr( pName, '[' ) : NULL; + if ( pOpen == NULL || pClose == NULL || pClose < pOpen || pClose[1] != 0 ) + return 0; + Index = strtol( pOpen + 1, &pEnd, 10 ); + if ( pEnd != pClose || Index < 0 ) + return 0; + *pIndex = (int)Index; + *ppBase = ABC_ALLOC( char, pOpen - pName + 1 ); + strncpy( *ppBase, pName, pOpen - pName ); + (*ppBase)[pOpen - pName] = 0; + return 1; +} + +static int Abc_NtkVerifyCollectWords( Vec_Ptr_t * vWords, const char * const * ppNames, const int * pValues, int nEntries ) +{ + int i, fHasVector = 0; + for ( i = 0; i < nEntries; i++ ) + { + char * pBase = NULL; + int Index = 0; + int fVector = Abc_NtkVerifyParseBitName( ppNames[i], &pBase, &Index ); + Abc_NtkCexWord_t * pWord = Abc_NtkVerifyFindWord( vWords, fVector ? pBase : ppNames[i] ); + Vec_IntSetEntry( pWord->vBits, fVector ? Index : 0, pValues[i] ); + fHasVector |= fVector; + if ( pBase ) + ABC_FREE( pBase ); + } + return fHasVector; +} + +static void Abc_NtkVerifyPrintWords( Vec_Ptr_t * vWords ) +{ + Abc_NtkCexWord_t * pWord; + int i, d, b, nBits, nHex, Digit; + Vec_PtrForEachEntry( Abc_NtkCexWord_t *, vWords, pWord, i ) + { + nBits = Vec_IntSize( pWord->vBits ); + nHex = (nBits + 3) / 4; + nHex = nHex ? nHex : 1; + { + char * pHex = ABC_ALLOC( char, nHex + 1 ); + for ( d = 0; d < nHex; d++ ) + { + Digit = 0; + for ( b = 0; b < 4; b++ ) + { + int iBit = d * 4 + b; + if ( iBit < nBits && Vec_IntEntry( pWord->vBits, iBit ) ) + Digit |= 1 << b; + } + pHex[nHex - d - 1] = "0123456789ABCDEF"[Digit]; + } + pHex[nHex] = 0; + printf( "%s = %d'h%s", pWord->pBase, nBits, pHex ); + ABC_FREE( pHex ); + } + if ( i + 1 < Vec_PtrSize(vWords) ) + printf( ", " ); + } +} + +void Abc_NtkVerifyPrintCex( const int * pModel, const int * pValues1, const int * pValues2, + const char * const * ppInputNames, int nInputs, const char * const * ppOutputNames, int nOutputs, + const char * pNtkName1, const char * pNtkName2 ) +{ + Vec_Ptr_t * vInputs = Vec_PtrAlloc( 0 ); + Vec_Ptr_t * vOutputs1 = Vec_PtrAlloc( 0 ); + Vec_Ptr_t * vOutputs2 = Vec_PtrAlloc( 0 ); + Abc_NtkVerifyCollectWords( vInputs, ppInputNames, pModel, nInputs ); + Abc_NtkVerifyCollectWords( vOutputs1, ppOutputNames, pValues1, nOutputs ); + if ( pValues2 ) + Abc_NtkVerifyCollectWords( vOutputs2, ppOutputNames, pValues2, nOutputs ); + if ( Vec_PtrSize(vInputs) || Vec_PtrSize(vOutputs1) || Vec_PtrSize(vOutputs2) ) + { + printf( "INPUT: " ); + Abc_NtkVerifyPrintWords( vInputs ); + printf( ". OUTPUT: " ); + Abc_NtkVerifyPrintWords( vOutputs1 ); + printf( " (%s)", pNtkName1 ? pNtkName1 : "network1" ); + if ( pValues2 && Vec_PtrSize(vOutputs2) ) + { + printf( ", " ); + Abc_NtkVerifyPrintWords( vOutputs2 ); + printf( " (%s)", pNtkName2 ? pNtkName2 : "network2" ); + } + printf( ".\n" ); + } + Abc_NtkVerifyFreeWords( vInputs ); + Abc_NtkVerifyFreeWords( vOutputs1 ); + Abc_NtkVerifyFreeWords( vOutputs2 ); +} + +static const char * Abc_NtkVerifyNetworkName( Abc_Ntk_t * pNtk ) +{ + if ( pNtk == NULL ) + return NULL; + return Abc_NtkSpec(pNtk) ? Abc_NtkSpec(pNtk) : Abc_NtkName(pNtk); +} + /**Function************************************************************* @@ -747,6 +888,8 @@ void Abc_NtkVerifyReportError( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int * pMode { Vec_Ptr_t * vNodes; Abc_Obj_t * pNode; + const char ** ppCiNames = NULL; + const char ** ppCoNames = NULL; int * pValues1, * pValues2; int nErrors, nPrinted, i, iNode = -1; @@ -755,6 +898,17 @@ void Abc_NtkVerifyReportError( Abc_Ntk_t * pNtk1, Abc_Ntk_t * pNtk2, int * pMode // get the CO values under this model pValues1 = Abc_NtkVerifySimulatePattern( pNtk1, pModel ); pValues2 = Abc_NtkVerifySimulatePattern( pNtk2, pModel ); + // print word-level counter-example, if available + ppCiNames = ABC_ALLOC( const char *, Abc_NtkCiNum(pNtk1) ); + Abc_NtkForEachCi( pNtk1, pNode, i ) + ppCiNames[i] = Abc_ObjName( pNode ); + ppCoNames = ABC_ALLOC( const char *, Abc_NtkCoNum(pNtk1) ); + Abc_NtkForEachCo( pNtk1, pNode, i ) + ppCoNames[i] = Abc_ObjName( pNode ); + Abc_NtkVerifyPrintCex( pModel, pValues1, pValues2, ppCiNames, Abc_NtkCiNum(pNtk1), + ppCoNames, Abc_NtkCoNum(pNtk1), Abc_NtkVerifyNetworkName(pNtk1), Abc_NtkVerifyNetworkName(pNtk2) ); + ABC_FREE( ppCiNames ); + ABC_FREE( ppCoNames ); // count the mismatches nErrors = 0; for ( i = 0; i < Abc_NtkCoNum(pNtk1); i++ ) @@ -1115,4 +1269,3 @@ int Abc_NtkIsValidCex( Abc_Ntk_t * pNtk, Abc_Cex_t * pCex ) ABC_NAMESPACE_IMPL_END - diff --git a/src/proof/cec/cec.h b/src/proof/cec/cec.h index a3fb06376..e360bb99d 100644 --- a/src/proof/cec/cec.h +++ b/src/proof/cec/cec.h @@ -140,6 +140,9 @@ struct Cec_ParCec_t_ int fVeryVerbose; // verbose stats int fVerbose; // verbose stats int iOutFail; // the number of failed output + const char * pNameSpec; // name of the first (spec) network + const char * pNameImpl; // name of the second (impl) network + Vec_Ptr_t * vNamesIn; // input names of the first network }; // sequential register correspodence parameters @@ -273,4 +276,3 @@ ABC_NAMESPACE_HEADER_END //////////////////////////////////////////////////////////////////////// /// END OF FILE /// //////////////////////////////////////////////////////////////////////// - diff --git a/src/proof/cec/cecCec.c b/src/proof/cec/cecCec.c index e52258b9d..5951b58fa 100644 --- a/src/proof/cec/cecCec.c +++ b/src/proof/cec/cecCec.c @@ -24,6 +24,10 @@ #include "misc/extra/extra.h" #include "sat/cnf/cnf.h" +void Abc_NtkVerifyPrintCex( const int * pModel, const int * pValues1, const int * pValues2, + const char * const * ppInputNames, int nInputs, const char * const * ppOutputNames, int nOutputs, + const char * pNtkName1, const char * pNtkName2 ); + ABC_NAMESPACE_IMPL_START @@ -35,6 +39,105 @@ ABC_NAMESPACE_IMPL_START /// FUNCTION DEFINITIONS /// //////////////////////////////////////////////////////////////////////// +static int * Cec_ManSimulateCombOutputs( Gia_Man_t * p, const int * pInputs ) +{ + Gia_Obj_t * pObj; + int * pValues, * pOutputs, i; + pValues = ABC_ALLOC( int, Gia_ManObjNum(p) ); + if ( pValues == NULL ) + return NULL; + pValues[0] = 0; + Gia_ManForEachPi( p, pObj, i ) + pValues[Gia_ObjId(p, pObj)] = pInputs[i]; + Gia_ManForEachAnd( p, pObj, i ) + { + int v0 = pValues[Gia_ObjFaninId0p(p, pObj)] ^ Gia_ObjFaninC0(pObj); + int v1 = pValues[Gia_ObjFaninId1p(p, pObj)] ^ Gia_ObjFaninC1(pObj); + pValues[Gia_ObjId(p, pObj)] = v0 & v1; + } + pOutputs = ABC_ALLOC( int, Gia_ManPoNum(p) ); + Gia_ManForEachPo( p, pObj, i ) + pOutputs[i] = pValues[Gia_ObjFaninId0p(p, pObj)] ^ Gia_ObjFaninC0(pObj); + ABC_FREE( pValues ); + return pOutputs; +} + +static const char * Cec_ManGetName( const char * pName, const char * pPrefix, int Idx, Vec_Ptr_t * vStore ) +{ + if ( pName ) + return pName; + { + char Buffer[64]; + sprintf( Buffer, "%s[%d]", pPrefix, Idx ); + pName = Abc_UtilStrsav( Buffer ); + Vec_PtrPush( vStore, (void *)pName ); + return pName; + } +} + +static void Cec_ManPrintCexSummary( Gia_Man_t * p, Abc_Cex_t * pCex, Cec_ParCec_t * pPars ) +{ + Vec_Ptr_t * vToFree = NULL; + const char ** ppInNames = NULL; + const char ** ppOutNames = NULL; + int * pInputs = NULL; + int * pOutputs = NULL; + int * pOut1 = NULL; + int * pOut2 = NULL; + int nPis, nPairs, i; + if ( (pPars && pPars->fSilent) || pCex == NULL ) + return; + if ( pCex->nRegs || pCex->iFrame || Gia_ManRegNum(p) ) + return; + if ( (Gia_ManPoNum(p) & 1) || Gia_ManPiNum(p) != pCex->nPis ) + return; + nPis = Gia_ManPiNum(p); + nPairs = Gia_ManPoNum(p) / 2; + pInputs = ABC_ALLOC( int, nPis ); + for ( i = 0; i < nPis; i++ ) + pInputs[i] = Abc_InfoHasBit( pCex->pData, pCex->nRegs + i ); + pOutputs = Cec_ManSimulateCombOutputs( p, pInputs ); + if ( pOutputs == NULL ) + goto finish; + pOut1 = ABC_ALLOC( int, nPairs ); + pOut2 = ABC_ALLOC( int, nPairs ); + for ( i = 0; i < nPairs; i++ ) + { + pOut1[i] = pOutputs[2 * i]; + pOut2[i] = pOutputs[2 * i + 1]; + } + vToFree = Vec_PtrAlloc( 16 ); + ppInNames = ABC_ALLOC( const char *, nPis ); + for ( i = 0; i < nPis; i++ ) + { + const char * pName = Gia_ObjCiName(p, i); + if ( pName == NULL && pPars && pPars->vNamesIn && i < Vec_PtrSize(pPars->vNamesIn) ) + pName = (const char *)Vec_PtrEntry( pPars->vNamesIn, i ); + ppInNames[i] = Cec_ManGetName( pName, "a", i, vToFree ); + } + ppOutNames = ABC_ALLOC( const char *, nPairs ); + for ( i = 0; i < nPairs; i++ ) + ppOutNames[i] = Cec_ManGetName( Gia_ObjCoName(p, 2 * i), "z", i, vToFree ); + Abc_NtkVerifyPrintCex( pInputs, pOut1, pOut2, ppInNames, nPis, ppOutNames, nPairs, + pPars && pPars->pNameSpec ? pPars->pNameSpec : Gia_ManName(p), + pPars && pPars->pNameImpl ? pPars->pNameImpl : (p->pSpec ? p->pSpec : Gia_ManName(p)) ); + +finish: + if ( vToFree ) + { + char * pName; + Vec_PtrForEachEntry( char *, vToFree, pName, i ) + ABC_FREE( pName ); + Vec_PtrFree( vToFree ); + } + ABC_FREE( ppInNames ); + ABC_FREE( ppOutNames ); + ABC_FREE( pOut1 ); + ABC_FREE( pOut2 ); + ABC_FREE( pOutputs ); + ABC_FREE( pInputs ); +} + /**Function************************************************************* Synopsis [Saves the input pattern with the given number.] @@ -160,6 +263,7 @@ int Cec_ManHandleSpecialCases( Gia_Man_t * p, Cec_ParCec_t * pPars ) } pPars->iOutFail = i/2; Cec_ManTransformPattern( p, i/2, NULL ); + Cec_ManPrintCexSummary( p, p->pCexComb, pPars ); return 0; } // get the drivers @@ -178,6 +282,7 @@ int Cec_ManHandleSpecialCases( Gia_Man_t * p, Cec_ParCec_t * pPars ) // if their compl attributes are the same - one should be complemented assert( Gia_ObjFaninC0(pObj1) == Gia_ObjFaninC0(pObj2) ); Abc_InfoSetBit( p->pCexComb->pData, Gia_ObjCioId(pDri1) ); + Cec_ManPrintCexSummary( p, p->pCexComb, pPars ); return 0; } // one of the drivers is a PI; another is a constant 0 @@ -197,6 +302,7 @@ int Cec_ManHandleSpecialCases( Gia_Man_t * p, Cec_ParCec_t * pPars ) Abc_InfoSetBit( p->pCexComb->pData, Gia_ObjCioId(pDri1) ); else Abc_InfoSetBit( p->pCexComb->pData, Gia_ObjCioId(pDri2) ); + Cec_ManPrintCexSummary( p, p->pCexComb, pPars ); return 0; } } @@ -383,6 +489,7 @@ int Cec_ManVerify( Gia_Man_t * pInit, Cec_ParCec_t * pPars ) Abc_Print( 1, "Networks are NOT EQUIVALENT. " ); Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); } + Cec_ManPrintCexSummary( p, p->pCexComb, pPars ); return 0; } Vec_IntFreeP( &pInit->vIdsEquiv ); @@ -417,6 +524,8 @@ int Cec_ManVerify( Gia_Man_t * pInit, Cec_ParCec_t * pPars ) p->pCexComb = pNew->pCexComb; pNew->pCexComb = NULL; if ( p->pCexComb && !Gia_ManVerifyCex( p, p->pCexComb, 1 ) ) Abc_Print( 1, "Counter-example simulation has failed.\n" ); + if ( RetValue == 0 ) + Cec_ManPrintCexSummary( p, p->pCexComb, pPars ); Gia_ManStop( pNew ); return RetValue; } @@ -601,4 +710,3 @@ Aig_Man_t * Cec_FraigCombinational( Aig_Man_t * pAig, int nConfs, int fVerbose ) ABC_NAMESPACE_IMPL_END - diff --git a/src/proof/cec/cecCore.c b/src/proof/cec/cecCore.c index f94bd27f9..fdba94f00 100644 --- a/src/proof/cec/cecCore.c +++ b/src/proof/cec/cecCore.c @@ -167,6 +167,9 @@ void Cec_ManCecSetDefaultParams( Cec_ParCec_t * p ) p->fVeryVerbose = 0; // verbose stats p->fVerbose = 0; // verbose stats p->iOutFail = -1; // the number of failed output + p->pNameSpec = NULL; // name of the first (spec) network + p->pNameImpl = NULL; // name of the second (impl) network + p->vNamesIn = NULL; // input names of the first network } /**Function************************************************************* @@ -569,4 +572,3 @@ finalize: ABC_NAMESPACE_IMPL_END - From 58023c97b79473d0f711a7f99fd801c6a3da9f37 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 15:57:57 -0800 Subject: [PATCH 16/36] Added counter-example printout to "&cec -t". --- src/base/abci/abc.c | 23 ++++++++++++++++++++++- src/proof/cec/cecCec.c | 2 +- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index fca1b8e6b..4016ff3d8 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -42284,6 +42284,7 @@ usage: ***********************************************************************/ int Abc_CommandAbc9Cec( Abc_Frame_t * pAbc, int argc, char ** argv ) { + extern void Cec_ManPrintCexSummary( Gia_Man_t * p, Abc_Cex_t * pCex, Cec_ParCec_t * pPars ); Cec_ParCec_t ParsCec, * pPars = &ParsCec; FILE * pFile; Gia_Man_t * pGias[2] = {NULL, NULL}, * pMiter; @@ -42553,12 +42554,32 @@ int Abc_CommandAbc9Cec( Abc_Frame_t * pAbc, int argc, char ** argv ) extern int Gia_ManCheckSimEquiv( Gia_Man_t * p, int fVerbose ); int Status = Gia_ManCheckSimEquiv( pMiter, pPars->fVerbose ); if ( Status == 1 ) + { Abc_Print( 1, "Networks are equivalent. " ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } else if ( Status == 0 ) + { Abc_Print( 1, "Networks are NOT equivalent. " ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + { + Cec_ParCec_t ParsTemp = *pPars; + ParsTemp.fSilent = 1; + Cec_ManVerify( pMiter, &ParsTemp ); + if ( pMiter->pCexComb ) + { + Cec_ManPrintCexSummary( pMiter, pMiter->pCexComb, pPars ); + pGias[0]->pCexComb = pMiter->pCexComb; + pMiter->pCexComb = NULL; + Abc_FrameReplaceCex( pAbc, &pGias[0]->pCexComb ); + } + } + } else + { Abc_Print( 1, "Networks are UNDECIDED. " ); - Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + } } else if ( fUseNewX ) { diff --git a/src/proof/cec/cecCec.c b/src/proof/cec/cecCec.c index 5951b58fa..e881c53be 100644 --- a/src/proof/cec/cecCec.c +++ b/src/proof/cec/cecCec.c @@ -75,7 +75,7 @@ static const char * Cec_ManGetName( const char * pName, const char * pPrefix, in } } -static void Cec_ManPrintCexSummary( Gia_Man_t * p, Abc_Cex_t * pCex, Cec_ParCec_t * pPars ) +void Cec_ManPrintCexSummary( Gia_Man_t * p, Abc_Cex_t * pCex, Cec_ParCec_t * pPars ) { Vec_Ptr_t * vToFree = NULL; const char ** ppInNames = NULL; From 60f52cc0826a0c022dcfd5a1316da2ef9b8ac6d5 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 17:45:00 -0800 Subject: [PATCH 17/36] Changes to "read_jsonc". --- src/base/io/io.c | 10 +- src/base/io/ioJsonc.c | 536 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 502 insertions(+), 44 deletions(-) diff --git a/src/base/io/io.c b/src/base/io/io.c index b30b70fca..d841bdb52 100644 --- a/src/base/io/io.c +++ b/src/base/io/io.c @@ -1890,7 +1890,8 @@ usage: ***********************************************************************/ int IoCommandReadJsonc( Abc_Frame_t * pAbc, int argc, char ** argv ) { - extern int Jsonc_ReadTest( char * pFileName ); + extern Abc_Ntk_t * Jsonc_ReadNetwork( char * pFileName ); + Abc_Ntk_t * pNtk; char * pFileName; FILE * pFile; int c; @@ -1920,7 +1921,11 @@ int IoCommandReadJsonc( Abc_Frame_t * pAbc, int argc, char ** argv ) } fclose( pFile ); - Jsonc_ReadTest( pFileName ); + pNtk = Jsonc_ReadNetwork( pFileName ); + if ( pNtk == NULL ) + return 1; + Abc_FrameReplaceCurrentNetwork( pAbc, pNtk ); + Abc_FrameClearVerifStatus( pAbc ); return 0; usage: @@ -4607,4 +4612,3 @@ usage: ABC_NAMESPACE_IMPL_END - diff --git a/src/base/io/ioJsonc.c b/src/base/io/ioJsonc.c index 43ce5c271..5efffe995 100644 --- a/src/base/io/ioJsonc.c +++ b/src/base/io/ioJsonc.c @@ -21,12 +21,14 @@ #include #include #include +#include #include #include -#include -#include "ioAbc.h" +#include "base/abc/abc.h" +#include "base/main/main.h" #include "map/mio/mio.h" +#include "ioAbc.h" ABC_NAMESPACE_IMPL_START @@ -45,38 +47,50 @@ typedef enum { } json_type_t; // Core structure: stores offset and length in the original string -typedef struct { - uint32_t offset; // Offset in the JSON string - uint32_t length; // Length of this value - json_type_t type; // Type of JSON value +typedef struct json_value_t_ { + uint32_t offset; // Offset in the JSON string + uint32_t length; // Length of this value + json_type_t type; // Type of JSON value + uint32_t container; // Index of the container if this is an object/array } json_value_t; // Key-value pair for objects -typedef struct { - json_value_t key; // Key (always a string) - json_value_t value; // Associated value +typedef struct json_pair_t_ { + json_value_t key; // Key (always a string) + json_value_t value; // Associated value } json_pair_t; // Dynamic array for storing pairs (objects) or values (arrays) -typedef struct { +typedef struct json_container_t_ { union { json_pair_t *pairs; // For objects json_value_t *values; // For arrays } data; - uint32_t count; // Number of elements - uint32_t capacity; // Allocated capacity + uint32_t count; // Number of elements + uint32_t capacity; // Allocated capacity } json_container_t; // Main JSON structure -typedef struct { - char *str; // The original JSON string - uint32_t str_len; // Length of the string - json_value_t root; // Root element - json_container_t *containers; // Array of containers - uint32_t container_count; // Number of containers - uint32_t container_capacity; // Allocated capacity +typedef struct json_t_ { + char *str; // The original JSON string + uint32_t str_len; // Length of the string + json_value_t root; // Root element + json_container_t *containers; // Array of containers + uint32_t container_count; // Number of containers + uint32_t container_capacity; // Allocated capacity } json_t; +// Creation / destruction +json_t * json_create(void); +void json_destroy(json_t *json); +bool json_load_file(json_t *json, const char *filename); + +// Debug/printing helpers +void json_print(json_t *json, FILE *fp); +void json_print_value(json_t *json, json_value_t val, FILE *fp, int indent, bool format); +void json_print_string(json_t *json, json_value_t val, FILE *fp); +void json_debug_value(json_t *json, json_value_t val, int indent); + // Function prototypes json_t* json_create(void); void json_destroy(json_t *json); @@ -93,6 +107,12 @@ void json_print_value(json_t *json, json_value_t val, FILE *fp, int indent, bool void json_print_string(json_t *json, json_value_t val, FILE *fp); void json_debug_value(json_t *json, json_value_t val, int indent); +extern Abc_Ntk_t * Abc_NtkFromMiniMapping( int * pArray ); + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + // Create a new JSON structure json_t* json_create(void) { json_t *json = (json_t*)calloc(1, sizeof(json_t)); @@ -185,6 +205,7 @@ json_container_t* json_add_container(json_t *json) { // Parse a JSON string json_value_t json_parse_string(json_t *json, uint32_t *pos) { json_value_t val = {0}; + val.container = UINT32_MAX; val.type = JSON_STRING; val.offset = *pos; @@ -203,6 +224,7 @@ json_value_t json_parse_string(json_t *json, uint32_t *pos) { // Parse a JSON number json_value_t json_parse_number(json_t *json, uint32_t *pos) { json_value_t val = {0}; + val.container = UINT32_MAX; val.type = JSON_NUMBER; val.offset = *pos; @@ -223,6 +245,7 @@ json_value_t json_parse_number(json_t *json, uint32_t *pos) { // Parse a JSON object json_value_t json_parse_object(json_t *json, uint32_t *pos) { json_value_t val = {0}; + val.container = UINT32_MAX; val.type = JSON_OBJECT; val.offset = *pos; @@ -230,6 +253,8 @@ json_value_t json_parse_object(json_t *json, uint32_t *pos) { json_container_t *container = json_add_container(json); if (!container) return val; + val.container = json->container_count - 1; + container = json->containers + val.container; container->capacity = 8; container->data.pairs = (json_pair_t*)calloc(container->capacity, sizeof(json_pair_t)); @@ -249,6 +274,7 @@ json_value_t json_parse_object(json_t *json, uint32_t *pos) { json_value_t value = json_parse_value(json, pos); // Add pair to container + container = json->containers + val.container; if (container->count >= container->capacity) { uint32_t new_capacity = container->capacity * 2; json_pair_t *new_pairs = (json_pair_t*)realloc( @@ -278,6 +304,7 @@ json_value_t json_parse_object(json_t *json, uint32_t *pos) { // Parse a JSON array json_value_t json_parse_array(json_t *json, uint32_t *pos) { json_value_t val = {0}; + val.container = UINT32_MAX; val.type = JSON_ARRAY; val.offset = *pos; @@ -285,6 +312,8 @@ json_value_t json_parse_array(json_t *json, uint32_t *pos) { json_container_t *container = json_add_container(json); if (!container) return val; + val.container = json->container_count - 1; + container = json->containers + val.container; container->capacity = 8; container->data.values = (json_value_t*)calloc(container->capacity, sizeof(json_value_t)); @@ -297,6 +326,7 @@ json_value_t json_parse_array(json_t *json, uint32_t *pos) { json_value_t value = json_parse_value(json, pos); // Add value to container + container = json->containers + val.container; if (container->count >= container->capacity) { uint32_t new_capacity = container->capacity * 2; json_value_t *new_values = (json_value_t*)realloc( @@ -325,6 +355,7 @@ json_value_t json_parse_array(json_t *json, uint32_t *pos) { // Parse any JSON value json_value_t json_parse_value(json_t *json, uint32_t *pos) { json_value_t val = {0}; + val.container = UINT32_MAX; json_skip_whitespace(json->str, pos, json->str_len); @@ -342,18 +373,21 @@ json_value_t json_parse_value(json_t *json, uint32_t *pos) { val.type = JSON_NULL; val.offset = *pos; val.length = 4; + val.container = UINT32_MAX; *pos += 4; return val; } else if (c == 't') { val.type = JSON_BOOL; val.offset = *pos; val.length = 4; + val.container = UINT32_MAX; *pos += 4; return val; } else if (c == 'f') { val.type = JSON_BOOL; val.offset = *pos; val.length = 5; + val.container = UINT32_MAX; *pos += 5; return val; } else if ((c >= '0' && c <= '9') || c == '-') { @@ -397,6 +431,7 @@ bool json_load_file(json_t *json, const char *filename) { json->str[file_size] = '\0'; json->str_len = (uint32_t)file_size; + json->container_count = 0; // Parse JSON uint32_t pos = 0; @@ -442,24 +477,8 @@ void json_print_string(json_t *json, json_value_t val, FILE *fp) { fputc('"', fp); } -// Find the container index for a given value -static int find_container_index(json_t *json, json_value_t val) { - static int last_container = 0; - - // Check if it's an object or array - if (val.type == JSON_OBJECT || val.type == JSON_ARRAY) { - int index = last_container++; - if (index < json->container_count) { - return index; - } - } - return -1; -} - // Print a JSON value recursively void json_print_value(json_t *json, json_value_t val, FILE *fp, int indent, bool format) { - static int container_index = 0; - switch (val.type) { case JSON_NULL: fprintf(fp, "null"); @@ -486,8 +505,8 @@ void json_print_value(json_t *json, json_value_t val, FILE *fp, int indent, bool case JSON_ARRAY: { fputc('[', fp); - if (container_index < json->container_count) { - json_container_t *container = &json->containers[container_index++]; + if (val.container < json->container_count) { + json_container_t *container = &json->containers[val.container]; for (uint32_t i = 0; i < container->count; i++) { if (format) { @@ -516,8 +535,8 @@ void json_print_value(json_t *json, json_value_t val, FILE *fp, int indent, bool case JSON_OBJECT: { fputc('{', fp); - if (container_index < json->container_count) { - json_container_t *container = &json->containers[container_index++]; + if (val.container < json->container_count) { + json_container_t *container = &json->containers[val.container]; for (uint32_t i = 0; i < container->count; i++) { if (format) { @@ -549,7 +568,6 @@ void json_print_value(json_t *json, json_value_t val, FILE *fp, int indent, bool // Print the entire JSON to a file void json_print(json_t *json, FILE *fp) { - // Reset the static container index in json_print_value json_print_value(json, json->root, fp, 0, true); fprintf(fp, "\n"); } @@ -722,7 +740,6 @@ void Jsonc_WriteTest( Abc_Ntk_t * p, char * pFileName ) fprintf( pFile, " }%s\n", Mio_PinReadNext(pPin) ? "," : "" ); } fprintf( pFile, " }\n" ); - //fprintf( pFile, " ]\n" ); fprintf( pFile, " }%s\n", ++Counter == Total ? "" : "," ); } Abc_NtkForEachPo( p, pObj, i ) @@ -749,6 +766,443 @@ void Jsonc_WriteTest( Abc_Ntk_t * p, char * pFileName ) Vec_PtrFree( vNodes ); } + + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static json_container_t * Jsonc_GetContainer( json_t * pJson, json_value_t value ) +{ + if ( value.container == UINT32_MAX ) + return NULL; + if ( value.container >= pJson->container_count ) + return NULL; + return pJson->containers + value.container; +} +static int Jsonc_StringEqual( json_t * pJson, json_value_t value, const char * pStr ) +{ + uint32_t Len; + if ( value.type != JSON_STRING || value.length < 2 ) + return 0; + Len = value.length - 2; + return strlen(pStr) == Len && strncmp( pJson->str + value.offset + 1, pStr, Len ) == 0; +} +static char * Jsonc_StringDup( json_t * pJson, json_value_t value ) +{ + char * pRes; + uint32_t i, Len; + if ( value.type != JSON_STRING || value.length < 2 ) + return NULL; + Len = value.length - 2; + pRes = ABC_ALLOC( char, Len + 1 ); + for ( i = 0; i < Len; i++ ) + { + char c = pJson->str[value.offset + 1 + i]; + if ( c == '\\' && i + 1 < Len ) + { + i++; + c = pJson->str[value.offset + 1 + i]; + } + pRes[i] = c; + } + pRes[Len] = '\0'; + return pRes; +} +static int Jsonc_ParseInt( json_t * pJson, json_value_t value, int * pResult ) +{ + char * pBuffer, * pEnd; + uint32_t Len; + long Temp; + if ( value.type != JSON_NUMBER ) + return 0; + Len = value.length; + pBuffer = ABC_ALLOC( char, Len + 1 ); + memcpy( pBuffer, pJson->str + value.offset, Len ); + pBuffer[Len] = '\0'; + Temp = strtol( pBuffer, &pEnd, 10 ); + ABC_FREE( pBuffer ); + if ( pEnd == NULL || pEnd == pBuffer ) + return 0; + *pResult = (int)Temp; + return 1; +} +static json_value_t * Jsonc_ObjectLookup( json_t * pJson, json_value_t object, const char * pKey ) +{ + json_container_t * pCont; + uint32_t i; + if ( object.type != JSON_OBJECT ) + return NULL; + pCont = Jsonc_GetContainer( pJson, object ); + if ( pCont == NULL ) + return NULL; + for ( i = 0; i < pCont->count; i++ ) + if ( Jsonc_StringEqual( pJson, pCont->data.pairs[i].key, pKey ) ) + return &pCont->data.pairs[i].value; + return NULL; +} +static Vec_Int_t * Jsonc_NameCounts( Vec_Ptr_t * vBases, Vec_Int_t ** pvBaseIds ) +{ + Abc_Nam_t * pNames; + Vec_Int_t * vCounts, * vBaseIds; + char * pBase; + int i, Id; + pNames = Abc_NamStart( Vec_PtrSize(vBases) + 1, 24 ); + vCounts = Vec_IntAlloc( Vec_PtrSize(vBases) + 1 ); + vBaseIds = Vec_IntAlloc( Vec_PtrSize(vBases) + 1 ); + Vec_IntFillExtra( vCounts, 1, 0 ); + Vec_PtrForEachEntry( char *, vBases, pBase, i ) + { + int fFound; + Id = Abc_NamStrFindOrAdd( pNames, pBase, &fFound ); + Vec_IntFillExtra( vCounts, Id + 1, 0 ); + Vec_IntAddToEntry( vCounts, Id, 1 ); + Vec_IntPush( vBaseIds, Id ); + } + Abc_NamDeref( pNames ); + *pvBaseIds = vBaseIds; + return vCounts; +} +static void Jsonc_AppendName( Vec_Str_t * vNames, const char * pBase, int Bit, int fUseBit ) +{ + int nSize = (int)strlen(pBase ? pBase : "") + 32; + char * pBuffer = ABC_ALLOC( char, nSize ); + if ( fUseBit ) + snprintf( pBuffer, nSize, "%s[%d]", pBase ? pBase : "", Bit ); + else + snprintf( pBuffer, nSize, "%s", pBase ? pBase : "" ); + Vec_StrPrintStr( vNames, pBuffer ); + Vec_StrPush( vNames, '\0' ); + ABC_FREE( pBuffer ); +} +static void Jsonc_AppendPortNames( Vec_Str_t * vNames, Vec_Ptr_t * vBases, Vec_Int_t * vBits ) +{ + Vec_Int_t * vCounts, * vBaseIds; + char * pBase; + int i, Bit, Count; + vCounts = Jsonc_NameCounts( vBases, &vBaseIds ); + Vec_PtrForEachEntry( char *, vBases, pBase, i ) + { + Bit = Vec_IntEntry( vBits, i ); + Count = Vec_IntEntry( vCounts, Vec_IntEntry( vBaseIds, i ) ); + Jsonc_AppendName( vNames, pBase, Bit, Bit != 0 || Count > 1 ); + } + Vec_IntFree( vCounts ); + Vec_IntFree( vBaseIds ); +} +static Vec_Int_t * Jsonc_ConvertToMiniMapping( json_t * pJson, Mio_Library_t * pLib, char ** ppDesignName ) +{ + Vec_Ptr_t * vPiBases = NULL, * vPoBases = NULL; + Vec_Int_t * vPiBits = NULL, * vPoBits = NULL; + Vec_Int_t * vNodeMap = NULL, * vGateIdx = NULL, * vPoIdx = NULL; + Vec_Int_t * vMapping = NULL, * vPoDrivers = NULL; + Vec_Str_t * vNames = NULL; + json_container_t * pNodes; + json_value_t * pTemp; + char * pBase; + int i, k, nCis = 0, nNodes = 0, nCos = 0; + int fSuccess = 0; + if ( ppDesignName ) + *ppDesignName = NULL; + if ( pLib == NULL ) + { + printf( "Genlib library is not available.\n" ); + return NULL; + } + if ( pJson->root.type != JSON_OBJECT ) + { + printf( "JSONC root should be an object.\n" ); + return NULL; + } + pTemp = Jsonc_ObjectLookup( pJson, pJson->root, "name" ); + if ( pTemp && ppDesignName && pTemp->type == JSON_STRING ) + *ppDesignName = Jsonc_StringDup( pJson, *pTemp ); + pTemp = Jsonc_ObjectLookup( pJson, pJson->root, "nodes" ); + if ( pTemp == NULL || pTemp->type != JSON_ARRAY ) + { + printf( "JSONC file is missing top-level \"nodes\" array.\n" ); + return NULL; + } + pNodes = Jsonc_GetContainer( pJson, *pTemp ); + if ( pNodes == NULL ) + { + printf( "Internal JSONC structure is incomplete.\n" ); + return NULL; + } + vNodeMap = Vec_IntStartFull( pNodes->count ); + vGateIdx = Vec_IntAlloc( pNodes->count ); + vPoIdx = Vec_IntAlloc( pNodes->count ); + vPiBases = Vec_PtrAlloc( pNodes->count ); + vPiBits = Vec_IntAlloc( pNodes->count ); + vPoBases = Vec_PtrAlloc( pNodes->count ); + vPoBits = Vec_IntAlloc( pNodes->count ); + // first pass: collect object types and names + for ( i = 0; i < (int)pNodes->count; i++ ) + { + json_value_t Node = pNodes->data.values[i]; + json_value_t * pType = Jsonc_ObjectLookup( pJson, Node, "type" ); + json_value_t * pName = Jsonc_ObjectLookup( pJson, Node, "name" ); + json_value_t * pBit = Jsonc_ObjectLookup( pJson, Node, "bit" ); + int Bit = 0; + if ( pType == NULL || pType->type != JSON_STRING ) + { + printf( "Node %d does not have a valid \"type\" field.\n", i ); + goto cleanup; + } + if ( pBit && !Jsonc_ParseInt( pJson, *pBit, &Bit ) ) + { + printf( "Node %d has an invalid \"bit\" field.\n", i ); + goto cleanup; + } + if ( Jsonc_StringEqual( pJson, *pType, "pi" ) ) + { + Vec_IntWriteEntry( vNodeMap, i, nCis++ ); + pBase = Jsonc_StringDup( pJson, pName ? *pName : *pType ); + if ( pBase == NULL ) + { + printf( "Primary input %d is missing a name.\n", i ); + goto cleanup; + } + Vec_PtrPush( vPiBases, pBase ); + Vec_IntPush( vPiBits, Bit ); + } + else if ( Jsonc_StringEqual( pJson, *pType, "instance" ) ) + { + Vec_IntPush( vGateIdx, i ); + nNodes++; + } + else if ( Jsonc_StringEqual( pJson, *pType, "PO" ) || Jsonc_StringEqual( pJson, *pType, "po" ) ) + { + Vec_IntPush( vPoIdx, i ); + nCos++; + pBase = Jsonc_StringDup( pJson, pName ? *pName : *pType ); + if ( pBase == NULL ) + { + printf( "Primary output %d is missing a name.\n", i ); + goto cleanup; + } + Vec_PtrPush( vPoBases, pBase ); + Vec_IntPush( vPoBits, Bit ); + } + else + { + printf( "Node %d has unsupported type.\n", i ); + goto cleanup; + } + } + // assign IDs to internal nodes + for ( i = 0; i < Vec_IntSize(vGateIdx); i++ ) + Vec_IntWriteEntry( vNodeMap, Vec_IntEntry(vGateIdx, i), nCis + i ); + // allocate storage for mapping + vMapping = Vec_IntAlloc( 4 + nNodes * 4 + nCos + 10 ); + vNames = Vec_StrAlloc( 1024 ); + vPoDrivers = Vec_IntAlloc( nCos ); + Vec_IntPush( vMapping, nCis ); + Vec_IntPush( vMapping, nCos ); + Vec_IntPush( vMapping, nNodes ); + Vec_IntPush( vMapping, 0 ); // flops + // second pass: build nodes and POs + for ( i = 0; i < (int)pNodes->count; i++ ) + { + json_value_t Node = pNodes->data.values[i]; + json_value_t * pType = Jsonc_ObjectLookup( pJson, Node, "type" ); + if ( Jsonc_StringEqual( pJson, *pType, "instance" ) ) + { + json_value_t * pGateName = Jsonc_ObjectLookup( pJson, Node, "name" ); + json_value_t * pFanins = Jsonc_ObjectLookup( pJson, Node, "fanins" ); + json_container_t * pFanObj; + Mio_Gate_t * pGate; + Mio_Pin_t * pPin; + char * pGateStr; + if ( pGateName == NULL || pGateName->type != JSON_STRING ) + { + printf( "Gate node %d is missing a name.\n", i ); + goto cleanup; + } + pGateStr = Jsonc_StringDup( pJson, *pGateName ); + if ( pGateStr == NULL ) + { + printf( "Gate node %d has an invalid name.\n", i ); + goto cleanup; + } + pGate = Mio_LibraryReadGateByName( pLib, pGateStr, NULL ); + if ( pGate == NULL ) + { + printf( "Gate \"%s\" is not found in the current library.\n", pGateStr ); + ABC_FREE( pGateStr ); + goto cleanup; + } + if ( pFanins == NULL || pFanins->type != JSON_OBJECT ) + { + printf( "Gate \"%s\" is missing \"fanins\" description.\n", pGateStr ); + ABC_FREE( pGateStr ); + goto cleanup; + } + pFanObj = Jsonc_GetContainer( pJson, *pFanins ); + if ( pFanObj == NULL ) + { + printf( "Gate \"%s\" has incomplete fanin information.\n", pGateStr ); + ABC_FREE( pGateStr ); + goto cleanup; + } + Vec_IntPush( vMapping, Mio_GateReadPinNum(pGate) ); + for ( pPin = Mio_GateReadPins(pGate), k = 0; pPin; pPin = Mio_PinReadNext(pPin), k++ ) + { + json_value_t * pPinInfo = Jsonc_ObjectLookup( pJson, *pFanins, Mio_PinReadName(pPin) ); + json_value_t * pNodeLit; + int NodeId, MapId; + if ( pPinInfo == NULL || pPinInfo->type != JSON_OBJECT ) + { + printf( "Gate \"%s\" is missing connection for pin \"%s\".\n", pGateStr, Mio_PinReadName(pPin) ); + ABC_FREE( pGateStr ); + goto cleanup; + } + pNodeLit = Jsonc_ObjectLookup( pJson, *pPinInfo, "node" ); + if ( pNodeLit == NULL || !Jsonc_ParseInt( pJson, *pNodeLit, &NodeId ) ) + { + printf( "Gate \"%s\" has invalid node reference on pin \"%s\".\n", pGateStr, Mio_PinReadName(pPin) ); + ABC_FREE( pGateStr ); + goto cleanup; + } + if ( NodeId < 0 || NodeId >= Vec_IntSize(vNodeMap) ) + { + printf( "Gate \"%s\" references out-of-range node %d.\n", pGateStr, NodeId ); + ABC_FREE( pGateStr ); + goto cleanup; + } + MapId = Vec_IntEntry( vNodeMap, NodeId ); + if ( MapId < 0 ) + { + printf( "Gate \"%s\" refers to unsupported node %d.\n", pGateStr, NodeId ); + ABC_FREE( pGateStr ); + goto cleanup; + } + Vec_IntPush( vMapping, MapId ); + } + Vec_StrPrintStr( vNames, pGateStr ); + Vec_StrPush( vNames, '\0' ); + ABC_FREE( pGateStr ); + } + else if ( Jsonc_StringEqual( pJson, *pType, "PO" ) || Jsonc_StringEqual( pJson, *pType, "po" ) ) + { + json_value_t * pFanin = Jsonc_ObjectLookup( pJson, Node, "fanin" ); + json_value_t * pNodeId = pFanin ? Jsonc_ObjectLookup( pJson, *pFanin, "node" ) : NULL; + int NodeId, MapId; + if ( pNodeId == NULL || !Jsonc_ParseInt( pJson, *pNodeId, &NodeId ) ) + { + printf( "Primary output %d is missing driver information.\n", Vec_IntSize(vPoDrivers) ); + goto cleanup; + } + if ( NodeId < 0 || NodeId >= Vec_IntSize(vNodeMap) ) + { + printf( "Primary output %d refers to out-of-range node %d.\n", Vec_IntSize(vPoDrivers), NodeId ); + goto cleanup; + } + MapId = Vec_IntEntry( vNodeMap, NodeId ); + if ( MapId < 0 ) + { + printf( "Primary output %d points to unsupported node %d.\n", Vec_IntSize(vPoDrivers), NodeId ); + goto cleanup; + } + Vec_IntPush( vPoDrivers, MapId ); + } + } + if ( Vec_IntSize(vPoDrivers) != nCos ) + { + printf( "JSONC file declares %d POs but %d drivers were found.\n", nCos, Vec_IntSize(vPoDrivers) ); + goto cleanup; + } + // append CO drivers + Vec_IntForEachEntry( vPoDrivers, k, i ) + Vec_IntPush( vMapping, k ); + // append gate / CI / CO names + Jsonc_AppendPortNames( vNames, vPiBases, vPiBits ); + Jsonc_AppendPortNames( vNames, vPoBases, vPoBits ); + // align to 4 bytes and append strings as ints + { + int nExtra = (4 - Vec_StrSize(vNames) % 4) % 4; + int nWords, * pBuffer; + for ( i = 0; i < nExtra; i++ ) + Vec_StrPush( vNames, '\0' ); + nWords = Vec_StrSize(vNames) / 4; + pBuffer = (int *)Vec_StrArray( vNames ); + for ( i = 0; i < nWords; i++ ) + Vec_IntPush( vMapping, pBuffer[i] ); + } + fSuccess = 1; +cleanup: + if ( fSuccess == 0 && ppDesignName && *ppDesignName ) + ABC_FREE( *ppDesignName ); + Vec_IntFreeP( &vPoDrivers ); + Vec_IntFreeP( &vNodeMap ); + Vec_IntFreeP( &vGateIdx ); + Vec_IntFreeP( &vPoIdx ); + Vec_IntFreeP( &vPiBits ); + Vec_IntFreeP( &vPoBits ); + if ( vNames ) + Vec_StrFree( vNames ); + if ( vPiBases ) + { + Vec_PtrForEachEntry( char *, vPiBases, pBase, i ) + ABC_FREE( pBase ); + Vec_PtrFree( vPiBases ); + } + if ( vPoBases ) + { + Vec_PtrForEachEntry( char *, vPoBases, pBase, i ) + ABC_FREE( pBase ); + Vec_PtrFree( vPoBases ); + } + if ( fSuccess == 0 && vMapping ) + { + Vec_IntFree( vMapping ); + vMapping = NULL; + } + return vMapping; +} +Abc_Ntk_t * Jsonc_ReadNetwork( char * pFileName ) +{ + Mio_Library_t * pLib = (Mio_Library_t *)Abc_FrameReadLibGen(); + Vec_Int_t * vMapping; + Abc_Ntk_t * pNtk; + char * pDesignName = NULL; + json_t * pJson = json_create(); + if ( pJson == NULL ) + { + printf( "Failed to allocate JSONC parser.\n" ); + return NULL; + } + if ( !json_load_file( pJson, pFileName ) ) + { + printf( "Failed to load JSONC file \"%s\".\n", pFileName ); + json_destroy( pJson ); + return NULL; + } + vMapping = Jsonc_ConvertToMiniMapping( pJson, pLib, &pDesignName ); + json_destroy( pJson ); + if ( vMapping == NULL ) + return NULL; + pNtk = Abc_NtkFromMiniMapping( Vec_IntArray(vMapping) ); + Vec_IntFree( vMapping ); + if ( pNtk == NULL ) + { + ABC_FREE( pDesignName ); + return NULL; + } + ABC_FREE( pNtk->pName ); + pNtk->pName = pDesignName ? pDesignName : Extra_FileNameGeneric( pFileName ); + ABC_FREE( pNtk->pSpec ); + pNtk->pSpec = Abc_UtilStrsav( pFileName ); + return pNtk; +} + //////////////////////////////////////////////////////////////////////// /// END OF FILE /// //////////////////////////////////////////////////////////////////////// From 84fca2c3f03d279699f450d1c2ae1ba4558e3f7d Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 17:52:20 -0800 Subject: [PATCH 18/36] Fixing misplaced declaration issue. --- src/proof/cec/cecCec.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/proof/cec/cecCec.c b/src/proof/cec/cecCec.c index e881c53be..f01df4df4 100644 --- a/src/proof/cec/cecCec.c +++ b/src/proof/cec/cecCec.c @@ -24,17 +24,16 @@ #include "misc/extra/extra.h" #include "sat/cnf/cnf.h" -void Abc_NtkVerifyPrintCex( const int * pModel, const int * pValues1, const int * pValues2, - const char * const * ppInputNames, int nInputs, const char * const * ppOutputNames, int nOutputs, - const char * pNtkName1, const char * pNtkName2 ); - ABC_NAMESPACE_IMPL_START - //////////////////////////////////////////////////////////////////////// /// DECLARATIONS /// //////////////////////////////////////////////////////////////////////// +extern void Abc_NtkVerifyPrintCex( const int * pModel, const int * pValues1, const int * pValues2, + const char * const * ppInputNames, int nInputs, const char * const * ppOutputNames, int nOutputs, + const char * pNtkName1, const char * pNtkName2 ); + //////////////////////////////////////////////////////////////////////// /// FUNCTION DEFINITIONS /// //////////////////////////////////////////////////////////////////////// From 5cdded372aed1ef6d70906d3b81fa6698e2cf555 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 19:06:29 -0800 Subject: [PATCH 19/36] Command %ufar. --- Makefile | 2 +- src/base/main/mainInit.c | 2 + src/opt/ufar/UfarCmd.cpp | 336 ++++++++ src/opt/ufar/UfarCmd.h | 23 + src/opt/ufar/UfarMgr.cpp | 1663 ++++++++++++++++++++++++++++++++++++++ src/opt/ufar/UfarMgr.h | 144 ++++ src/opt/ufar/UfarPth.cpp | 289 +++++++ src/opt/ufar/UfarPth.h | 17 + src/opt/ufar/module.make | 3 + src/opt/untk/Netlist.cpp | 63 ++ src/opt/untk/Netlist.h | 33 + src/opt/untk/NtkCmd.cpp | 12 + src/opt/untk/NtkCmd.h | 15 + src/opt/untk/NtkNtk.cpp | 1487 ++++++++++++++++++++++++++++++++++ src/opt/untk/NtkNtk.h | 150 ++++ src/opt/untk/module.make | 6 + src/opt/util/module.make | 2 + src/opt/util/util.cpp | 119 +++ src/opt/util/util.h | 85 ++ 19 files changed, 4450 insertions(+), 1 deletion(-) create mode 100755 src/opt/ufar/UfarCmd.cpp create mode 100755 src/opt/ufar/UfarCmd.h create mode 100755 src/opt/ufar/UfarMgr.cpp create mode 100755 src/opt/ufar/UfarMgr.h create mode 100755 src/opt/ufar/UfarPth.cpp create mode 100755 src/opt/ufar/UfarPth.h create mode 100755 src/opt/ufar/module.make create mode 100755 src/opt/untk/Netlist.cpp create mode 100755 src/opt/untk/Netlist.h create mode 100755 src/opt/untk/NtkCmd.cpp create mode 100755 src/opt/untk/NtkCmd.h create mode 100755 src/opt/untk/NtkNtk.cpp create mode 100755 src/opt/untk/NtkNtk.h create mode 100755 src/opt/untk/module.make create mode 100755 src/opt/util/module.make create mode 100755 src/opt/util/util.cpp create mode 100755 src/opt/util/util.h diff --git a/Makefile b/Makefile index 3e4d02719..6d4901b88 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ MODULES := \ src/misc/mem src/misc/bar src/misc/bbl src/misc/parse \ 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/opt/eslim \ + src/opt/cgt src/opt/csw src/opt/dar src/opt/dau src/opt/dsc src/opt/sfm src/opt/sbd src/opt/eslim src/opt/ufar src/opt/untk src/opt/util \ 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 \ diff --git a/src/base/main/mainInit.c b/src/base/main/mainInit.c index d3ce672f5..03c6f94c0 100644 --- a/src/base/main/mainInit.c +++ b/src/base/main/mainInit.c @@ -69,6 +69,7 @@ extern void Glucose_Init( Abc_Frame_t *pAbc ); extern void Glucose_End( Abc_Frame_t * pAbc ); extern void Glucose2_Init( Abc_Frame_t *pAbc ); extern void Glucose2_End( Abc_Frame_t * pAbc ); +extern void Ufar_Init(Abc_Frame_t *pAbc); static Abc_FrameInitializer_t* s_InitializerStart = NULL; static Abc_FrameInitializer_t* s_InitializerEnd = NULL; @@ -123,6 +124,7 @@ void Abc_FrameInit( Abc_Frame_t * pAbc ) Cba_Init( pAbc ); Pla_Init( pAbc ); Test_Init( pAbc ); + Ufar_Init( pAbc ); Glucose_Init( pAbc ); Glucose2_Init( pAbc ); for( p = s_InitializerStart ; p ; p = p->next ) diff --git a/src/opt/ufar/UfarCmd.cpp b/src/opt/ufar/UfarCmd.cpp new file mode 100755 index 000000000..33b0cc3f7 --- /dev/null +++ b/src/opt/ufar/UfarCmd.cpp @@ -0,0 +1,336 @@ +/* + * UifCmd.cpp + * + * Created on: Aug 25, 2015 + * Author: Yen-Sheng Ho + */ + +#include "base/wlc/wlc.h" +#include "opt/ufar/UfarCmd.h" +#include "opt/ufar/UfarMgr.h" +#include "opt/untk/NtkNtk.h" +#include "opt/util/util.h" + +#include +#include +#include +#include + +using namespace std; + +static UFAR::UfarManager ufar_manager; + +static int Abc_CommandTest( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandAnalyzeCex( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandCreateAbstraction( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandCreateMiter( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandProveUsingUif( Abc_Frame_t * pAbc, int argc, char ** argv ); + +static inline Wlc_Ntk_t * Wlc_AbcGetNtk( Abc_Frame_t * pAbc ) { return (Wlc_Ntk_t *)pAbc->pAbcWlc; } +static inline void Wlc_AbcFreeNtk( Abc_Frame_t * pAbc ) { if ( pAbc->pAbcWlc ) Wlc_NtkFree(Wlc_AbcGetNtk(pAbc)); } +static inline void Wlc_AbcUpdateNtk( Abc_Frame_t * pAbc, Wlc_Ntk_t * pNtk ) { Wlc_AbcFreeNtk(pAbc); pAbc->pAbcWlc = pNtk; } + + +void Ufar_Init(Abc_Frame_t *pAbc) +{ + //Cmd_CommandAdd( pAbc, "Word level Prove", "%%test", Abc_CommandTest, 0 ); + //Cmd_CommandAdd( pAbc, "Word level Prove", "%%cex", Abc_CommandAnalyzeCex, 0 ); + //Cmd_CommandAdd( pAbc, "Word level Prove", "%%abs", Abc_CommandCreateAbstraction, 0 ); + Cmd_CommandAdd( pAbc, "Word level", "%ufar", Abc_CommandProveUsingUif, 0 ); + //Cmd_CommandAdd( pAbc, "Word level Prove", "%%miter", Abc_CommandCreateMiter, 0 ); +} + +static int Abc_CommandCreateMiter( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Wlc_Ntk_t * pNew = UFAR::CreateMiter(Wlc_AbcGetNtk(pAbc), 0); + Wlc_AbcUpdateNtk(pAbc, pNew); + return 0; +} + +static int Abc_CommandCreateAbstraction( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + Wlc_Ntk_t * pNew = ufar_manager.BuildCurrentAbstraction(); + Wlc_AbcUpdateNtk(pAbc, pNew); + return 0; +} + +static int Abc_CommandAnalyzeCex( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + if ( pAbc->pCex == NULL ) + { + cout << "There is no CEX.\n"; + return 0; + } + + // ufar_manager.GetOperatorsInCex(pAbc->pCex); + ufar_manager.FindUifPairsUsingCex(pAbc->pCex); + + return 0; +} + +static int Abc_CommandProveUsingUif( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + if (Wlc_AbcGetNtk(pAbc) == NULL) { + cout << "There is no current design.\n"; + return 0; + } + + OptMgr opt_mgr(argv[0]); + opt_mgr.AddOpt("--norm", ufar_manager.params.fNorm ? "yes" : "no", "", "toggle using data type normalization"); + opt_mgr.AddOpt("--adder", "no", "", "toggle including adders"); + opt_mgr.AddOpt("--cexmin", ufar_manager.params.fCexMin ? "yes" : "no", "", "toggle using CEX minimization"); + opt_mgr.AddOpt("--sim", "none", "str", "use simulation and specify its setting"); + opt_mgr.AddOpt("-v", to_string(ufar_manager.params.iVerbosity), "num", "specify verbosity level"); + opt_mgr.AddOpt("--seq", to_string(ufar_manager.params.nSeqLookBack), "num", "specify the number of look-back frames (0 = no sequential UIF)"); + opt_mgr.AddOpt("--profile", "no", "", "dump time distribution"); + opt_mgr.AddOpt("--pba_uif", ufar_manager.params.fPbaUif ? "yes" : "no", "", "toggle using proof-based refinement for UIF pairs"); + opt_mgr.AddOpt("--lazysim", ufar_manager.params.fLazySim ? "yes" : "no", "", "toggle applying UIF pairs based on simulation"); + opt_mgr.AddOpt("--pbasim", ufar_manager.params.fPbaSim ? "yes" : "no", "", "toggle combining pba and sim"); + opt_mgr.AddOpt("--pbacex", ufar_manager.params.fPbaCex ? "yes" : "no", "", "toggle combining pba and cex"); + opt_mgr.AddOpt("--satmin", ufar_manager.params.fSatMin ? "yes" : "no", "", "toggle using sat-min in pba"); + opt_mgr.AddOpt("--cbawb", ufar_manager.params.fCbaWb ? "yes" : "no", "", "toggle using cex-based refinement for white boxing"); + opt_mgr.AddOpt("--grey", ufar_manager.params.fGrey ? "yes" : "no", "", "toggle using grey-box constraints"); + opt_mgr.AddOpt("--grey2", to_string(ufar_manager.params.nGrey), "float", "specify the greyness threshold"); + opt_mgr.AddOpt("--dump", "none", "str", "specify file name"); + opt_mgr.AddOpt("--dump-abs", "none", "str", "specify file name"); + opt_mgr.AddOpt("--par", "none", "str", "use parallel solvers"); + opt_mgr.AddOpt("--dump_states", "none", "str", "specify the name for the states file"); + opt_mgr.AddOpt("--read_states", "none", "str", "specify the name for the states file"); + opt_mgr.AddOpt("--sp", ufar_manager.params.fSuper_prove ? "yes" : "no", "", "toggle using super_prove"); + opt_mgr.AddOpt("--simp", ufar_manager.params.fSimple ? "yes" : "no", "", "toggle using simple (prove)"); + opt_mgr.AddOpt("--syn", ufar_manager.params.fSyn ? "yes" : "no", "", "toggle using simple synthesis"); + opt_mgr.AddOpt("--pth", ufar_manager.params.fPthread ? "yes" : "no", "", "toggle using pthreads"); + opt_mgr.AddOpt("--onewb", to_string(ufar_manager.params.iOneWb), "int", "specify the mode for one-white-boxing"); + opt_mgr.AddOpt("--timeout", to_string(ufar_manager.params.nTimeout), "num", "specify the timeout (sec)"); + opt_mgr.AddOpt("--exp", to_string(ufar_manager.params.iExp), "int", "specify the exp mode"); + opt_mgr.AddOpt("--miter", "yes", "", "toggle mitering the problem"); + opt_mgr.AddOpt("--under", "-1", "num", "try under-approximation with the given size"); + if(!opt_mgr.Parse(argc, argv)) { + opt_mgr.PrintUsage(); + cout << "\n This command was developed by Yen-Sheng Ho at UC Berkeley in 2015.\n"; + cout << " https://people.eecs.berkeley.edu/~alanmi/publications/2016/fmcad16_uif.pdf \n"; + return 0; + } + + if(opt_mgr["--norm"]) + ufar_manager.params.fNorm ^= 1; + if(opt_mgr["--cexmin"]) + ufar_manager.params.fCexMin ^= 1; + if(opt_mgr["--pba_uif"]) + ufar_manager.params.fPbaUif ^= 1; + if(opt_mgr["--pbasim"]) + ufar_manager.params.fPbaSim ^= 1; + if(opt_mgr["--pbacex"]) + ufar_manager.params.fPbaCex ^= 1; + if(opt_mgr["--satmin"]) + ufar_manager.params.fSatMin ^= 1; + if(opt_mgr["--cbawb"]) + ufar_manager.params.fCbaWb ^= 1; + if(opt_mgr["--grey"]) + ufar_manager.params.fGrey ^= 1; + if(opt_mgr["--grey2"]) + ufar_manager.params.nGrey = stof(opt_mgr.GetOptVal("--grey2")); + if(opt_mgr["--sp"]) + ufar_manager.params.fSuper_prove ^= 1; + if(opt_mgr["--simp"]) + ufar_manager.params.fSimple ^= 1; + if(opt_mgr["--syn"]) + ufar_manager.params.fSyn ^= 1; + if(opt_mgr["--pth"]) + ufar_manager.params.fPthread ^= 1; + if(opt_mgr["--onewb"]) + ufar_manager.params.iOneWb = stoi(opt_mgr.GetOptVal("--onewb")); + if(opt_mgr["--exp"]) + ufar_manager.params.iExp = stoi(opt_mgr.GetOptVal("--exp")); + if(opt_mgr["--par"]) + ufar_manager.params.parSetting = opt_mgr.GetOptVal("--par"); + if(opt_mgr["--sim"]) + ufar_manager.params.simSetting = opt_mgr.GetOptVal("--sim"); + if(opt_mgr["--dump_states"]) { + smatch sub_match; + string option = opt_mgr.GetOptVal("--dump_states"); + if(regex_search(option, sub_match, regex(R"(/?(\w+\.v)$)"))) + ufar_manager.params.fileStatesOut = sub_match[1].str(); + else + ufar_manager.params.fileStatesOut = opt_mgr.GetOptVal("--dump_states"); + } + if(opt_mgr["--read_states"]) + ufar_manager.params.fileStatesIn = opt_mgr.GetOptVal("--read_states"); + if(opt_mgr["--lazysim"]) + ufar_manager.params.fLazySim ^= 1; + if(opt_mgr["-v"]) + ufar_manager.params.iVerbosity = stoi(opt_mgr.GetOptVal("-v")); + if(opt_mgr["--timeout"]) + ufar_manager.params.nTimeout = stoi(opt_mgr.GetOptVal("--timeout")); + if(opt_mgr["--seq"]) + ufar_manager.params.nSeqLookBack = stoi(opt_mgr.GetOptVal("--seq")); + if(opt_mgr["--dump-abs"]) { + smatch sub_match; + string option = opt_mgr.GetOptVal("--dump-abs"); + if(regex_search(option, sub_match, regex(R"(/?(\w+)\.v$)"))) + ufar_manager.params.fileAbs = sub_match[1].str(); + else + ufar_manager.params.fileAbs = opt_mgr.GetOptVal("--dump"); + } + if(opt_mgr["--dump"]) { + smatch sub_match; + string option = opt_mgr.GetOptVal("--dump"); + if(regex_search(option, sub_match, regex(R"(/?(\w+\.v)$)"))) + ufar_manager.params.fileName = sub_match[1].str(); + else + ufar_manager.params.fileName = opt_mgr.GetOptVal("--dump"); + } + + // ufar_manager.DumpParams(); + LogT::prefix = "UIF_PROVE"; + + set set_op_types; + set_op_types.insert(WLC_OBJ_ARI_MULTI); + if (opt_mgr["--adder"]) + set_op_types.insert(WLC_OBJ_ARI_ADD); + if (!UFAR::HasOperator(Wlc_AbcGetNtk(pAbc), set_op_types)) { + cout << "There is no operator for UIF.\n"; + return 0; + } + + Wlc_Ntk_t * pNew = NULL; + timeval t1, t2; + gettimeofday(&t1, NULL); + + if (!opt_mgr["--miter"]) { + if (Wlc_NtkPoNum(Wlc_AbcGetNtk(pAbc)) != 2) { + cout << "The current design doesn't have dual outputs.\n"; + return 0; + } + pNew = UFAR::CreateMiter(Wlc_AbcGetNtk(pAbc), 0); + Wlc_AbcUpdateNtk(pAbc, pNew); + } + + if (ufar_manager.params.fNorm) { + pNew = UFAR::NormalizeDataTypes(Wlc_AbcGetNtk(pAbc), set_op_types, true); + Wlc_AbcUpdateNtk(pAbc, pNew); + } + + if (opt_mgr["--under"]) { + pNew = UFAR::MakeUnderApprox(Wlc_AbcGetNtk(pAbc), stoi(opt_mgr.GetOptVal("--under"))); + Wlc_AbcUpdateNtk(pAbc, pNew); + pNew = UFAR::MakeUnderApprox2(Wlc_AbcGetNtk(pAbc), set_op_types, stoi(opt_mgr.GetOptVal("--under"))); + Wlc_AbcUpdateNtk(pAbc, pNew); + Wlc_WriteVer(Wlc_AbcGetNtk(pAbc), "UND.v", 0, 0); + } + + Gia_Man_t * pGia = UFAR::BitBlast(Wlc_AbcGetNtk(pAbc)); + Gia_ManPrintStats( pGia, NULL ); + Gia_ManStop( pGia ); + + ufar_manager.Initialize(Wlc_AbcGetNtk(pAbc), set_op_types); + + int ret = ufar_manager.PerformUIFProve(t1); + if (ret == 1) { + LOG(0) << "Proved by UIF (UNSAT)."; + } else if (ret == 0) { + LOG(0) << "Falsified by UIF (SAT)."; + } else { + LOG(0) << "Undecided by UIF."; + } + + gettimeofday(&t2, NULL); + unsigned tTotal = elapsed_time_usec(t1, t2); + if(opt_mgr["--profile"]) { + auto log_profile = [&](const string& str, abctime t) { + LOG(1) << str << " time = " << fixed << setprecision(4) << setw(8) << (double)t/1000000 << " sec [" << setw(7) << (double)t/tTotal*100 << "%]"; + }; + log_profile("BLSolver ", ufar_manager.profile.tBLSolver); + log_profile("UifRefine ", ufar_manager.profile.tUifRefine); + log_profile("WbRefine ", ufar_manager.profile.tWbRefine); + log_profile("UifSim ", ufar_manager.profile.tUifSim); + log_profile("GbRefine ", ufar_manager.profile.tGbRefine); + } + + LOG(0) << "Time = " << setprecision(5) << ((double) (tTotal) / 1000000) << " sec"; + + return 0; +} + +static int Abc_CommandTest( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + LogT::prefix = "TEST::"; + OptMgr opt_mgr(argv[0]); + opt_mgr.AddOpt("--ntk", "none", "str", "specify the file name (*.aig) for the input network"); + opt_mgr.AddOpt("--adder", "no", "", "toggle including adders"); + if(!opt_mgr.Parse(argc, argv)) { + opt_mgr.PrintUsage(); + return 0; + } + set set_op_types; + set_op_types.insert(WLC_OBJ_ARI_MULTI); + if(opt_mgr["--adder"]) { + set_op_types.insert(WLC_OBJ_ARI_ADD); + } + Wlc_Ntk_t * pNew = UFAR::AddConstFlops(Wlc_AbcGetNtk(pAbc), set_op_types); + Wlc_AbcUpdateNtk(pAbc, pNew); + return 0; + +#if 0 + opt_mgr.AddOpt("--ntk", "none", "str", "specify the file name (*.aig) for the input network"); + opt_mgr.AddOpt("--inv", "none", "str", "specify the file name (*.pla) for the invariant"); + if(!opt_mgr.Parse(argc, argv)) { + opt_mgr.PrintUsage(); + return 0; + } + + string nameNtk = opt_mgr.GetOptVal("--ntk"); + string nameInv = opt_mgr.GetOptVal("--inv"); + UFAR::TestInvariant(nameNtk, nameInv); +#endif +#if 0 + set set_op_types; + set_op_types.insert(WLC_OBJ_ARI_MULTI); + + OptMgr opt_mgr(argv[0]); + opt_mgr.AddOpt("--cube", "no", "", "toggle using cubes for interpolants"); + if(!opt_mgr.Parse(argc, argv)) { + opt_mgr.PrintUsage(); + return 0; + } + + UFAR::TestITP(Wlc_AbcGetNtk(pAbc), set_op_types, opt_mgr["--cube"]); +#endif +#if 0 + if ( Wlc_AbcGetNtk(pAbc) == NULL ) { + cout << "There is no current design.\n"; + return 0; + } + + if (Wlc_NtkPoNum(Wlc_AbcGetNtk(pAbc)) != 2) { + cout << "The current design doesn't have dual outputs.\n"; + return 0; + } + + set set_op_types; + set_op_types.insert(WLC_OBJ_ARI_MULTI); + if (!UFAR::HasOperator(Wlc_AbcGetNtk(pAbc), set_op_types)) { + cout << "There is no operator for UIF.\n"; + return 0; + } + + Wlc_Ntk_t * pNew = NULL; + + pNew = UFAR::CreateMiter(Wlc_AbcGetNtk(pAbc), 0); + Wlc_AbcUpdateNtk(pAbc, pNew); + + pNew = UFAR::NormalizeDataTypes(Wlc_AbcGetNtk(pAbc), set_op_types, true); + Wlc_AbcUpdateNtk(pAbc, pNew); + + ufar_manager.Initialize(Wlc_AbcGetNtk(pAbc), set_op_types); + ufar_manager.DumpMgrInfo(); + + pNew = ufar_manager.BuildCurrentAbstraction(); + Wlc_AbcUpdateNtk(pAbc, pNew); + + + return 0; +#endif +} + + diff --git a/src/opt/ufar/UfarCmd.h b/src/opt/ufar/UfarCmd.h new file mode 100755 index 000000000..40bdd5cf6 --- /dev/null +++ b/src/opt/ufar/UfarCmd.h @@ -0,0 +1,23 @@ +/* + * UifCmd.h + * + * Created on: Aug 25, 2015 + * Author: Yen-Sheng Ho + */ + +#ifndef SRC_EXT2_UIF_UIFCMD_H_ +#define SRC_EXT2_UIF_UIFCMD_H_ + +#include "base/main/mainInt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void Ufar_Init(Abc_Frame_t *pAbc); + +#ifdef __cplusplus +} +#endif + +#endif /* SRC_EXT2_UIF_UIFCMD_H_ */ diff --git a/src/opt/ufar/UfarMgr.cpp b/src/opt/ufar/UfarMgr.cpp new file mode 100755 index 000000000..071cfcf9e --- /dev/null +++ b/src/opt/ufar/UfarMgr.cpp @@ -0,0 +1,1663 @@ +/* + * UifMgr.cpp + * + * Created on: Aug 25, 2015 + * Author: Yen-Sheng Ho + */ + +#include +#include + +#include "UfarMgr.h" +#include "opt/untk/NtkNtk.h" +#include "opt/util/util.h" + +#include +#include +#include + +extern "C" { + Abc_Ntk_t * Abc_NtkFromAigPhase( Aig_Man_t * pMan ); + void Wlc_NtkSimulatePrint( Wlc_Ntk_t * p, Vec_Int_t * vNodes, Vec_Ptr_t * vRes, int nWords, int nFrames ); +} + +#include + +#include +#include +#include +#include +#include +#include + +using namespace std; + +namespace UFAR { + +static inline unsigned log_level() { + return LogT::loglevel; +} + +static void split(const string &s, char delim, vector& elems) { + stringstream ss(s); + string item; + elems.clear(); + while (getline(ss, item, delim)) { + elems.push_back(std::move(item)); + } +} + +static void set_state_b(vector& vec_b_state, const string& str_state) { + if (vec_b_state.size() != str_state.size()) { + cerr << "Invalid initial states for B.\n" ; + return; + } + + for(size_t i = 0; i < str_state.size(); ++i) { + char state = str_state[i]; + if (state == '0') { + vec_b_state[i] = false; + } else if (state == '1') { + vec_b_state[i] = true; + } else if (state != '1') { + assert(false); + } + } +} + +static void set_state_p(set& set_p_state, const string& str_pair) { + vector tokens; + split(str_pair, ',', tokens); + assert(tokens.size() == 5); + set_p_state.insert(UIF_PAIR(OperatorID(stoi(tokens[0]), stoi(tokens[1])), OperatorID(stoi(tokens[2]), stoi(tokens[3])), tokens[4]=="1")); +} + +static void readCexFromVec(Gia_Man_t * pGia, const vector& vec_cex, Abc_Cex_t ** ppCex) { + int nBits = vec_cex.size(); + int nRegs = Gia_ManRegNum(pGia); + int nPis = Gia_ManPiNum(pGia); + + int iFrame = (nBits - nRegs) / nPis - 1; + assert(nBits == (nRegs + (iFrame + 1)*nPis)); + + *ppCex = Abc_CexAlloc(nRegs, nPis, iFrame + 1); + (*ppCex)->iFrame = iFrame; + (*ppCex)->iPo = 0; + for(size_t i = 0; i < vec_cex.size(); ++i) { + if (vec_cex[i] == 1 || vec_cex[i] == 0) { + if (vec_cex[i] == 1) + Abc_InfoSetBit((*ppCex)->pData, i); + } + } +} + +static Vec_Int_t * collect_boxes(const vector& vec_ids, const vector& vec_marks, bool fCollectBlack) { + Vec_Int_t * vec_ops = Vec_IntAlloc(10); + for(unsigned i = 0; i < vec_ids.size(); i++) { + int op_id = vec_ids[i]; + bool is_black = vec_marks[i]; + if((is_black && fCollectBlack) || (!is_black && !fCollectBlack)) + Vec_IntPush(vec_ops, op_id); + } + return vec_ops; +} + +static bool verify_cex_direct(Wlc_Ntk_t * pNtk, Abc_Cex_t * pCex) { + Gia_Man_t * pGia = BitBlast(pNtk); + int i; + + Gia_ManConst0(pGia)->Value = 0; + Gia_Obj_t * pObj, * pObjRi; + Gia_ManForEachRi( pGia, pObj, i ) + pObj->Value = 0; + for ( int f = 0; f <= pCex->iFrame; f++ ) { + for( i = 0; i < Gia_ManPiNum(pGia); i++ ) { + Gia_ManPi(pGia, i)->Value = Abc_InfoHasBit(pCex->pData, pCex->nRegs+pCex->nPis*f + i); + } + Gia_ManForEachRiRo( pGia, pObjRi, pObj, i ) + pObj->Value = pObjRi->Value; + Gia_ManForEachAnd( pGia, pObj, i ) + pObj->Value = Gia_ObjFanin0Copy(pObj) & Gia_ObjFanin1Copy(pObj); + Gia_ManForEachCo( pGia, pObj, i ) + pObj->Value = Gia_ObjFanin0Copy(pObj); + Gia_ManForEachPo( pGia, pObj, i ) { + if (pObj->Value==1) { + LOG(3) << "CEX is valid."; + Gia_ManStop(pGia); + return true; + } + } + } + + LOG(3) << "CEX is invalid."; + Gia_ManStop(pGia); + return false; +} + +static bool verify_cex_on_original(Wlc_Ntk_t * pOrig, Abc_Cex_t * pCex) { + Gia_Man_t * pGiaOrig = BitBlast(pOrig); + + unsigned nbits_orig_pis = compute_bit_level_pi_num(pOrig); + int nbits_ppis = pCex->nPis - Gia_ManPiNum(pGiaOrig); + LOG(3) << "VerifyCEX: #ppis = " << nbits_ppis; + + int i; + + Gia_ManConst0(pGiaOrig)->Value = 0; + Gia_Obj_t * pObj, * pObjRi; + Gia_ManForEachRi( pGiaOrig, pObj, i ) + pObj->Value = 0; + for ( int f = 0; f <= pCex->iFrame; f++ ) { + for( i = 0; i < Gia_ManPiNum(pGiaOrig); i++ ) { + if ( i < nbits_orig_pis ) { + Gia_ManPi(pGiaOrig, i)->Value = Abc_InfoHasBit(pCex->pData, pCex->nRegs+pCex->nPis*f + i); + } else { // undc PIs + Gia_ManPi(pGiaOrig, i)->Value = Abc_InfoHasBit(pCex->pData, pCex->nRegs+pCex->nPis*f + i + nbits_ppis); + } + } + Gia_ManForEachRiRo( pGiaOrig, pObjRi, pObj, i ) + pObj->Value = pObjRi->Value; + Gia_ManForEachAnd( pGiaOrig, pObj, i ) + pObj->Value = Gia_ObjFanin0Copy(pObj) & Gia_ObjFanin1Copy(pObj); + Gia_ManForEachCo( pGiaOrig, pObj, i ) + pObj->Value = Gia_ObjFanin0Copy(pObj); + Gia_ManForEachPo( pGiaOrig, pObj, i ) { + if (pObj->Value==1) { + LOG(3) << "CEX is real."; + return true; + } + } + } + + Gia_ManStop(pGiaOrig); + LOG(3) << "CEX is spurious."; + return false; +} + +static bool bitstr_not_equal(const string& str1, const string& str2) { + if (str1.size()!=str2.size()) return true; + + for(unsigned i = 0; i < str1.size(); i++) { + if (str1[i]=='1' && str2[i]=='0') + return true; + if (str1[i]=='0' && str2[i]=='1') + return true; + + if (str1[i]=='u' && str2[i]=='s') + return true; + if (str1[i]=='s' && str2[i]=='u') + return true; + } + + return false; +} + +static bool bitstr_all_x(const string& str) { + for(auto& x : str) { + if(x!='x') return false; + } + return true; +} + +static void print_blackboxes(const vector& vec_bb) { + ostringstream oss; + for(const auto& x : vec_bb) { + oss << (x ? "1" : "0"); + } + LOG(3) << oss.str(); +} + +static void print_pairs(const set& set_pairs) { + for(auto& x : set_pairs) { + LOG(3) << "(" << x.first << ", " << x.second << ")" << (x.fMark ? " [r]" : ""); + } +} + +/* +static int super_prove(Wlc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, bool fSimp, const string* pFileName = NULL) { + if(*ppCex) { + Abc_CexFree(*ppCex); + *ppCex = NULL; + } + + Gia_Man_t * pGia = BitBlast(pNtk); + + if (pFileName && !pFileName->empty()) + Gia_AigerWriteSimple(pGia, &((*pFileName + ".aig")[0u])); + + string tmpName = tmpnam(nullptr); + string tmpFileName = tmpName + ".aig"; + Gia_AigerWrite( pGia, &tmpFileName[0u], 0, 0 ); + + //string cmd = "super_prove -r /dev/stderr " + tmpFileName; + string cmd; + if (fSimp) + cmd = "simple " + tmpFileName; + else + cmd = "super_prove " + tmpFileName; + + vector vec_cex; + int result = call_python("run_sp", "verify", cmd.c_str(), vec_cex); + if(result == 1) + readCexFromVec(pGia, vec_cex, ppCex); + remove(tmpFileName.c_str()); + Gia_ManStop(pGia); + + if (result == 0) { + return 1; + } else if (result == 1) { + return 0; + } else { + return -1; + } +} +*/ + +static void get_operator_pair(Wlc_Ntk_t * p, const UIF_PAIR& uif_pair, array& wires1, array& wires2, const vector& vec_ids, const VecVecInt& vv_op_ffs) { + const UIF_PAIR& x = uif_pair; + assert(x.second.timediff == 0); + wires2[2] = vec_ids[x.second.idx]; + wires2[0] = x.fMark ? Wlc_ObjFaninId1(Wlc_NtkObj(p, vec_ids[x.second.idx])) : Wlc_ObjFaninId0(Wlc_NtkObj(p, vec_ids[x.second.idx])); + wires2[1] = x.fMark ? Wlc_ObjFaninId0(Wlc_NtkObj(p, vec_ids[x.second.idx])) : Wlc_ObjFaninId1(Wlc_NtkObj(p, vec_ids[x.second.idx])); + + if (x.first.timediff == 0) { + wires1[2] = vec_ids[x.first.idx]; + wires1[0] = Wlc_ObjFaninId0(Wlc_NtkObj(p, vec_ids[x.first.idx])); + wires1[1] = Wlc_ObjFaninId1(Wlc_NtkObj(p, vec_ids[x.first.idx])); + } else { + int ci_idx = vv_op_ffs[x.first.idx][0 - x.first.timediff - 1]; + wires1[2] = Wlc_ObjId(p, Wlc_NtkCi(p, ci_idx)); + wires1[1] = Wlc_ObjId(p, Wlc_NtkCi(p, ci_idx - 1)); + wires1[0] = Wlc_ObjId(p, Wlc_NtkCi(p, ci_idx - 2)); + } +} + +static Gia_Man_t * unroll_with_cex(Wlc_Ntk_t * pChoice, Abc_Cex_t * pCex, int nbits_old_pis, int num_sel_pis, int * p_num_ppis, bool sel_pi_first) { + Gia_Man_t * pGiaChoice = BitBlast( pChoice ); + + int nbits_new_pis = compute_bit_level_pi_num(pChoice); + int num_ppis = nbits_new_pis - nbits_old_pis - num_sel_pis; + *p_num_ppis = num_ppis; + int num_undc_pis = Gia_ManPiNum(pGiaChoice) - nbits_new_pis; + LOG(3) << "#orig_pis = " << nbits_old_pis << ", #ppis = " << num_ppis << ", #sel_pis = " << num_sel_pis << ", #undc_pis = " << num_undc_pis; + assert(Gia_ManPiNum(pGiaChoice)==nbits_old_pis+num_ppis+num_sel_pis+num_undc_pis); + assert(Gia_ManPiNum(pGiaChoice)==pCex->nPis+num_sel_pis); + + Gia_Man_t * pFrames = NULL; + + Gia_Obj_t * pObj, * pObjRi; + int f, i; + pFrames = Gia_ManStart( 10000 ); + pFrames->pName = Abc_UtilStrsav( pGiaChoice->pName ); + Gia_ManHashAlloc( pFrames ); + Gia_ManConst0(pGiaChoice)->Value = 0; + Gia_ManForEachRi( pGiaChoice, pObj, i ) + pObj->Value = 0; + + auto is_sel_pi = [=](int iPi) { + return (sel_pi_first ? iPi < nbits_old_pis + num_sel_pis : iPi >= nbits_old_pis + num_ppis); + }; + + for ( f = 0; f <= pCex->iFrame; f++ ) { + for( i = 0; i < Gia_ManPiNum(pGiaChoice); i++ ) { + if ( i >= nbits_old_pis && i < nbits_old_pis + num_ppis + num_sel_pis ) { + if(f == 0 || !is_sel_pi(i)) + Gia_ManPi(pGiaChoice, i)->Value = Gia_ManAppendCi(pFrames); + } else if (i < nbits_old_pis) { + Gia_ManPi(pGiaChoice, i)->Value = Abc_InfoHasBit(pCex->pData, pCex->nRegs+pCex->nPis*f + i); + } else if (i >= nbits_old_pis + num_ppis + num_sel_pis) { + Gia_ManPi(pGiaChoice, i)->Value = Abc_InfoHasBit(pCex->pData, pCex->nRegs+pCex->nPis*f + i - num_sel_pis); + } + } + Gia_ManForEachRiRo( pGiaChoice, pObjRi, pObj, i ) + pObj->Value = pObjRi->Value; + Gia_ManForEachAnd( pGiaChoice, pObj, i ) + pObj->Value = Gia_ManHashAnd(pFrames, Gia_ObjFanin0Copy(pObj), Gia_ObjFanin1Copy(pObj)); + Gia_ManForEachCo( pGiaChoice, pObj, i ) + pObj->Value = Gia_ObjFanin0Copy(pObj); + Gia_ManForEachPo( pGiaChoice, pObj, i ) + Gia_ManAppendCo(pFrames, pObj->Value); + } + Gia_ManHashStop (pFrames); + Gia_ManSetRegNum(pFrames, 0); + Gia_Man_t * pGia; + pFrames = Gia_ManCleanup(pGia = pFrames); + Gia_ManStop(pGia); + Gia_ManStop(pGiaChoice); + + return pFrames; +} + +static void compute_core_using_sat(Gia_Man_t * pFrames, vector& vec_cores, int nFrames, int num_sel_pis, int num_other_pis, bool sel_pi_first, VecVecInt* ppi_model = NULL, bool fSatMin = false, int nConfLimit = 0) { + Aig_Man_t * pAigFrames = Gia_ManToAigSimple( pFrames ); + Cnf_Dat_t * pCnf = Cnf_Derive(pAigFrames, Aig_ManCoNum(pAigFrames)); + sat_solver * pSat = sat_solver_new(); + sat_solver_setnvars(pSat, pCnf->nVars); + for (unsigned i = 0; i < pCnf->nClauses; i++) { + if (!sat_solver_addclause(pSat, pCnf->pClauses[i], pCnf->pClauses[i + 1])) + assert(false); + } + { + Vec_Int_t* vLits = Vec_IntAlloc(100); + Aig_Obj_t* pObj; + int i; + Aig_ManForEachCo( pAigFrames, pObj, i ) + { + assert(pCnf->pVarNums[pObj->Id] >= 0); + Vec_IntPush(vLits, toLitCond(pCnf->pVarNums[pObj->Id], 0)); + } + int ret = sat_solver_addclause(pSat, Vec_IntArray(vLits), Vec_IntArray(vLits) + Vec_IntSize(vLits)); + if (!ret) { + LOG(0) << "UNSAT after adding PO clause."; + } + + Vec_IntFree(vLits); + } + { + Vec_Int_t* vLits = Vec_IntAlloc(100); + map varMap; + int first_sel_pi = sel_pi_first ? 0 : num_other_pis; + for (int i = 0; i < num_sel_pis; i++) { + //for (int i = num_sel_pis - 1; i >= 0; --i) { + int cur_pi = first_sel_pi + i; + int var = pCnf->pVarNums[Aig_ManCi(pAigFrames, cur_pi)->Id]; + assert(var >= 0); + varMap[var] = i; + Vec_IntPush(vLits, toLitCond(var, 0)); + } + if(log_level() >= 3) { + int i, Entry; + ostringstream oss; + oss << "#vLits = " << Vec_IntSize(vLits) << "; vLits = "; + Vec_IntForEachEntry(vLits, Entry, i) + oss << Entry << " "; + LOG(3) << oss.str(); + } + int status = sat_solver_solve(pSat, Vec_IntArray(vLits), Vec_IntArray(vLits) + Vec_IntSize(vLits), (ABC_INT64_T)(nConfLimit), (ABC_INT64_T)(0), (ABC_INT64_T)(0), (ABC_INT64_T)(0)); + if (status == l_False) { + LOG(3) << "UNSAT."; + int nCoreLits, *pCoreLits; + nCoreLits = sat_solver_final(pSat, &pCoreLits); + + vector vec_cores_marks (Vec_IntSize(vLits),false); + for (int i = 0; i < nCoreLits; i++) { + vec_cores_marks[varMap[lit_var(pCoreLits[i])]] = true; + } + + // SAT-min + if (fSatMin && nCoreLits > 1) { + for (int i = 0; i < vec_cores_marks.size(); i++) { + if (!vec_cores_marks[i]) + Vec_IntWriteEntry(vLits, i, lit_neg(Vec_IntEntry(vLits, i))); + } + + for (int i = 0; i < vec_cores_marks.size(); i++) { + if (!vec_cores_marks[i]) + continue; + + Vec_IntWriteEntry(vLits, i, lit_neg(Vec_IntEntry(vLits, i))); + int status = sat_solver_solve(pSat, Vec_IntArray(vLits), Vec_IntArray(vLits) + Vec_IntSize(vLits), (ABC_INT64_T) (5000), (ABC_INT64_T) (0), (ABC_INT64_T) (0), (ABC_INT64_T) (0)); + + if (status == l_False) { + LOG(4) << "Loop[" << i << "] : Drop 1 literal = " << Vec_IntEntry(vLits, i); + vec_cores_marks[i] = false; + } else { + if (status == l_True) + LOG(4) << "Loop[" << i << "] : SAT"; + else + LOG(4) << "Loop[" << i << "] : Unknown"; + Vec_IntWriteEntry(vLits, i, lit_neg(Vec_IntEntry(vLits, i))); + } + } + LOG(2) << "SAT-MIN : " << nCoreLits << " ==> " << count(vec_cores_marks.begin(), vec_cores_marks.end(), true); + } + + for (int i = 0; i < vec_cores_marks.size(); i++) { + if(vec_cores_marks[i]) + vec_cores.push_back(i); + } + } else if (status == l_True) { + LOG(3) << "SAT."; + + if (ppi_model && sel_pi_first) { + ppi_model->clear(); + for (int f = 0; f < nFrames; f++) { + ppi_model->push_back(vector()); + for (int i = 0; i < num_other_pis; i++) { + int cur_pi = num_sel_pis + f * num_other_pis + i; + int var = pCnf->pVarNums[Aig_ManCi(pAigFrames, cur_pi)->Id]; + ppi_model->back().push_back(sat_solver_var_value(pSat, var)); + } + } + } + } else { + LOG(3) << "UNKNOWN."; + } + + Vec_IntFree(vLits); + } + Cnf_ManFree(); + sat_solver_delete(pSat); + Aig_ManStop(pAigFrames); +} + +std::ostream& operator<<(ostream& os, const OperatorID& obj) { + os << obj.idx << " @ " << obj.timediff; + return os; +} + +class SimUifPairFinder { + public: + SimUifPairFinder(Wlc_Ntk_t * pOrig, const vector* p_vec_ids) : + nWords(1), + nFrames(1), + nThreshold(0), + _pOrigNtk(pOrig), + _p_vec_op_ids(p_vec_ids) + {} + + void Simulate() { + _compute_max_bw(); + _simulate_and_count(); + } + void ComputeUifPairs() { + _insert_sim_pairs(); + } + void SetParams(const string& setting); + + bool HasResults() const {return !_mat_pairwise_matches.empty();} + int GetCount(const UIF_PAIR& x) const; + bool IsGood(const UIF_PAIR& x) const {return (GetCount(x) >= nThreshold);} + void AddPair(const UIF_PAIR& x); + + const set* GetSimPairsPtr() { return &_set_sim_pairs; } + + int nWords; + int nFrames; + int nThreshold; + private: + void _compute_max_bw() { ComputeMaxBW(_pOrigNtk, *_p_vec_op_ids, _max_input_bw, _max_output_bw); } + void _simulate_and_count(); + void _insert_sim_pairs(); + void _count_matches(Vec_Ptr_t * vRes, int range); + Vec_Int_t * _collect_sim_nodes(Wlc_Ntk_t * pNtk); + + Wlc_Ntk_t * _pOrigNtk; + const vector* _p_vec_op_ids; + + set _set_sim_pairs; + + int _max_input_bw {-1}; + int _max_output_bw {-1}; + + VecVecInt _mat_pairwise_matches; +}; + +void SimUifPairFinder::AddPair(const UIF_PAIR& x) { + assert(x.first.timediff == 0 && x.second.timediff == 0); + assert(x.first.idx < x.second.idx); + + if(x.fMark) + _mat_pairwise_matches[x.second.idx][x.first.idx] += nThreshold; + else + _mat_pairwise_matches[x.first.idx][x.second.idx] += nThreshold; +} +int SimUifPairFinder::GetCount(const UIF_PAIR& x) const { + assert(x.first.timediff == 0 && x.second.timediff == 0); + assert(x.first.idx < x.second.idx); + + return (x.fMark ? _mat_pairwise_matches[x.second.idx][x.first.idx] : _mat_pairwise_matches[x.first.idx][x.second.idx]); +} + +void SimUifPairFinder::SetParams(const string& setting) { + int temp = 0; + smatch sub_match; + + if(regex_search(setting, sub_match, regex(R"(w:(\d+))"))) + nWords = stoi(sub_match[1].str()); + + if(regex_search(setting, sub_match, regex(R"(f:(\d+))"))) + nFrames = stoi(sub_match[1].str()); + + if(regex_search(setting, sub_match, regex(R"(t:(\d+))"))) + temp = stoi(sub_match[1].str()); + + nThreshold = temp * nWords * nFrames * 64 / 100; +} + +Vec_Int_t * SimUifPairFinder::_collect_sim_nodes(Wlc_Ntk_t * pNtk) { + Vec_Int_t * vNodes = Vec_IntAlloc( _p_vec_op_ids->size() * 2 ); + int i; + Wlc_Obj_t * pPo; + int range = Wlc_ObjRange(Wlc_NtkPo(pNtk, 1)); + Wlc_NtkForEachPo(pNtk, pPo, i) { + if ((i%3)==0) continue; + assert(range == Wlc_ObjRange(pPo)); + Vec_IntPush(vNodes, Wlc_ObjId(pNtk, pPo)); + } + assert(Vec_IntSize(vNodes) == _p_vec_op_ids->size() * 2); + + return vNodes; +} + +static unsigned bit_count (word value) { + unsigned count = 0; + while (value > 0) { // until all bits are zero + if ((value & 1) == 1) // check lower bit + count++; + value >>= 1; // shift bits, removing lower bit + } + return count; +} + +static void bit_print (word value) { + int n_bits = 0; + while(n_bits < 64) { + if ((value & 1) == 1) + cout << "1"; + else + cout << "0"; + value >>= 1; + n_bits++; + } + cout << endl; +} + +void SimUifPairFinder::_count_matches(Vec_Ptr_t * vRes, int range) { + VecVecInt& mat = _mat_pairwise_matches; + int num_ops = _p_vec_op_ids->size(); + mat = VecVecInt(num_ops, vector(num_ops, 0)); + array op1, op2; + + for(int i = 0; i < num_ops; i++) { + for(int j = 0; j < num_ops; j++) { + if (i == j) continue; + + for(int f = 0; f < nFrames; f++) { + vector merges(nWords, ~0); + + op1[0] = (i < j) ? (i << 1) : (i << 1) + 1; + op1[1] = (i < j) ? (i << 1) + 1 : (i << 1); + op2[0] = (j << 1); + op2[1] = (j << 1) + 1; + + for (int k = 0; k < range; k++) { + for (int w = 0; w < nWords; w++) { + word w1, w2; + w1 = ((word*) Vec_VecEntryEntry((Vec_Vec_t *) vRes, op1[0], k))[f * nWords + w]; + w2 = ((word*) Vec_VecEntryEntry((Vec_Vec_t *) vRes, op2[0], k))[f * nWords + w]; + merges[w] = merges[w] & (~(w1 ^ w2)); + + w1 = ((word*) Vec_VecEntryEntry((Vec_Vec_t *) vRes, op1[1], k))[f * nWords + w]; + w2 = ((word*) Vec_VecEntryEntry((Vec_Vec_t *) vRes, op2[1], k))[f * nWords + w]; + merges[w] = merges[w] & (~(w1 ^ w2)); + } + } + + for (int w = 0; w < nWords; w++) { + mat[i][j] += bit_count(merges[w]); + } + } + } + } + + /* + for(int i = 0; i < num_ops; ++i) { + for(int j = 0; j < num_ops; ++j) { + if(i == j) continue; + if(mat[i][j] == 0) continue; + cout << "[" << i << "][" << j << "] = " << mat[i][j] << endl; + } + } + */ +} + +void SimUifPairFinder::_simulate_and_count() { + vector vec_ids = *_p_vec_op_ids; + Wlc_Ntk_t * pSimNtk = AddAuxPOsForOperators(_pOrigNtk, vec_ids, _max_input_bw, _max_output_bw); + Vec_Int_t * vNodes = _collect_sim_nodes(pSimNtk); + Vec_Ptr_t * vRes; + vRes = Wlc_NtkSimulate( pSimNtk, vNodes, nWords, nFrames ); + // Wlc_NtkSimulatePrint(pSimNtk, vNodes, vRes, nWords, nFrames); + _count_matches(vRes, Wlc_ObjRange(Wlc_NtkObj(pSimNtk, Vec_IntEntry(vNodes, 0)))); + + Wlc_NtkFree(pSimNtk); + Vec_IntFree(vNodes); + Wlc_NtkDeleteSim(vRes); +} + +void SimUifPairFinder::_insert_sim_pairs() { + VecVecInt& mat = _mat_pairwise_matches; + _set_sim_pairs.clear(); + + int num_ops = mat.size(); + + for(int i = 0; i < num_ops; ++i) { + for(int j = 0; j < num_ops; ++j) { + if(i == j) continue; + if(mat[i][j] < nThreshold) continue; + + if (i < j) + _set_sim_pairs.insert(UIF_PAIR(OperatorID(i), OperatorID(j), 0)); + else + _set_sim_pairs.insert(UIF_PAIR(OperatorID(j), OperatorID(i), 1)); + } + } +} + +class CexUifPairFinder { + public: + CexUifPairFinder(Wlc_Ntk_t * pOrigNtk, const vector* p_vec_op_ids) : + _pOrigNtk(pOrigNtk), + _p_vec_op_ids(p_vec_op_ids) + {} + + void SetStates(Abc_Cex_t * pCex, const vector* p_vec_op_marks, const set* p_set_uif_pairs, set* p_set_prev_ops, const UfarManager::Params* p_params) { + _pCex = pCex; + _p_vec_op_blackbox_marks = p_vec_op_marks; + _p_set_uif_pairs = p_set_uif_pairs; + _p_set_prev_ops = p_set_prev_ops; + _p_params = p_params; + } + void FindUifPairs(const VecVecChar& cex_po_values, unsigned nLookBack, set& set_new_pairs); + void FindUifPairsBasic(const VecVecChar& cex_po_values, unsigned nLookBack, set& set_new_pairs); + void ComputeCoreUifPairs(const set& set_candidates, set& set_core, Abc_Cex_t ** ppCex); + const VecVecStr* GetOutputs() const {return &_outputs;} + private: + void _compute_max_bw(); + string _get_bitstr(Wlc_Obj_t * pObj, const VecChar& cur_pos, unsigned& pos, bool isOutput); + + Wlc_Ntk_t * _introduce_choices(const set& set_candidates, vector& vec_choice2pair); + Wlc_Ntk_t * _apply_uif_pairs_with_choices(Wlc_Ntk_t * pNtk, const VecVecInt& vv_op_ffs, vector& vec_ids, const set& set_candidates, vector& vec_choice2pair); + + Wlc_Ntk_t * _pOrigNtk; + const vector* _p_vec_op_ids; + + Abc_Cex_t * _pCex {NULL}; + const vector* _p_vec_op_blackbox_marks {NULL}; + const set* _p_set_uif_pairs {NULL}; + set* _p_set_prev_ops {NULL}; + const UfarManager::Params* _p_params {NULL}; + + int _max_input_bw {-1}; + int _max_output_bw {-1}; + + VecVecStr _outputs; + VecVecStr _inputs; +}; + +void CexUifPairFinder::_compute_max_bw() { + // compute max bit-width + ComputeMaxBW(_pOrigNtk, *_p_vec_op_ids, _max_input_bw, _max_output_bw); + LOG(3) << "maxIn = " << _max_input_bw << "; maxOut = " << _max_output_bw; +} + +string CexUifPairFinder::_get_bitstr(Wlc_Obj_t * pObj, const VecChar& cur_pos, unsigned& pos, bool isOutput) { + int range = Wlc_ObjRange(pObj); + int max_bw = isOutput ? _max_output_bw : _max_input_bw; + string bitstr = string(cur_pos.begin() + pos, cur_pos.begin() + pos + range).append(max_bw - range, *(cur_pos.begin() + pos + range - 1)); + pos += range; + + return bitstr; +} + +Wlc_Ntk_t * CexUifPairFinder::_apply_uif_pairs_with_choices(Wlc_Ntk_t * pNtk, const VecVecInt& vv_op_ffs, vector& vec_ids, const set& set_candidates, vector& vec_choice2pair) { + array wires1; + array wires2; + + Wlc_Ntk_t * p = DupNtkAndUpdateIDs(pNtk, vec_ids); + Vec_Int_t * vUifConstrs = Vec_IntAlloc( 100 ); + Vec_Int_t * vFanins = Vec_IntAlloc( 2 ); + + for(auto& x : *_p_set_uif_pairs) { + get_operator_pair(p, x, wires1, wires2, vec_ids, vv_op_ffs); + int iObj = AddOneUifImplication(p, wires1, wires2); + Vec_IntPush(vUifConstrs, iObj); + } + + for(auto& x : set_candidates) { + vec_choice2pair.push_back(x); + get_operator_pair(p, x, wires1, wires2, vec_ids, vv_op_ffs); + int iObj = AddOneUifImplication(p, wires1, wires2); + int iSelPi = Wlc_ObjAlloc( p, WLC_OBJ_PI, 0, 0, 0); + Vec_IntFill(vFanins, 1, iSelPi); + int iSelPiNeg = Wlc_ObjCreate( p, WLC_OBJ_LOGIC_NOT, 0, 0, 0, vFanins); + Vec_IntFillTwo(vFanins, 2, iObj, iSelPiNeg); + int iObjNew = Wlc_ObjCreate( p, WLC_OBJ_LOGIC_OR, 0, 0, 0, vFanins); + Vec_IntPush(vUifConstrs, iObjNew); + } + + if (Wlc_NtkFfNum(_pOrigNtk) == 0) + FoldCombConstraints(p, vUifConstrs); + else + FoldSeqConstraints(p, vUifConstrs); + + Wlc_Ntk_t * pNew = DupNtkAndUpdateIDs(p, vec_ids); + + Wlc_NtkFree(p); + Vec_IntFree(vUifConstrs); + Vec_IntFree(vFanins); + + return pNew; +} + +Wlc_Ntk_t * CexUifPairFinder::_introduce_choices(const set& set_candidates, vector& vec_choice2pair) { + vector vec_ids = *_p_vec_op_ids; + VecVecInt vv_op_ffs; + Wlc_Ntk_t * pNew, * pTemp; + + if(!_p_set_prev_ops->empty()) + pNew = IntroducePrevOperators(_pOrigNtk, vec_ids, *_p_set_prev_ops, vv_op_ffs); + else + pNew = DupNtkAndUpdateIDs(_pOrigNtk, vec_ids); + + if(_p_params->fGrey) { + pNew = ApplyGreyConstraints(pTemp = pNew, vec_ids, Wlc_NtkFfNum(_pOrigNtk) > 0); + Wlc_NtkFree(pTemp); + } + + pNew = _apply_uif_pairs_with_choices(pTemp = pNew, vv_op_ffs, vec_ids, set_candidates, vec_choice2pair); + Wlc_NtkFree(pTemp); + + Vec_Int_t * vec_ops = collect_boxes(vec_ids, *_p_vec_op_blackbox_marks, true /*black*/); + assert(Vec_IntSize(vec_ops) > 0); + pNew = AbstractNodes(pTemp = pNew, vec_ops); + Wlc_NtkFree(pTemp); + + return pNew; +} + + +static Abc_Cex_t * get_new_cex_with_ppi(Abc_Cex_t * pOrigCex, const VecVecInt& ppi_model, int num_orig_pis, int num_ppis, bool one_more_flop) { + Abc_Cex_t * pCex = Abc_CexAlloc( one_more_flop ? pOrigCex->nRegs + 1 : pOrigCex->nRegs, pOrigCex->nPis, pOrigCex->iFrame + 1); + pCex->iFrame = pOrigCex->iFrame; + pCex->iPo = 0; + + for(int f = 0; f < pOrigCex->iFrame + 1; f++) { + for(int i = 0; i < pOrigCex->nPis; i++) { + int iOrigBit = pOrigCex->nRegs+pOrigCex->nPis*f + i; + int iBit = one_more_flop ? iOrigBit + 1 : iOrigBit; + if ( i >= num_orig_pis && i < num_orig_pis + num_ppis ) { + if(ppi_model[f][i-num_orig_pis]) { + Abc_InfoSetBit(pCex->pData, iBit); + } + } else { // i < num_orig_pis || i >= num_orig_pis + num_ppis + if(Abc_InfoHasBit(pOrigCex->pData, iOrigBit)) { + Abc_InfoSetBit(pCex->pData, iBit); + } + } + } + } + + return pCex; +} + +void CexUifPairFinder::ComputeCoreUifPairs(const set& set_candidates, set& set_core, Abc_Cex_t ** ppCex) { + vector vec_choice2pair; + Wlc_Ntk_t * pChoice = _introduce_choices(set_candidates, vec_choice2pair); + assert(set_candidates.size() == vec_choice2pair.size()); + + int num_ppis; + int num_orig_pis = compute_bit_level_pi_num(_pOrigNtk); + Gia_Man_t * pGiaFrames = unroll_with_cex(pChoice, _pCex, num_orig_pis, vec_choice2pair.size(), &num_ppis, true); + + vector vec_cores; + VecVecInt ppi_model; + compute_core_using_sat(pGiaFrames, vec_cores, _pCex->iFrame+1, vec_choice2pair.size(), num_ppis, true, &ppi_model, false, 5000); + if(ppCex && !ppi_model.empty()) { + if(*ppCex) Abc_CexFree(*ppCex); + *ppCex = get_new_cex_with_ppi(_pCex, ppi_model, num_orig_pis, num_ppis, _p_set_uif_pairs->empty() && Wlc_NtkFfNum(_pOrigNtk)); + } + // Abc_CexPrintStats(_pCex); + // Abc_CexPrintStats(*ppCex); + + set_core.clear(); + for(auto& x : vec_cores) { + set_core.insert(vec_choice2pair[x]); + } + + Gia_ManStop(pGiaFrames); + Wlc_NtkFree(pChoice); +} + +void CexUifPairFinder::FindUifPairsBasic(const VecVecChar& cex_po_values, unsigned nLookBack, set& set_new_pairs) { + assert(Wlc_NtkPoNum(_pOrigNtk) == 1); + assert(Wlc_ObjRange(Wlc_NtkPo(_pOrigNtk, 0)) == 1); + + _inputs.clear(); + _outputs.clear(); + + for (unsigned f = 0; f < cex_po_values.size(); f++) { + _inputs.push_back(vector()); + _outputs.push_back(vector()); + + const vector& cur_po_values = cex_po_values[f]; + unsigned pos = 1; + + for (unsigned i = 0; i < _p_vec_op_ids->size(); i++) { + int op_id = _p_vec_op_ids->at(i); + Wlc_Obj_t * pObj = Wlc_NtkObj(_pOrigNtk, op_id); + + Wlc_Obj_t * pObjInput = NULL; + + pObjInput = Wlc_ObjFanin0(_pOrigNtk, pObj); + _max_input_bw = Wlc_ObjRange(pObjInput); + string bits_input0 = _get_bitstr(pObjInput, cur_po_values, pos, false); + bits_input0 += (Wlc_ObjIsSigned(pObjInput) ? 's' : 'u'); + + pObjInput = Wlc_ObjFanin1(_pOrigNtk, pObj); + _max_input_bw = Wlc_ObjRange(pObjInput); + string bits_input1 = _get_bitstr(pObjInput, cur_po_values, pos, false); + bits_input1 += (Wlc_ObjIsSigned(pObjInput) ? 's' : 'u'); + + _max_output_bw = Wlc_ObjRange(pObj); + string bits_output = _get_bitstr(pObj, cur_po_values, pos, true); + bits_output += (Wlc_ObjIsSigned(pObj) ? 's' : 'u'); + _outputs.back().push_back(bits_output); + + vector keys; + keys.push_back(bits_input0 + ':' + bits_input1); + keys.push_back(bits_input1 + ':' + bits_input0); + _inputs.back().push_back(keys[0]); + + + for (unsigned reversed = 0; reversed < keys.size(); reversed++) { + const string& key = keys[reversed]; + for (int g = f; g >= 0 && g >= (int)f - (int)nLookBack; g--) { + for (int j = 0; j < _inputs[g].size(); j++) { + if (!bitstr_not_equal(key, _inputs[g][j]) && bitstr_not_equal(_outputs[f][i], _outputs[g][j])) { + UIF_PAIR pair_int(OperatorID(j, g-f), OperatorID(i), reversed); + if (_p_set_uif_pairs->count(pair_int) == 0) + set_new_pairs.insert(pair_int); + if(g < f) + _p_set_prev_ops->insert(OperatorID(j, g-f)); + } + } + } + } + LOG(4) << "[" << f << "][" << i << "] : " << "key = " << keys[0] << " output = " << _outputs[f][i] ; + } + } +} + +void CexUifPairFinder::FindUifPairs(const VecVecChar& cex_po_values, unsigned nLookBack, set& set_new_pairs) { + assert(Wlc_NtkPoNum(_pOrigNtk) == 1); + assert(Wlc_ObjRange(Wlc_NtkPo(_pOrigNtk, 0)) == 1); + + _compute_max_bw(); + + _inputs.clear(); + _outputs.clear(); + + for (unsigned f = 0; f < cex_po_values.size(); f++) { + + _inputs.push_back(vector()); + _outputs.push_back(vector()); + + const vector& cur_po_values = cex_po_values[f]; + unsigned pos = 1; + + for (unsigned i = 0; i < _p_vec_op_ids->size(); i++) { + int op_id = _p_vec_op_ids->at(i); + Wlc_Obj_t * pObj = Wlc_NtkObj(_pOrigNtk, op_id); + + string bits_input0 = _get_bitstr(Wlc_ObjFanin0(_pOrigNtk, pObj), cur_po_values, pos, false); + string bits_input1 = _get_bitstr(Wlc_ObjFanin1(_pOrigNtk, pObj), cur_po_values, pos, false); + assert(bits_input0.size() == bits_input1.size()); + + string bits_output = _get_bitstr(pObj, cur_po_values, pos, true); + _outputs.back().push_back(bits_output); + + vector keys; + keys.push_back(bits_input0 + ':' + bits_input1); + keys.push_back(bits_input1 + ':' + bits_input0); + _inputs.back().push_back(keys[0]); + + for (unsigned reversed = 0; reversed < keys.size(); reversed++) { + const string& key = keys[reversed]; + for (int g = f; g >= 0 && g >= (int)f - (int)nLookBack; g--) { + for (int j = 0; j < _inputs[g].size(); j++) { + if (!bitstr_not_equal(key, _inputs[g][j]) && bitstr_not_equal(_outputs[f][i], _outputs[g][j])) { + UIF_PAIR pair_int(OperatorID(j, g-f), OperatorID(i), reversed); + if (_p_set_uif_pairs->count(pair_int) == 0) + set_new_pairs.insert(pair_int); + if(g < f) + _p_set_prev_ops->insert(OperatorID(j, g-f)); + } + } + } + } + LOG(4) << "[" << f << "][" << i << "] : " << "key = " << keys[0] << " output = " << _outputs[f][i] ; + } + } + +} + +UfarManager::Params::Params() : + fCexMin(true), + fPbaUif(false), + fLazySim(true), + fPbaSim(false), + fPbaCex(false), + fSatMin(false), + fCbaWb(false), + fGrey(false), + nGrey(0), + fNorm(true), + fSuper_prove(false), + fSimple(false), + fSyn(false), + fPthread(false), + iOneWb(-1), + iExp(-1), + nConstraintLimit(65536), + iVerbosity(0), + nSeqLookBack(0), + nTimeout(65536) + {} +UfarManager::UfarManager() : _pOrigNtk(NULL), _pAbsWithAuxPos(NULL) {} + +UfarManager::~UfarManager() { + if (_pOrigNtk) Wlc_NtkFree(_pOrigNtk); +} + +void UfarManager::Initialize(Wlc_Ntk_t * pNtk, const set& types) { + if (_pOrigNtk) Wlc_NtkFree(_pOrigNtk); + _vec_orig_names.clear(); + _vec_op_ids.clear(); + _vec_op_blackbox_marks.clear(); + _vec_op_greyness.clear(); + _set_uif_pairs.clear(); + _set_uif_sim_pairs.clear(); + + _vec_vec_op_ffs.clear(); + _set_prev_ops.clear(); + + profile = Profile(); + + _pOrigNtk = Wlc_NtkDupDfsSimple(pNtk); + + Wlc_Obj_t * pObj; + int i; + _vec_orig_names.resize(Wlc_NtkObjNumMax(pNtk)); + Wlc_NtkForEachObj(pNtk, pObj, i) { + _vec_orig_names[i] = (Wlc_ObjName(pNtk, i)); + + if(types.count(pObj->Type) > 0) { + _vec_op_ids.push_back(i); + if(params.nGrey) { + _vec_op_greyness.push_back(Greyness(pNtk, pObj)); + } + } + } + + _vec_op_blackbox_marks.resize(_vec_op_ids.size(), true); + + _p_sim_mgr.reset(new SimUifPairFinder(_pOrigNtk, &_vec_op_ids)); + _p_cex_mgr.reset(new CexUifPairFinder(_pOrigNtk, &_vec_op_ids)); + + LogT::loglevel = params.iVerbosity; + + struct timeval now; + gettimeofday(&now, NULL); + _timeout.tv_sec = now.tv_sec + params.nTimeout; + _timeout.tv_nsec = now.tv_usec * 1000; +} + +Wlc_Ntk_t * UfarManager::ApplyUifConstraints(Wlc_Ntk_t * pNtk, vector& vec_ids) { + array wires1; + array wires2; + + Wlc_Ntk_t * p = DupNtkAndUpdateIDs(pNtk, vec_ids); + Vec_Int_t * vUifConstrs = Vec_IntAlloc( 100 ); + + for(auto& x : _set_uif_pairs) { + get_operator_pair(p, x, wires1, wires2, vec_ids, _vec_vec_op_ffs); + int iObj = AddOneUifImplication(p, wires1, wires2); + Vec_IntPush(vUifConstrs, iObj); + } + + if (Wlc_NtkFfNum(_pOrigNtk) == 0) + FoldCombConstraints(p, vUifConstrs); + else + FoldSeqConstraints(p, vUifConstrs); + + Wlc_Ntk_t * pNew = DupNtkAndUpdateIDs(p, vec_ids); + + Wlc_NtkFree(p); + Vec_IntFree(vUifConstrs); + + return pNew; +} + +Wlc_Ntk_t * UfarManager::_set_up_constraints(vector& vec_ids) { + Wlc_Ntk_t * pNew, * pTemp; + + if(!_set_prev_ops.empty()) + pNew = IntroducePrevOperators(_pOrigNtk, vec_ids, _set_prev_ops, _vec_vec_op_ffs); + else + pNew = DupNtkAndUpdateIDs(_pOrigNtk, vec_ids); + + if(params.fGrey) { + pNew = ApplyGreyConstraints(pTemp = pNew, vec_ids, Wlc_NtkFfNum(_pOrigNtk) > 0); + Wlc_NtkFree(pTemp); + } + + if(!_set_uif_pairs.empty()) { + pNew = ApplyUifConstraints(pTemp = pNew, vec_ids); + Wlc_NtkFree(pTemp); + } + + if(params.nGrey) { + pNew = ApplyGreynessConstraints(pTemp = pNew, _vec_op_greyness, vec_ids); + Wlc_NtkFree(pTemp); + } + + return pNew; +} + + +Wlc_Ntk_t * UfarManager::BuildCurrentAbstraction() { + vector vec_ids = _vec_op_ids; + Wlc_Ntk_t * pNew, * pTemp; + + pNew = _set_up_constraints(vec_ids); + + pNew = AddAuxPOsForOperators(pTemp = pNew, vec_ids); + Wlc_NtkFree(pTemp); + // for(auto x : vec_ids) cout << "ID = " << x << " Type = " << Wlc_NtkObj(pNew, x)->Type << endl; + + if (count(_vec_op_blackbox_marks.begin(), _vec_op_blackbox_marks.end(), true) > 0) { + pNew = _abstract_operators(pTemp = pNew, vec_ids); + Wlc_NtkFree(pTemp); + } + + if(_pAbsWithAuxPos) Wlc_NtkFree(_pAbsWithAuxPos); + _pAbsWithAuxPos = Wlc_NtkDupDfsSimple(pNew); + + pNew = RemoveAuxPOs(pTemp = pNew, Wlc_NtkPoNum(_pOrigNtk)); + Wlc_NtkFree(pTemp); + + // Wlc_WriteVer(pNew, "Abs.v", 0, 0); + + return pNew; +} + +void UfarManager::_simulate() { + timeval t1, t2; + gettimeofday(&t1, NULL); + + _p_sim_mgr->SetParams(params.simSetting); + _p_sim_mgr->Simulate(); + + gettimeofday(&t2, NULL); + profile.tUifSim += elapsed_time_usec(t1, t2); +} + +void UfarManager::FindUifPairsUsingSim() { + timeval t1, t2; + gettimeofday(&t1, NULL); + + _p_sim_mgr->ComputeUifPairs(); + _set_uif_sim_pairs = *_p_sim_mgr->GetSimPairsPtr(); + + set set_new_pairs; + for(auto& x:_set_uif_sim_pairs) { + auto res = _set_uif_pairs.insert(x); + if(res.second) + set_new_pairs.insert(x); + } + if(log_level() >= 3) + print_pairs(set_new_pairs); + + gettimeofday(&t2, NULL); + profile.tUifSim += elapsed_time_usec(t1, t2); +} + +void UfarManager::FindUifPairsUsingCex(Abc_Cex_t * pCex, Abc_Cex_t ** ppCex) { + timeval t1, t2; + gettimeofday(&t1, NULL); + + VecVecChar cex_po_values; + Gia_Man_t * pGia = BitBlast(_pAbsWithAuxPos); + CollectPoValuesInCex(pGia, pCex, cex_po_values, params.fCexMin); + Gia_ManStop(pGia); + + _p_cex_mgr->SetStates(pCex, &_vec_op_blackbox_marks, &_set_uif_pairs, &_set_prev_ops, ¶ms); + set set_found_pairs, set_temp; + if (params.fNorm) { + _p_cex_mgr->FindUifPairs(cex_po_values, params.nSeqLookBack, set_found_pairs); + } else { + _p_cex_mgr->FindUifPairsBasic(cex_po_values, params.nSeqLookBack, set_found_pairs); + } + if(log_level() >= 3) print_pairs(set_found_pairs); + + if(params.fPbaUif && !set_found_pairs.empty()) { + _p_cex_mgr->ComputeCoreUifPairs(set_temp = set_found_pairs, set_found_pairs, ppCex); + if(set_found_pairs.empty()) { + LOG(2) << "Proof-based refinement failed (SAT)"; + // set_found_pairs = set_temp; + if(params.fPbaCex) { + set_found_pairs = set_temp; + } + if(params.fPbaSim && _p_sim_mgr->HasResults()) { + for(auto& x : set_temp) { + if(_p_sim_mgr->GetCount(x) > 0) + set_found_pairs.insert(x); + } + if(set_found_pairs.empty()) { + LOG(2) << "No pair found with PBA and SIM"; + } + } + } + } + + for(auto& x : set_found_pairs) _set_uif_pairs.insert(x); + + gettimeofday(&t2, NULL); + profile.tUifRefine += elapsed_time_usec(t1, t2); +} + +Wlc_Ntk_t * UfarManager::_introduce_bitwise_choices(vector& vec_choice2idx) { + vector vec_ids = _vec_op_ids; + Wlc_Ntk_t * pNew, * pTemp; + + pNew = _set_up_constraints(vec_ids); + + pNew = IntroduceBitwiseChoices(pTemp = pNew, vec_ids, _vec_op_greyness, vec_choice2idx); + Wlc_NtkFree(pTemp); + + return pNew; +} + +Wlc_Ntk_t * UfarManager::_introduce_choices(vector& vec_choice2idx, bool fBlack) { + vector vec_ids = _vec_op_ids; + Wlc_Ntk_t * pNew, * pTemp; + + pNew = _set_up_constraints(vec_ids); + + Vec_Int_t * vec_ops = collect_boxes(vec_ids, _vec_op_blackbox_marks, fBlack); + assert(Vec_IntSize(vec_ops) > 0); + map map_temp; + for(unsigned i = 0; i < vec_ids.size(); i++) + map_temp[vec_ids[i]] = i; + Vec_IntSort(vec_ops, 0); + for(int i = 0; i < Vec_IntSize(vec_ops); i++) + vec_choice2idx.push_back(map_temp[Vec_IntEntry(vec_ops, i)]); + + pNew = IntroduceChoices(pTemp = pNew, vec_ops); + Wlc_NtkFree(pTemp); + Vec_IntFree(vec_ops); + + return pNew; +} + +void UfarManager::_compute_core_choices(Wlc_Ntk_t * pChoice, Abc_Cex_t * pCex, vector& vec_cores, int num_sel_pis) { + int num_ppis = -1; + Gia_Man_t * pGiaFrames = unroll_with_cex(pChoice, pCex, compute_bit_level_pi_num(_pOrigNtk), num_sel_pis, &num_ppis, false); + + compute_core_using_sat(pGiaFrames, vec_cores, pCex->iFrame+1, num_sel_pis, num_ppis, false, NULL, params.fSatMin); + + Gia_ManStop(pGiaFrames); +} + +void UfarManager::_perform_cex_based_white_boxing() { + const VecVecStr* cex_outputs = _p_cex_mgr->GetOutputs(); + + unsigned nFrames = cex_outputs->size(); + assert(nFrames); + unsigned nOperators = (*cex_outputs)[0].size(); + assert(nOperators == _vec_op_ids.size()); + + vector vec_op_refine(nOperators, false); + for(unsigned i = 0; i < nOperators; i++) { + for(unsigned f = 0; f < nFrames; f++) { + if(!bitstr_all_x((*cex_outputs)[f][i])) { + vec_op_refine[i] = true; + break; + } + } + } + + if(log_level() >= 2) { + _oss.str(""); + _oss << "#vWBs = " << count(vec_op_refine.begin(), vec_op_refine.end(), true) << "(" << nOperators << ")" << "; vWBs = "; + for(unsigned i = 0; i < nOperators; i++) { + if(vec_op_refine[i]) + _oss << i << " "; + } + LOG(2) << _oss.str(); + } + + for(unsigned i = 0; i < nOperators; i++) { + if(vec_op_refine[i]) + _vec_op_blackbox_marks[i] = false; + } +} + +void UfarManager::_shrink_final_abstraction() { + vector vec_choice2idx; + Wlc_Ntk_t * pNtkWithChoices = _introduce_choices(vec_choice2idx, false /*white*/); + + int num_sel_pis = vec_choice2idx.size(); + int num_other_pis = compute_bit_level_pi_num(pNtkWithChoices) - num_sel_pis; + + Gia_Man_t * pMiter = GetInvMiter(pNtkWithChoices, "inv.pla"); + printf("Miter stat: pi = %d, ff = %d, and = %d, po = %d\n", Gia_ManPiNum(pMiter), Gia_ManRegNum(pMiter), Gia_ManAndNum(pMiter), Gia_ManPoNum(pMiter)); + + vector vec_core_choices; + compute_core_using_sat(pMiter, vec_core_choices, 1, num_sel_pis, num_other_pis, false, NULL, params.fSatMin); + + Wlc_NtkFree(pNtkWithChoices); + Gia_ManStop(pMiter); + + LOG(2) << "Shrink from " << vec_choice2idx.size() << " to " << vec_core_choices.size(); + if(log_level() >= 3) { + _oss.str(""); + _oss << "remaining white boxes = "; + for (auto x : vec_core_choices) { + _oss << x << " "; + } + LOG(3) << _oss.str(); + } + + _vec_op_blackbox_marks = vector(_vec_op_ids.size(), true); + for (auto x : vec_core_choices) + _vec_op_blackbox_marks[vec_choice2idx[x]] = false; +} + +void UfarManager::_perform_proof_based_grey_boxing(Abc_Cex_t * pCex) { + vector vec_choice2idx; + Wlc_Ntk_t * pNtkWithChoices = _introduce_bitwise_choices(vec_choice2idx); + + vector vec_core_choices; + _compute_core_choices(pNtkWithChoices, pCex, vec_core_choices, vec_choice2idx.size()); + assert(!vec_core_choices.empty()); + Wlc_NtkFree(pNtkWithChoices); + + if(log_level() >= 4) { + for (auto x : vec_core_choices) { + LOG(4) << "WBit : operator[" << vec_choice2idx[x].first << "] at " << vec_choice2idx[x].second << "/" << _vec_op_greyness[vec_choice2idx[x].first].Size() ; + } + } + + vector vec_change_marks(_vec_op_greyness.size(), false); + for (auto x : vec_core_choices) { + _vec_op_greyness[vec_choice2idx[x].first].Set(vec_choice2idx[x].second, false); + vec_change_marks[vec_choice2idx[x].first] = true; + } + + for (size_t i = 0; i < _vec_op_greyness.size(); ++i) { + Greyness& grey = _vec_op_greyness[i]; + if(vec_change_marks[i]) + grey.UpdateCost(); + + if(grey.IsTooWhite(params.nGrey)) { + LOG(2) << "Grey: SetWhite: [" << i << "] = " << float(grey.CurrentCost())/float(grey.TotalCost()) << " (" << grey.CurrentCost() << " / " << grey.TotalCost() << ") " << grey.to_string(); + grey.SetWhite(); + _vec_op_blackbox_marks[i] = false; + } + } +} + +void UfarManager::_perform_proof_based_white_boxing(Abc_Cex_t * pCex) { + vector vec_choice2idx; + Wlc_Ntk_t * pNtkWithChoices = _introduce_choices(vec_choice2idx, true /*black*/); + + // for(unsigned i = 0; i < vec_choice2idx.size(); i++) cout << "i = " << i << " idx = " << vec_choice2idx[i] << endl; + + vector vec_core_choices; + _compute_core_choices(pNtkWithChoices, pCex, vec_core_choices, vec_choice2idx.size()); + assert(!vec_core_choices.empty()); + Wlc_NtkFree(pNtkWithChoices); + + if(log_level() >= 2) { + _oss.str(""); + _oss << "#vCores = " << vec_core_choices.size() << "; vCores = "; + for (unsigned i = 0; i < vec_core_choices.size(); i++) { + _oss << vec_choice2idx[vec_core_choices[i]] << " "; + } + LOG(2) << _oss.str(); + } + + if (params.iOneWb == -1) { + for (unsigned i = 0; i < vec_core_choices.size(); i++) + _vec_op_blackbox_marks[vec_choice2idx[vec_core_choices[i]]] = false; + } else if (params.iOneWb == 0) { + unsigned idx = 0; + for (auto x : vec_core_choices) { + if (vec_choice2idx[x] > idx) + idx = vec_choice2idx[x]; + } + _vec_op_blackbox_marks[idx] = false; + LOG(2) << "White boxing ID(" << idx << ")"; + } +} + +void UfarManager::DetermineGreyness(Abc_Cex_t *pCex) { + timeval t1, t2; + gettimeofday(&t1, NULL); + + _perform_proof_based_grey_boxing(pCex); + + gettimeofday(&t2, NULL); + profile.tGbRefine += elapsed_time_usec(t1, t2); +} + +void UfarManager::DetermineWhiteBoxes(Abc_Cex_t * pCex) { + timeval t1, t2; + gettimeofday(&t1, NULL); + + if(params.fCbaWb) { + _perform_cex_based_white_boxing(); + } else { + _perform_proof_based_white_boxing(pCex); + } + + gettimeofday(&t2, NULL); + profile.tWbRefine += elapsed_time_usec(t1, t2); +} + +int UfarManager::VerifyCurrentAbstraction(Abc_Cex_t ** ppCex) { + timeval t1, t2; + gettimeofday(&t1, NULL); + + Wlc_Ntk_t * pCurrent = BuildCurrentAbstraction(); + if(!params.fileAbs.empty()) + Wlc_WriteVer(pCurrent, &((params.fileAbs + "_abs.v")[0u]), 0, 0); + + int ret = -1; + /* + if(params.fSuper_prove || params.fSimple) { + ret = super_prove(pCurrent, ppCex, params.fSimple, ¶ms.fileName); + } else { + if ( params.fPthread ) { + ret = verify_model(pCurrent, ppCex, ¶ms.fileName, ¶ms.parSetting, params.fSyn, &_timeout); + } else { + ret = bit_level_solve(pCurrent, ppCex, ¶ms.fileName, ¶ms.parSetting, params.fSyn); + } + } + */ + ret = verify_model(pCurrent, ppCex, ¶ms.fileName, ¶ms.parSetting, params.fSyn, &_timeout); + Wlc_NtkFree(pCurrent); + + gettimeofday(&t2, NULL); + profile.tBLSolver += elapsed_time_usec(t1, t2); + + return ret; +} + +int UfarManager::PerformUIFProve(const timeval& timer) { + struct Mem { + Mem() : pCex(NULL), pCex2(NULL) {} + ~Mem() { + if (pCex) Abc_CexFree(pCex); + if (pCex2) Abc_CexFree(pCex2); + } + Abc_Cex_t * pCex; + Abc_Cex_t * pCex2; + }; + + Mem mem; + char buffer [256]; + int ret; + unsigned n_iter_wb = 0; + unsigned n_iter_uif = 0; + timeval curTime; + float elapsedTime; + + auto dump_grey_stat = [&]() { + if(!params.nGrey || log_level()<2 ) return; + float stat = 0; + for(size_t i = 0; i < _vec_op_greyness.size(); ++i) { + if (_vec_op_greyness[i].IsGrey()) { + unsigned current_cost = _vec_op_greyness[i].CurrentCost(); + unsigned total_cost = _vec_op_greyness[i].TotalCost(); + float cur_stat = float(current_cost) / float(total_cost); + stat += cur_stat; + LOG(2) << "Grey: [" << i << "] = " << setprecision(2) << cur_stat << "\t (" << current_cost << " / " + << total_cost << ") \t" << _vec_op_greyness[i].to_string(); + } + } + LOG(1) << "Grey: [Total] = " << setprecision(4) << stat << " (" << _vec_op_greyness.size() << ")"; + }; + auto print_stat = [&]() { + gettimeofday(&curTime, NULL); + elapsedTime = elapsed_time_usec(timer, curTime)/1000000.0; + sprintf(buffer, "Iteration[%2u][%3u]: %4lu White boxes\t%4lu UIF constraints (time = %.4f)", n_iter_wb, n_iter_uif, count(_vec_op_blackbox_marks.begin(), _vec_op_blackbox_marks.end(), false), _set_uif_pairs.size(), elapsedTime); + LOG(1) << buffer << _get_profile_uf_wb(); + dump_grey_stat(); + if(!params.fileStatesOut.empty()) _dump_states(params.fileStatesOut); + }; + + if (!params.fileStatesIn.empty()) { + _read_states(params.fileStatesIn); + if (params.iExp != -1) _massage_state_b(); + print_stat(); + } + + if (!params.fLazySim && !params.simSetting.empty()) { + unsigned origSize = _set_uif_pairs.size(); + LOG(1) << "Try using simulation to find UIF pairs"; + _simulate(); + FindUifPairsUsingSim(); + params.simSetting.clear(); + if (origSize == _set_uif_pairs.size()) { + LOG(1) << "No new UIF pair is found using simulation"; + } else { + ++n_iter_uif; + print_stat(); + } + } + + while(true) { + while(true) { + if (params.fPbaCex && mem.pCex2) { + if(mem.pCex) Abc_CexFree(mem.pCex); + mem.pCex = mem.pCex2; + mem.pCex2 = NULL; + Wlc_Ntk_t * pCurrent = BuildCurrentAbstraction(); + Wlc_NtkFree(pCurrent); + ret = 0; + } else { + ret = VerifyCurrentAbstraction(&mem.pCex); + } + + if (ret == 0) { // SAT + // GetOperatorsInCex(mem.pCex); + if (verify_cex_on_original(_pOrigNtk, mem.pCex)) { + PrintWordCEX(_pOrigNtk, mem.pCex, &_vec_orig_names); + return 0; + } + unsigned n_before = _set_uif_pairs.size(); + + FindUifPairsUsingCex(mem.pCex, &mem.pCex2); + + if (n_before == _set_uif_pairs.size()) { + LOG(1) << "No new UIF pair is found in CEX"; + + if(!params.simSetting.empty()) { + LOG(1) << "Try using simulation to find UIF pairs"; + _simulate(); + FindUifPairsUsingSim(); + params.simSetting.clear(); + if (n_before == _set_uif_pairs.size()) { + LOG(1) << "No new UIF pair is found using simulation"; + } + } + + if (params.nGrey) { + DetermineGreyness(mem.pCex); + } + + if (n_before == _set_uif_pairs.size() && !params.nGrey) { + break; // entering white-boxing refinement + } + } + + ++n_iter_uif; + print_stat(); + + if(_set_uif_pairs.size() > params.nConstraintLimit) + return -1; + } else if (ret == 1) { // UNSAT + //_shrink_final_abstraction(); + //print_stat(); + return 1; + + } else { + return -1; + + } + } + + DetermineWhiteBoxes(mem.pCex); + + ++n_iter_wb; + print_stat(); + } + return -1; +} + +void UfarManager::_massage_state_b() { + for(const auto& x : _set_uif_pairs ) { + bool first_black = _vec_op_blackbox_marks[x.first.idx]; + bool second_black = _vec_op_blackbox_marks[x.second.idx]; + if (!first_black && !second_black) { + int to_black_idx = (params.iExp == 0 ? x.second.idx : x.first.idx); + _vec_op_blackbox_marks[to_black_idx] = true; + LOG(2) << "Blackbox (" << to_black_idx << ") for UF (" << x.first.idx << ", " << x.second.idx << ")"; + } + } +} + + +string UfarManager::_get_profile_uf_wb() { + if (log_level() <= 4) return ""; + unsigned num_full_white = 0; + unsigned num_full_black = 0; + unsigned num_grey = 0; + for(const auto& x : _set_uif_pairs ) { + bool first_black = _vec_op_blackbox_marks[x.first.idx]; + bool second_black = _vec_op_blackbox_marks[x.second.idx]; + if(first_black && second_black) { + ++num_full_black; + } else if (!first_black && !second_black) { + ++num_full_white; + LOG(3) << "ww : (" << x.first.idx << ", " << x.second.idx << ")"; + } else { + ++num_grey; + } + } + ostringstream oss; + oss << "; ww = " << num_full_white << "; bb = " << num_full_black << "; gr = " << num_grey <<";"; + return oss.str(); +} + +void UfarManager::GetOperatorsInCex(Abc_Cex_t *pCex, VecVecStr* pRes) { + VecVecChar cex_po_values; + + Gia_Man_t * pGia = BitBlast(_pAbsWithAuxPos); + CollectPoValuesInCex(pGia, pCex, cex_po_values, params.fCexMin); + Gia_ManStop(pGia); + + unsigned pos; + for(unsigned f = 0; f < cex_po_values.size(); f++) { + if (pRes) pRes->push_back(VecStr()); + vector& cur_po_vals = cex_po_values[f]; + pos = 1; + for(unsigned i = 1; i < Wlc_NtkPoNum(_pAbsWithAuxPos); i++) { + int range = Wlc_ObjRange(Wlc_NtkPo(_pAbsWithAuxPos, i)); + string bit_str = string(cur_po_vals.begin() + pos, cur_po_vals.begin() + pos + range); + if (!pRes) { + cout << "PO[" << i << "]@" << f << " = " << string(bit_str.rbegin(), bit_str.rend()) << endl; + } else { + pRes->back().push_back(bit_str); + } + pos += range; + } + } + +} + +void UfarManager::DumpParams() const { + unsigned n_setw = 15; + cout << "Parameters : " << endl; + cout << " " << setw(n_setw) << left << "cexmin" << " : " << (params.fCexMin ? "yes" : "no") << endl; + cout << " " << setw(n_setw) << left << "iLogLevel" << " : " << params.iVerbosity << endl; +} + +void UfarManager::DumpMgrInfo() const { + cout << "Operators : " << endl; + for(unsigned i = 0; i < _vec_op_ids.size(); i++, printf("\n")) { + cout << "OP_ID = " << i << " : "; + cout << _vec_orig_names[_vec_op_ids[i]] << " = "; + cout << _vec_orig_names[Wlc_ObjFaninId0(Wlc_NtkObj(_pOrigNtk, _vec_op_ids[i]))] << " * "; + cout << _vec_orig_names[Wlc_ObjFaninId1(Wlc_NtkObj(_pOrigNtk, _vec_op_ids[i]))] ; + cout << (_vec_op_blackbox_marks[i] ? " (black)" : "") ; + } +} + + +void UfarManager::_dump_states(const std::string& file) { + fstream fs; + fs.open((file + "_s").c_str(), fstream::out); + + if(!fs.is_open()) { + cerr << "Cannot open the file for dumping states.\n"; + return; + } + + fs << "B\n"; + for (int i = 0; i < _vec_op_blackbox_marks.size(); ++i) { + fs << (_vec_op_blackbox_marks[i] == true ? "1" : "0"); + } + fs << "\nP\n"; + for (const auto& x : _set_uif_pairs) { + fs << x.first.idx << "," << x.first.timediff << ","; + fs << x.second.idx << "," << x.second.timediff << ","; + fs << (x.fMark == true ? "1" : "0") << "\n"; + } + + fs.close(); +} + +void UfarManager::_read_states(const std::string &file) { + fstream fs; + fs.open(file.c_str(), fstream::in); + + if(!fs.is_open()) { + cerr << "Cannot open the file for reading states.\n"; + return; + } + + string line; + getline(fs, line); + assert(line == "B"); + getline(fs, line); + set_state_b(_vec_op_blackbox_marks, line); + getline(fs, line); + assert(line == "P"); + while(getline(fs, line)) { + set_state_p(_set_uif_pairs, line); + } + + if(log_level() >= 3) print_blackboxes(_vec_op_blackbox_marks); + if(log_level() >= 3) print_pairs(_set_uif_pairs); + + fs.close(); +} + + Wlc_Ntk_t * UfarManager::_abstract_operators(Wlc_Ntk_t * pNtk, const vector& vec_ids) const { + Vec_Int_t * vec_ops = collect_boxes(vec_ids, _vec_op_blackbox_marks, true /*black*/); + assert(Vec_IntSize(vec_ops) > 0); + + Wlc_Ntk_t * pNew = NULL; + pNew = AbstractNodes(pNtk, vec_ops); + + Vec_IntFree(vec_ops); + + return pNew; +} + +} diff --git a/src/opt/ufar/UfarMgr.h b/src/opt/ufar/UfarMgr.h new file mode 100755 index 000000000..823e47a18 --- /dev/null +++ b/src/opt/ufar/UfarMgr.h @@ -0,0 +1,144 @@ +/* + * UifMgr.h + * + * Created on: Aug 25, 2015 + * Author: Yen-Sheng Ho + */ + +#ifndef SRC_EXT2_UIF_UIFMGR_H_ +#define SRC_EXT2_UIF_UIFMGR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct Wlc_Ntk_t_ Wlc_Ntk_t; +typedef struct Abc_Cex_t_ Abc_Cex_t; +typedef struct Gia_Man_t_ Gia_Man_t; + +namespace UFAR { + +using VecVecInt = std::vector >; +using VecVecStr = std::vector >; +using VecChar = std::vector; +using VecStr = std::vector; +using IntPair = std::pair; + +struct OperatorID; +struct UifPair; +struct Greyness; +using UIF_PAIR = UifPair; + +class SimUifPairFinder; +class CexUifPairFinder; + +class UfarManager { + public: + struct Params { + Params(); + bool fCexMin; + bool fPbaUif; + bool fLazySim; + bool fPbaSim; + bool fPbaCex; + bool fSatMin; + bool fCbaWb; + bool fGrey; + float nGrey; + bool fNorm; + bool fSuper_prove; + bool fSimple; + bool fSyn; + bool fPthread; + int iOneWb; + int iExp; + unsigned nConstraintLimit; + unsigned iVerbosity; + unsigned nSeqLookBack; + unsigned nTimeout; + std::string simSetting; + std::string parSetting; + std::string fileName; + std::string fileAbs; + std::string fileStatesOut; + std::string fileStatesIn; + }; + struct Profile { + Profile() : tBLSolver(0), tUifRefine(0), tWbRefine(0), tUifSim(0), tGbRefine(0) {} + unsigned tBLSolver; + unsigned tUifRefine; + unsigned tWbRefine; + unsigned tUifSim; + unsigned tGbRefine; + }; + + UfarManager(); + ~UfarManager(); + + void Initialize(Wlc_Ntk_t * pNtk, const std::set& types); + + Wlc_Ntk_t * BuildCurrentAbstraction(); + Wlc_Ntk_t * ApplyUifConstraints(Wlc_Ntk_t * pNtk, std::vector& vec_ids); + void FindUifPairsUsingCex(Abc_Cex_t * pCex, Abc_Cex_t ** ppCex = NULL); + void FindUifPairsUsingSim(); + void DetermineWhiteBoxes(Abc_Cex_t * pCex); + void DetermineGreyness(Abc_Cex_t * pCex); + + int VerifyCurrentAbstraction(Abc_Cex_t ** ppCex); + int PerformUIFProve(const timeval& timer); + + void DumpMgrInfo() const; + void DumpParams() const; + void GetOperatorsInCex(Abc_Cex_t *pCex, VecVecStr* pRes); + + Params params; + Profile profile; + + private: + Wlc_Ntk_t * _abstract_operators(Wlc_Ntk_t * pNtk, const std::vector& vec_ids) const; + + Wlc_Ntk_t * _introduce_choices(std::vector& vec_choice2idx, bool fBlack); + Wlc_Ntk_t * _introduce_bitwise_choices(std::vector& vec_choice2idx); + Wlc_Ntk_t * _set_up_constraints(std::vector& vec_ids); + void _compute_core_choices(Wlc_Ntk_t * pChoice, Abc_Cex_t * pCex, std::vector& vec_cores, int num_sel_pis); + void _simulate(); + void _perform_proof_based_white_boxing(Abc_Cex_t * pCex); + void _perform_proof_based_grey_boxing(Abc_Cex_t * pCex); + void _perform_cex_based_white_boxing(); + std::string _get_profile_uf_wb(); + void _massage_state_b(); + void _dump_states(const std::string& file); + void _read_states(const std::string& file); + + void _shrink_final_abstraction(); + + Wlc_Ntk_t * _pOrigNtk; + std::vector _vec_orig_names; + + std::vector _vec_op_ids; + std::vector _vec_op_blackbox_marks; + std::vector > _vec_vec_op_ffs; + std::vector _vec_op_greyness; + + std::set _set_uif_pairs; + std::set _set_uif_sim_pairs; + std::set _set_prev_ops; + + Wlc_Ntk_t * _pAbsWithAuxPos; + + std::unique_ptr _p_sim_mgr; + std::unique_ptr _p_cex_mgr; + + std::ostringstream _oss; + + struct timespec _timeout; +}; + +} + +#endif /* SRC_EXT2_UIF_UIFMGR_H_ */ diff --git a/src/opt/ufar/UfarPth.cpp b/src/opt/ufar/UfarPth.cpp new file mode 100755 index 000000000..45adea846 --- /dev/null +++ b/src/opt/ufar/UfarPth.cpp @@ -0,0 +1,289 @@ +#include "UfarPth.h" +#include "opt/util/util.h" +#include "opt/untk/NtkNtk.h" +#include "sat/bmc/bmc.h" +#include "proof/pdr/pdr.h" +#include "aig/gia/giaAig.h" + +#include +#include + +extern "C" { + Abc_Ntk_t * Abc_NtkFromAigPhase( Aig_Man_t * pAig ); + int Abc_NtkDarBmc3( Abc_Ntk_t * pAbcNtk, Saig_ParBmc_t * pBmcPars, int fOrDecomp ); + Wla_Man_t * Wla_ManStart( Wlc_Ntk_t * pNtk, Wlc_Par_t * pPars ); + void Wla_ManStop( Wla_Man_t * pWla ); + int Wla_ManSolve( Wla_Man_t * pWla, Wlc_Par_t * pPars ); +} + +static volatile int g_nRunIds = 0; // the number of the last prover instance +int Ufar_CallBackToStop( int RunId ) { assert( RunId <= g_nRunIds ); return RunId < g_nRunIds; } +int Ufar_GetGlobalRunId() { return g_nRunIds; } + +using namespace std; + +namespace UFAR { + +// mutext to control access to shared variables +pthread_mutex_t g_mutex; +// cv to control timer +pthread_cond_t g_cond; + +struct Pth_Data_t +{ + Pth_Data_t () : RunId(-1), pGia(NULL), pWlc(NULL), ppCex(NULL), RetValue(-1), engine(NULL) {} + void set ( const string * eng, int id, Wlc_Ntk_t * pWL, Gia_Man_t * pBL, Abc_Cex_t ** pp ) { engine = eng; RunId = id; pWlc = pWL; pGia = pBL; ppCex = pp; } + int RunId; + Gia_Man_t * pGia; + Wlc_Ntk_t * pWlc; + Abc_Cex_t ** ppCex; + int RetValue; + const string * engine; +}; + +class Solver { +public: + virtual ~Solver() {} + virtual int Solve() = 0; + virtual void SetCex( Abc_Cex_t ** ppCex ) = 0; +}; + +class PDR : public Solver { + public: + PDR( void * pArg ) { + Pth_Data_t * pData = (Pth_Data_t *)pArg; + + _pAig = Gia_ManToAigSimple( pData->pGia ); + + Pdr_Par_t * pPdrPars = &_PdrPars; + Pdr_ManSetDefaultParams(pPdrPars); + pPdrPars->nConfLimit = 0; + pPdrPars->RunId = pData->RunId; + pPdrPars->pFuncStop = Ufar_CallBackToStop; + } + ~PDR() { Aig_ManStop(_pAig); } + + virtual int Solve() { + return Pdr_ManSolve( _pAig, &_PdrPars ); + } + + virtual void SetCex( Abc_Cex_t ** ppCex ) { + assert( _pAig->pSeqModel ); + *(ppCex) = _pAig->pSeqModel; + _pAig->pSeqModel = NULL; + } + + private: + Aig_Man_t * _pAig; + Pdr_Par_t _PdrPars; +}; + +class PDRA : public Solver { + public: + PDRA( void * pArg ) { + Pth_Data_t * pData = (Pth_Data_t *)pArg; + + _pAig = Gia_ManToAigSimple( pData->pGia ); + + Pdr_Par_t * pPdrPars = &_PdrPars; + Pdr_ManSetDefaultParams(pPdrPars); + pPdrPars->nConfLimit = 0; + pPdrPars->RunId = pData->RunId; + pPdrPars->pFuncStop = Ufar_CallBackToStop; + pPdrPars->fUseAbs = 1; // use 'pdr -t' (on-the-fly abstraction) + pPdrPars->fCtgs = 1; // use 'pdr -c' (improved generalization) + pPdrPars->fSkipDown = 0; // use 'pdr -n' (improved generalization) + pPdrPars->nRestLimit = 500; // reset queue or proof-obligations when it gets larger than this + } + ~PDRA() { Aig_ManStop(_pAig); } + + virtual int Solve() { + return Pdr_ManSolve( _pAig, &_PdrPars ); + } + + virtual void SetCex( Abc_Cex_t ** ppCex ) { + assert( _pAig->pSeqModel ); + *(ppCex) = _pAig->pSeqModel; + _pAig->pSeqModel = NULL; + } + + private: + Aig_Man_t * _pAig; + Pdr_Par_t _PdrPars; +}; + +class BMC3 : public Solver { + public: + BMC3 ( void * pArg ) { + Pth_Data_t * pData = (Pth_Data_t *)pArg; + Aig_Man_t * pAig = Gia_ManToAigSimple( pData->pGia ); + _pNtk = Abc_NtkFromAigPhase( pAig ); + Aig_ManStop( pAig ); + + Saig_ParBmc_t * pBmcPars = &_Pars; + Saig_ParBmcSetDefaultParams( pBmcPars ); + pBmcPars->RunId = pData->RunId; + pBmcPars->pFuncStop = Ufar_CallBackToStop; + } + ~BMC3() { Abc_NtkDelete(_pNtk); } + + virtual int Solve() { + return Abc_NtkDarBmc3( _pNtk, &_Pars, 0 ); + } + + virtual void SetCex( Abc_Cex_t ** ppCex ) { + assert( _pNtk->pSeqModel ); + *(ppCex) = _pNtk->pSeqModel; + _pNtk->pSeqModel = NULL; + } + private: + Abc_Ntk_t * _pNtk; + Saig_ParBmc_t _Pars; +}; + +class PDRWLA : public Solver { + public: + PDRWLA( void * pArg ) { + Pth_Data_t * pData = (Pth_Data_t *)pArg; + Wlc_Ntk_t * pNtk = pData->pWlc; + + Wlc_Par_t * pWlcPars = &_Pars; + Wlc_ManSetDefaultParams( pWlcPars ); + pWlcPars->nBitsAdd = 8; + pWlcPars->nBitsMul = 4; + pWlcPars->nBitsMux = 8; + pWlcPars->fXorOutput = 0; + pWlcPars->nLimit = 50; + pWlcPars->fVerbose = 1; + pWlcPars->fProofRefine = 1; + pWlcPars->fHybrid = 0; + pWlcPars->fCheckCombUnsat = 1; + pWlcPars->RunId = pData->RunId; + pWlcPars->pFuncStop = Ufar_CallBackToStop; + + _pWla = Wla_ManStart( pNtk, pWlcPars ); + } + ~PDRWLA() { Wla_ManStop(_pWla); } + + virtual int Solve() { + return Wla_ManSolve( _pWla, &_Pars ); + } + + virtual void SetCex( Abc_Cex_t ** ppCex ) { + assert( _pWla->pCex ); + *(ppCex) = _pWla->pCex; + _pWla->pCex = NULL; + } + private: + Wla_Man_t * _pWla; + Wlc_Par_t _Pars; +}; + +void KillOthers() { + pthread_cond_signal( &g_cond ); + ++g_nRunIds; +} + +void * RunSolver( void * pArg ) { + Pth_Data_t * pData = (Pth_Data_t *)pArg; + Solver * pSolver = NULL; + int status; + + if ( *(pData->engine) == "pdr" ) + pSolver = new PDR( pArg ); + else if ( *(pData->engine) == "pdra" ) + pSolver = new PDRA( pArg ); + else if ( *(pData->engine) == "bmc3" ) + pSolver = new BMC3( pArg ); + else if ( *(pData->engine) == "wla" ) + pSolver = new PDRWLA( pArg ); + else { + pthread_exit( NULL ); + assert(0); + return 0; + } + + pData->RetValue = pSolver->Solve(); + int ret = pData->RetValue; + + status = pthread_mutex_lock(&g_mutex); assert( status == 0 ); + if ( ret == 0 ) { + pSolver->SetCex( pData->ppCex ); + LOG(2) << *(pData->engine) << " found CEX. RunId = " << pData->RunId; + + KillOthers(); + } else if ( ret == 1 ) { + LOG(2) << *(pData->engine) << " proved the problem. RunId = " << pData->RunId; + KillOthers(); + } else { + if ( pData->RunId < g_nRunIds ) { + LOG(2) << *(pData->engine) << " was cancelled. RunId = " << pData->RunId; + } + } + status = pthread_mutex_unlock(&g_mutex); assert( status == 0 ); + + delete pSolver; + + pthread_exit( NULL ); + assert(0); + return 0; +} + +void * Timer ( void * pArg ) { + struct timespec * pTimeout = ( struct timespec * )pArg; + int retcode = 0; + int status; + + status = pthread_mutex_lock(&g_mutex); assert( status == 0 ); + retcode = pthread_cond_timedwait(&g_cond, &g_mutex, pTimeout); + if ( retcode == ETIMEDOUT ) { + LOG(2) << "Timer reached timeout."; + KillOthers(); + } else { + LOG(2) << "Timer was cancelled."; + } + status = pthread_mutex_unlock(&g_mutex); assert( status == 0 ); + + pthread_exit( NULL ); + assert(0); + return 0; +} + +int RunConcurrentSolver( Wlc_Ntk_t * pNtk, const vector& vSolvers, Abc_Cex_t ** ppCex, struct timespec * pTimeout ) { + assert( pTimeout ); + + int status; + vector vDatas ( vSolvers.size() ); + vector vThreads ( vSolvers.size() ); + pthread_t timer; + + Gia_Man_t * pGia = BitBlast( pNtk ); + + status = pthread_create( &timer, NULL, Timer, pTimeout ); + assert( status == 0 ); + + for ( size_t i = 0; i < vSolvers.size(); ++i ) { + vDatas[i].set( &vSolvers[i], g_nRunIds, pNtk, pGia, ppCex ); + status = pthread_create( &vThreads[i], NULL, RunSolver, &vDatas[i] ); + assert( status == 0 ); + } + + status = pthread_join( timer, NULL ); + assert( status == 0 ); + for ( size_t i = 0; i < vSolvers.size(); ++i ) { + status = pthread_join( vThreads[i], NULL ); + assert( status == 0 ); + } + + Gia_ManStop( pGia ); + + for( auto& x : vDatas ) { + if ( x.RetValue != -1 ) + return x.RetValue; + } + return -1; +} + + + +} diff --git a/src/opt/ufar/UfarPth.h b/src/opt/ufar/UfarPth.h new file mode 100755 index 000000000..d05da25fc --- /dev/null +++ b/src/opt/ufar/UfarPth.h @@ -0,0 +1,17 @@ +// +// Created by Yen-Sheng Ho on 04/09/17. +// + +#ifndef SRC_EXT2_UFAR_PTH_H +#define SRC_EXT2_UFAR_PTH_H + +#include "base/wlc/wlc.h" +#include +#include + +namespace UFAR +{ + int RunConcurrentSolver( Wlc_Ntk_t * pNtk, const std::vector& vSolvers, Abc_Cex_t ** ppCex, struct timespec * timeout ); +} + +#endif //SRC_EXT2_UFAR_PTH_H diff --git a/src/opt/ufar/module.make b/src/opt/ufar/module.make new file mode 100755 index 000000000..30c6c1d46 --- /dev/null +++ b/src/opt/ufar/module.make @@ -0,0 +1,3 @@ +SRC += src/opt/ufar/UfarCmd.cpp \ + src/opt/ufar/UfarPth.cpp \ + src/opt/ufar/UfarMgr.cpp diff --git a/src/opt/untk/Netlist.cpp b/src/opt/untk/Netlist.cpp new file mode 100755 index 000000000..c6643e443 --- /dev/null +++ b/src/opt/untk/Netlist.cpp @@ -0,0 +1,63 @@ +// +// Created by Yen-Sheng Ho on 8/9/16. +// + +#include "Netlist.h" + +#include + +#include + +using namespace std; + +namespace UFAR { + +WNetlist::WNetlist() { + _pNtk = Wlc_NtkAlloc("main", 100); +} + +WNetlist::WNetlist(Wlc_Ntk_t *pNtk) { + if (pNtk) + _pNtk = Wlc_NtkDupDfsSimple(pNtk); + else + _pNtk = NULL; +} + +WNetlist::WNetlist(const WNetlist& that) { + _pNtk = Wlc_NtkDupDfsSimple(that._pNtk); +} + +WNetlist::WNetlist(WNetlist && that) { + _pNtk = that._pNtk; + that._pNtk = NULL; +} + +WNetlist& WNetlist::operator=(const WNetlist& that) { + Wlc_Ntk_t * pTemp = _pNtk; + _pNtk = Wlc_NtkDupDfsSimple(that._pNtk); + if (pTemp) Wlc_NtkFree(pTemp); + return *this; +} + +WNetlist::~WNetlist() { + if (_pNtk) Wlc_NtkFree(_pNtk); +} + +void WNetlist::Clear() { + if (_pNtk) Wlc_NtkFree(_pNtk); + _pNtk = Wlc_NtkAlloc("main", 100); +} + +bool WNetlist::Empty() const { + if (_pNtk == NULL) return true; + + return Wlc_NtkObjNum(_pNtk) == 0; +} + +void WNetlist::Reset(Wlc_Ntk_t *pNtk) { + Wlc_Ntk_t * pTemp = _pNtk; + _pNtk = Wlc_NtkDupDfsSimple(pNtk); + if (pTemp) Wlc_NtkFree(pTemp); +} + +} diff --git a/src/opt/untk/Netlist.h b/src/opt/untk/Netlist.h new file mode 100755 index 000000000..07b6032c5 --- /dev/null +++ b/src/opt/untk/Netlist.h @@ -0,0 +1,33 @@ +// +// Created by ysho on 8/9/16. +// + +#ifndef ABC_WAR_NETLIST_H +#define ABC_WAR_NETLIST_H + +#include + +typedef struct Wlc_Ntk_t_ Wlc_Ntk_t; + +namespace UFAR { + +class WNetlist { +public: + WNetlist(); + WNetlist(Wlc_Ntk_t * pNtk); + WNetlist(const WNetlist& that); + ~WNetlist(); + WNetlist(WNetlist && that); + WNetlist& operator=(const WNetlist& that); + Wlc_Ntk_t * GetNtk() const {return _pNtk;} + void Clear(); + bool Empty() const; + void Reset(Wlc_Ntk_t * pNtk); +private: + Wlc_Ntk_t * _pNtk; +}; + + +} + +#endif //ABC_WAR_NETLIST_H diff --git a/src/opt/untk/NtkCmd.cpp b/src/opt/untk/NtkCmd.cpp new file mode 100755 index 000000000..19f73f87f --- /dev/null +++ b/src/opt/untk/NtkCmd.cpp @@ -0,0 +1,12 @@ +/* + * NtkCmd.cpp + * + * Created on: Aug 25, 2015 + * Author: Yen-Sheng Ho + */ + +#include "opt/untk/NtkCmd.h" + +void Ntk_Init (Abc_Frame_t *pAbc) +{ +} diff --git a/src/opt/untk/NtkCmd.h b/src/opt/untk/NtkCmd.h new file mode 100755 index 000000000..77790a083 --- /dev/null +++ b/src/opt/untk/NtkCmd.h @@ -0,0 +1,15 @@ +/* + * NtkCmd.h + * + * Created on: Aug 25, 2015 + * Author: Yen-Sheng Ho + */ + +#ifndef SRC_EXT2_NTK_NTKCMD_H_ +#define SRC_EXT2_NTK_NTKCMD_H_ + +#include "base/main/mainInt.h" + +void Ntk_Init(Abc_Frame_t *pAbc); + +#endif /* SRC_EXT2_NTK_NTKCMD_H_ */ diff --git a/src/opt/untk/NtkNtk.cpp b/src/opt/untk/NtkNtk.cpp new file mode 100755 index 000000000..2c94bcd16 --- /dev/null +++ b/src/opt/untk/NtkNtk.cpp @@ -0,0 +1,1487 @@ +/* + * NtkNtk.cpp + * + * Created on: Aug 25, 2015 + * Author: Yen-Sheng Ho + */ + +#include "NtkNtk.h" +#include "Netlist.h" +#include "opt/util/util.h" +#include "opt/ufar/UfarPth.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +extern "C" { + Abc_Ntk_t * Abc_NtkFromAigPhase( Aig_Man_t * pMan ); + int Abc_NtkDarPdr( Abc_Ntk_t * pNtk, Pdr_Par_t * pPars ); + int Abc_NtkDarBmc3( Abc_Ntk_t * pNtk, Saig_ParBmc_t * pPars, int fOrDecomp ); + int Wlc_ObjDup( Wlc_Ntk_t * pNew, Wlc_Ntk_t * p, int iObj, Vec_Int_t * vFanins ); + void Wlc_NtkDupDfs_rec( Wlc_Ntk_t * pNew, Wlc_Ntk_t * p, int iObj, Vec_Int_t * vFanins ); + void Wlc_ObjSetCo( Wlc_Ntk_t * p, Wlc_Obj_t * pObj, int fFlopInput ); + Aig_Man_t * Abc_NtkToDar( Abc_Ntk_t * pNtk, int fExors, int fRegisters ); + Gia_Man_t * Gia_ManDupInvMiter( Gia_Man_t * p, Gia_Man_t * pInv ); + Wla_Man_t * Wla_ManStart( Wlc_Ntk_t * pNtk, Wlc_Par_t * pPars ); + void Wla_ManStop( Wla_Man_t * pWla ); + int Wla_ManSolve( Wla_Man_t * pWla, Wlc_Par_t * pPars ); + Gia_Man_t * Wlc_NtkBitBlast2( Wlc_Ntk_t * p, Vec_Int_t * vBoxIds ); + void Gia_ManPrintStats( Gia_Man_t * p, Gps_Par_t * pPars ); +} + +using namespace std; + +namespace UFAR { + +static inline int create_buffer(Wlc_Ntk_t * p, Wlc_Obj_t * pObj, Vec_Int_t * vFanins, int max_bw) { + return Wlc_ObjCreate(p, WLC_OBJ_BUF, Wlc_ObjIsSigned(pObj), (max_bw == -1 ? Wlc_ObjRange(pObj) - 1 : max_bw - 1), 0, vFanins); +} + +Gia_Man_t * BitBlast(Wlc_Ntk_t * pNtk) { + Wlc_BstPar_t Par, * pPar = &Par; + Wlc_BstParDefault( pPar ); + return Wlc_NtkBitBlast(pNtk, pPar); + //Gia_Man_t * pGia = Wlc_NtkBitBlast2(pNtk, NULL); + //printf( "Usingn old bit-blaster: " ); + //Gia_ManPrintStats( pGia, NULL ); + //return pGia; +} + +template +void ModifyMarkedNodes(Wlc_Ntk_t * pNtk, int nOrigObjNum, Functor get_modified_node) { + Wlc_Obj_t * pObj; + int i, k, iFanin, iObj; + // iterate through the nodes in the DFS order + Wlc_NtkForEachObj( pNtk, pObj, i ) { + if (i == nOrigObjNum) { + // cout << "break at " << i << endl; + break; + } + if (pObj->Mark) { + pObj->Mark = 0; + iObj = get_modified_node(pObj); + } else { + // update fanins + Wlc_ObjForEachFanin( pObj, iFanin, k ) + Wlc_ObjFanins(pObj)[k] = Wlc_ObjCopy(pNtk, iFanin); + // node to remain + iObj = i; + } + Wlc_ObjSetCopy( pNtk, i, iObj ); + } + + Wlc_NtkForEachCo( pNtk, pObj, i ) + { + iObj = Wlc_ObjId(pNtk, pObj); + if (iObj != Wlc_ObjCopy(pNtk, iObj)) { + assert(pObj->fIsFi); + Wlc_NtkObj(pNtk, Wlc_ObjCopy(pNtk, iObj))->fIsFi = 1; + Vec_IntWriteEntry(&pNtk->vCos, i, Wlc_ObjCopy(pNtk, iObj)); + } + } +} + + +Greyness::Greyness(Wlc_Ntk_t * pNtk, Wlc_Obj_t * pOpObj) { + auto compute_cone_size = [&](Wlc_Ntk_t * pOrig, const vector& ids, Vec_Int_t * vFanins) { + int i; + Wlc_Obj_t * pObj; + WNetlist N; + Wlc_Ntk_t * pNew = N.GetNtk(); + Wlc_NtkForEachCi( pOrig, pObj, i ) { + int iNewPi = Wlc_ObjAlloc(pNew, WLC_OBJ_PI, Wlc_ObjIsSigned(pObj), Wlc_ObjRange(pObj) - 1, 0); + Wlc_ObjSetCopy(pOrig, Wlc_ObjId(pOrig, pObj), iNewPi); + } + + for(auto x : ids) + Wlc_NtkDupDfs_rec( pNew, pOrig, x, vFanins ); + for(auto x : ids) + Wlc_ObjSetCo( pNew, Wlc_NtkObj(pNew, Wlc_ObjCopy(pOrig, x)), 0 ); + + Gia_Man_t * pGia = BitBlast(pNew); + int cone_size = Gia_ManAndNum(pGia); + Gia_ManStop(pGia); + + return cone_size; + }; + + _current_cost = 0; + + _vec_black_bits.resize(Wlc_ObjRange(pOpObj), true); + + int iOpId = Wlc_ObjId(pNtk, pOpObj); + + _N_white = pNtk; + Wlc_Ntk_t * pOrig = _N_white.GetNtk(); + + iOpId = Wlc_ObjCopy(pNtk, iOpId); + pOpObj = Wlc_NtkObj(pOrig, iOpId); + _iOpId = iOpId; + int iFin0 = Wlc_ObjFaninId0(pOpObj); + int iFin1 = Wlc_ObjFaninId1(pOpObj); + + Vec_Int_t * vFanins = Vec_IntAlloc(100); + + Wlc_NtkCleanCopy( pOrig ); + int out_size = compute_cone_size(pOrig, vector{iOpId}, vFanins); + Wlc_NtkCleanCopy( pOrig ); + int in_size = compute_cone_size(pOrig, vector{iFin0, iFin1}, vFanins); + + assert(out_size >= in_size); + _total_cost = unsigned(out_size - in_size); + + Gia_Man_t * pGia = BitBlast(pOrig); + _orig_size = unsigned(Gia_ManAndNum(pGia)); + Gia_ManStop(pGia); + + Vec_IntFree(vFanins); +} + +void Greyness::UpdateCost() { + Wlc_Ntk_t * pOrig = _N_white.GetNtk(); + Wlc_Obj_t * pOpObj = Wlc_NtkObj(pOrig, _iOpId); + int iOpObj = _iOpId; + int offset = pOpObj->Beg; + int range = Wlc_ObjRange(pOpObj); + int is_signed = Wlc_ObjIsSigned(pOpObj); + + Wlc_NtkCleanCopy( pOrig ); + WNetlist N_Grey( pOrig ); + Wlc_Ntk_t * pGrey = N_Grey.GetNtk(); + Wlc_NtkCleanCopy( pGrey ); + int nOrigObjNum = Wlc_NtkObjNumMax( pGrey ); + iOpObj = Wlc_ObjCopy(pOrig, iOpObj); + Wlc_NtkObj(pGrey, iOpObj)->Mark = 1; + int iPPI = Wlc_ObjAlloc(pGrey, WLC_OBJ_PI, is_signed, range - 1, 0); + + auto create_grey_output = [&](Wlc_Obj_t * pObj){ + Vec_Int_t * vFanins = Vec_IntAlloc(3); + Vec_Int_t * vFanins2 = Vec_IntAlloc(3); + + for (size_t i = 0; i < _vec_black_bits.size(); ++i) { + Vec_IntClear(vFanins2); + if (_vec_black_bits[i]) + Vec_IntPush(vFanins2, iPPI); + else + Vec_IntPush(vFanins2, iOpObj); + Vec_IntPushTwo(vFanins2, i + offset, i + offset); + Vec_IntPush(vFanins, Wlc_ObjCreate(pGrey, WLC_OBJ_BIT_SELECT, 0, 0, 0, vFanins2)); + } + Vec_IntReverseOrder(vFanins); + int iObj = Wlc_ObjCreate(pGrey, WLC_OBJ_BIT_CONCAT, is_signed, range - 1, 0, vFanins); + + Vec_IntFree(vFanins); + Vec_IntFree(vFanins2); + + return iObj; + }; + + ModifyMarkedNodes(pGrey, nOrigObjNum, create_grey_output); + + WNetlist N_result(pGrey); + + Gia_Man_t * pGia = BitBlast(N_result.GetNtk()); + unsigned grey_size = unsigned(Gia_ManAndNum(pGia)); + Gia_ManStop(pGia); + + assert(_orig_size >= grey_size); + _current_cost = _total_cost - (_orig_size - grey_size); +} + +unsigned compute_bit_level_pi_num(Wlc_Ntk_t * pNtk) { + unsigned num = 0; + int i; + Wlc_Obj_t * pObj; + Wlc_NtkForEachPi(pNtk, pObj, i) { + num += Wlc_ObjRange(pObj); + } + return num; +} + +void DumpWlcNtk(Wlc_Ntk_t * pNtk) { + int i, k, iFanin; + Wlc_Obj_t * pObj; + Wlc_NtkForEachObj(pNtk, pObj, i) { + cout << i << " <-- "; + Wlc_ObjForEachFanin( pObj, iFanin, k ) { + cout << iFanin << " "; + } + cout << endl; + } +} + +Wlc_Ntk_t * DupNtkAndUpdateIDs(Wlc_Ntk_t * pNtk, std::vector& vec_ids) { + Wlc_Ntk_t * p = Wlc_NtkDupDfsSimple(pNtk); + for(unsigned i = 0; i < vec_ids.size(); i++) { + vec_ids[i] = Wlc_ObjCopy(pNtk, vec_ids[i]); + } + return p; +} + +int AddOneFanoutFF(Wlc_Ntk_t * pNtk, int obj_id, unsigned& count_bits) { + Vec_Int_t * vFanins = Vec_IntAlloc( 1 ); + + int range = Wlc_ObjRange(Wlc_NtkObj(pNtk, obj_id)); + + // create flop + int fo = Wlc_ObjCreate( pNtk, WLC_OBJ_FO, Wlc_ObjIsSigned(Wlc_NtkObj(pNtk, obj_id)), range-1, 0, vFanins ); + + // set up FI + Wlc_NtkObj(pNtk, obj_id)->fIsFi = 1; + + // push FI + Vec_IntPush(&pNtk->vCos, obj_id); + + // push vInits + if ( pNtk->vInits == NULL ) { + pNtk->vInits = Vec_IntAlloc( 100 ); + } + Vec_IntPush( pNtk->vInits, -range ); + + count_bits += range; + + Vec_IntFree( vFanins ); + + assert(Wlc_NtkCi(pNtk, Wlc_NtkCiNum(pNtk)-1) == Wlc_NtkObj(pNtk, fo)); + return Wlc_NtkCiNum(pNtk) - 1; +} + +Wlc_Ntk_t * IntroducePrevOperators(Wlc_Ntk_t * pNtk, vector& vec_ids, const set& set_prev_ops, VecVecInt& vv_op_ffs) { + array wires; + unsigned count_bits = 0; + + vv_op_ffs = vector >(vec_ids.size(), vector()); + Wlc_Ntk_t * p = DupNtkAndUpdateIDs(pNtk, vec_ids); + + for(auto it = set_prev_ops.begin(); it != set_prev_ops.end(); it++) { + int idx = it->idx; + int prev_idx = 0 - it->timediff - 1; + assert(idx < vv_op_ffs.size()); + assert(prev_idx >= 0); + + for(int i = 0; i <= prev_idx; i++) { + if(i < vv_op_ffs[idx].size()) continue; + assert(i == vv_op_ffs[idx].size()); + + if(i == 0) { + wires[2] = vec_ids[idx]; + wires[0] = Wlc_ObjFaninId0(Wlc_NtkObj(p, vec_ids[idx])); + wires[1] = Wlc_ObjFaninId1(Wlc_NtkObj(p, vec_ids[idx])); + } else { + wires[2] = Wlc_ObjId(p, Wlc_NtkCi(p, vv_op_ffs[idx][i-1])); + wires[1] = Wlc_ObjId(p, Wlc_NtkCi(p, vv_op_ffs[idx][i-1]) - 1); + wires[0] = Wlc_ObjId(p, Wlc_NtkCi(p, vv_op_ffs[idx][i-1]) - 2); + } + + AddOneFanoutFF(p, wires[0], count_bits); + AddOneFanoutFF(p, wires[1], count_bits); + vv_op_ffs[idx].push_back(AddOneFanoutFF(p, wires[2], count_bits)); + } + } + + if (p->pInits) { + char * pStr = strcpy( + ABC_ALLOC(char, strlen(p->pInits) + count_bits + 1), + p->pInits); + for (unsigned i = 0; i < count_bits; ++i) { + pStr[strlen(p->pInits) + i] = '0'; + } + pStr[strlen(p->pInits) + count_bits] = '\0'; + ABC_FREE(p->pInits); + p->pInits = pStr; + } + + Wlc_Ntk_t * pNew = DupNtkAndUpdateIDs(p, vec_ids); + Wlc_NtkFree(p); + return pNew; +} + +int AddZeroImplication(Wlc_Ntk_t * pNtk, const array& wires) +{ + int iFaninNew; + int iObjNew, iObjNew2; + int iObjConst0; + + Vec_Int_t * vFanins = Vec_IntAlloc( 2 ); + Vec_Int_t * vCompares = Vec_IntAlloc( 2 ); + + Vec_IntFill(vFanins, 1, 0); + iObjConst0 = Wlc_ObjCreate(pNtk, WLC_OBJ_CONST, 0, 0, 0, vFanins); + + Vec_IntFillTwo( vFanins, 2, wires[0], iObjConst0); + iFaninNew = Wlc_ObjCreate( pNtk, WLC_OBJ_COMP_NOTEQU, 0, 0, 0, vFanins ); + Vec_IntPush( vCompares, iFaninNew ); + + Vec_IntFillTwo( vFanins, 2, wires[1], iObjConst0); + iFaninNew = Wlc_ObjCreate( pNtk, WLC_OBJ_COMP_NOTEQU, 0, 0, 0, vFanins ); + Vec_IntPush( vCompares, iFaninNew ); + + // concatenate fanin comparators + iObjNew = Wlc_ObjCreate( pNtk, WLC_OBJ_BIT_CONCAT, 0, Vec_IntSize(vCompares) - 1, 0, vCompares ); + // create reduction-AND node + Vec_IntFill( vFanins, 1, iObjNew ); + iObjNew = Wlc_ObjCreate( pNtk, WLC_OBJ_REDUCT_AND, 0, 0, 0, vFanins ); + + // create output comparator node + Vec_IntFillTwo( vFanins, 2, wires[2], iObjConst0 ); + iObjNew2 = Wlc_ObjCreate( pNtk, WLC_OBJ_COMP_EQU, 0, 0, 0, vFanins ); + // create implication node (iObjNew is already complemented above) + Vec_IntFillTwo( vFanins, 2, iObjNew, iObjNew2 ); + iObjNew = Wlc_ObjCreate( pNtk, WLC_OBJ_LOGIC_OR, 0, 0, 0, vFanins ); + + Vec_IntFree( vCompares ); + Vec_IntFree( vFanins ); + + return iObjNew; +} + +int AddOneUifImplication(Wlc_Ntk_t * pNtk, const array& wires1, const array& wires2) +{ + int iFaninNew; + int iObjNew, iObjNew2; + + Vec_Int_t * vFanins = Vec_IntAlloc( 2 ); + Vec_Int_t * vCompares = Vec_IntAlloc( 2 ); + + Vec_IntFillTwo( vFanins, 2, wires1[0], wires2[0]); + iFaninNew = Wlc_ObjCreate( pNtk, WLC_OBJ_COMP_NOTEQU, 0, 0, 0, vFanins ); + Vec_IntPush( vCompares, iFaninNew ); + + Vec_IntFillTwo( vFanins, 2, wires1[1], wires2[1]); + iFaninNew = Wlc_ObjCreate( pNtk, WLC_OBJ_COMP_NOTEQU, 0, 0, 0, vFanins ); + Vec_IntPush( vCompares, iFaninNew ); + + // concatenate fanin comparators + iObjNew = Wlc_ObjCreate( pNtk, WLC_OBJ_BIT_CONCAT, 0, Vec_IntSize(vCompares) - 1, 0, vCompares ); + // create reduction-OR node + Vec_IntFill( vFanins, 1, iObjNew ); + iObjNew = Wlc_ObjCreate( pNtk, WLC_OBJ_REDUCT_OR, 0, 0, 0, vFanins ); + // create output comparator node + Vec_IntFillTwo( vFanins, 2, wires1[2], wires2[2] ); + iObjNew2 = Wlc_ObjCreate( pNtk, WLC_OBJ_COMP_EQU, 0, 0, 0, vFanins ); + // create implication node (iObjNew is already complemented above) + Vec_IntFillTwo( vFanins, 2, iObjNew, iObjNew2 ); + iObjNew = Wlc_ObjCreate( pNtk, WLC_OBJ_LOGIC_OR, 0, 0, 0, vFanins ); + + Vec_IntFree( vCompares ); + Vec_IntFree( vFanins ); + + return iObjNew; +} + +int AddOneGrey(Wlc_Ntk_t * pNtk, const array& wires, const Greyness& greyness) { + Vec_Int_t * vFanins = Vec_IntAlloc( 3 ); + Vec_Int_t * vEquals = Vec_IntAlloc( 32 ); + + Vec_IntFillTwo(vFanins, 2, wires[0], wires[1]); + Wlc_Obj_t * pOrigOut = Wlc_NtkObj(pNtk, wires[2]); + int iShadowOut = Wlc_ObjCreate(pNtk, pOrigOut->Type, Wlc_ObjIsSigned(pOrigOut), pOrigOut->End, pOrigOut->Beg, vFanins); + int offset = pOrigOut->Beg; + + for (size_t pos = 0; pos < greyness.Size(); ++pos) { + // black bit : do nothing + if (greyness.Get(pos)) continue; + + // white bit + Vec_IntClear(vFanins); + Vec_IntPush(vFanins, wires[2]); + Vec_IntPushTwo(vFanins, pos + offset, pos + offset); + int iOrigBit = Wlc_ObjCreate(pNtk, WLC_OBJ_BIT_SELECT, 0, 0, 0, vFanins); + Vec_IntClear(vFanins); + Vec_IntPush(vFanins, iShadowOut); + Vec_IntPushTwo(vFanins, pos + offset, pos + offset); + int iShadowBit = Wlc_ObjCreate(pNtk, WLC_OBJ_BIT_SELECT, 0, 0, 0, vFanins); + + Vec_IntFillTwo(vFanins, 2, iOrigBit, iShadowBit); + Vec_IntPush(vEquals, Wlc_ObjCreate(pNtk, WLC_OBJ_COMP_EQU, 0, 0, 0, vFanins)); + } + + int iConcat = Wlc_ObjCreate(pNtk, WLC_OBJ_BIT_CONCAT, 0, Vec_IntSize(vEquals)-1, 0, vEquals); + Vec_IntFill(vFanins, 1, iConcat); + int iRes = Wlc_ObjCreate(pNtk, WLC_OBJ_REDUCT_AND, 0, 0, 0, vFanins); + + Vec_IntFree( vFanins ); + Vec_IntFree( vEquals ); + + return iRes; +} + +Wlc_Ntk_t * ApplyGreynessConstraints(Wlc_Ntk_t * pNtk, const vector& vec_grey, vector& vec_ids) { + array wires; + + Wlc_Ntk_t * p = DupNtkAndUpdateIDs(pNtk, vec_ids); + Vec_Int_t * vGreyConstrs = Vec_IntAlloc( vec_ids.size() ); + + for (size_t i = 0; i < vec_ids.size(); ++i) { + if(!vec_grey[i].IsGrey()) continue; + + int iOut = vec_ids[i]; + wires[2] = iOut; + wires[0] = Wlc_ObjFaninId0(Wlc_NtkObj(p, iOut)); + wires[1] = Wlc_ObjFaninId1(Wlc_NtkObj(p, iOut)); + Vec_IntPush(vGreyConstrs, AddOneGrey(p, wires, vec_grey[i])); + } + + if (Wlc_NtkFfNum(p) == 0) + FoldCombConstraints(p, vGreyConstrs); + else + FoldSeqConstraints(p, vGreyConstrs); + + Wlc_Ntk_t * pNew = DupNtkAndUpdateIDs(p, vec_ids); + + Wlc_NtkFree(p); + Vec_IntFree(vGreyConstrs); + + return pNew; +} + +Wlc_Ntk_t * ApplyGreyConstraints(Wlc_Ntk_t * pNtk, std::vector& vec_ids, bool fSeq) { + array wires; + + Wlc_Ntk_t * p = DupNtkAndUpdateIDs(pNtk, vec_ids); + Vec_Int_t * vGreyConstrs = Vec_IntAlloc( vec_ids.size() ); + + for(auto& x : vec_ids) { + wires[2] = x; + wires[0] = Wlc_ObjFaninId0(Wlc_NtkObj(p, x)); + wires[1] = Wlc_ObjFaninId1(Wlc_NtkObj(p, x)); + int iObj = AddZeroImplication(p, wires); + Vec_IntPush(vGreyConstrs, iObj); + } + + if (!fSeq) + FoldCombConstraints(p, vGreyConstrs); + else + FoldSeqConstraints(p, vGreyConstrs); + + Wlc_Ntk_t * pNew = DupNtkAndUpdateIDs(p, vec_ids); + + Wlc_NtkFree(p); + Vec_IntFree(vGreyConstrs); + + return pNew; +} + +void FoldSeqConstraints(Wlc_Ntk_t *pNtk, Vec_Int_t *vConstrs) +{ + if (Vec_IntSize(vConstrs) == 0) return; + + int iObjNew; + Vec_Int_t * vFanins = Vec_IntAlloc( 100 ); + + // derive the AND of the UIF constraints + assert(Vec_IntSize(vConstrs) > 0); + if (Vec_IntSize(vConstrs) == 1) + iObjNew = Vec_IntEntry(vConstrs, 0); + else { + // concatenate + iObjNew = Wlc_ObjCreate(pNtk, WLC_OBJ_BIT_CONCAT, 0, + Vec_IntSize(vConstrs) - 1, 0, vConstrs); + // create reduction-AND node + Vec_IntFill(vFanins, 1, iObjNew); + iObjNew = Wlc_ObjCreate(pNtk, WLC_OBJ_REDUCT_AND, 0, 0, 0, vFanins); + } + + // create flop + int iObjFO = 0; + int iObjFI = 0; + int iObjNegAllConstraints = 0; + Vec_IntClear(vFanins); + iObjFO = Wlc_ObjCreate(pNtk, WLC_OBJ_FO, 0, 0, 0, vFanins); + Vec_IntFill(vFanins, 1, iObjNew); + iObjNegAllConstraints = Wlc_ObjCreate(pNtk, WLC_OBJ_LOGIC_NOT, 0, 0, 0, vFanins); + Vec_IntFillTwo(vFanins, 2, iObjNegAllConstraints, iObjFO); + iObjFI = Wlc_ObjCreate(pNtk, WLC_OBJ_LOGIC_OR, 0, 0, 0, vFanins); + Wlc_NtkObj(pNtk, iObjFI)->fIsFi = 1; + + if (pNtk->pInits) { + char * pStr = strcpy(ABC_ALLOC(char, strlen(pNtk->pInits) + 2), pNtk->pInits); + pStr[strlen(pNtk->pInits)] = '0'; + pStr[strlen(pNtk->pInits) + 1] = '\0'; + ABC_FREE(pNtk->pInits); + pNtk->pInits = pStr; + // push vInits + Vec_IntPush( pNtk->vInits, -1 ); + } + + //Vec_IntPush(&p->vCis, iObjFO); + Vec_IntPush(&pNtk->vCos, iObjFI); + + // update PO + Vec_IntFill(vFanins, 1, iObjFI); + int iObjNegFI = Wlc_ObjCreate(pNtk, WLC_OBJ_LOGIC_NOT, 0, 0, 0, vFanins); + int iObjPO = Wlc_ObjId(pNtk, Wlc_NtkPo(pNtk, 0)); + Vec_IntFillTwo(vFanins, 2, iObjNegFI, iObjPO); + int iObjNewPO = Wlc_ObjCreate(pNtk, WLC_OBJ_LOGIC_AND, 0, 0, 0, vFanins); + + Vec_IntWriteEntry(&pNtk->vPos, 0, iObjNewPO); + Vec_IntWriteEntry(&pNtk->vCos, 0, iObjNewPO); + + Wlc_NtkObj(pNtk, iObjNewPO)->fIsPo = 1; + Wlc_NtkObj(pNtk, iObjPO)->fIsPo = 0; + + Vec_IntFree( vFanins ); +} + +void FoldCombConstraints(Wlc_Ntk_t *pNtk, Vec_Int_t *vConstrs) +{ + if (Vec_IntSize(vConstrs) == 0) return; + + int iObjNew; + Vec_Int_t * vFanins = Vec_IntAlloc( 100 ); + + // derive the AND of the UIF constraints + assert(Vec_IntSize(vConstrs) > 0); + if (Vec_IntSize(vConstrs) == 1) + iObjNew = Vec_IntEntry(vConstrs, 0); + else { + // concatenate + iObjNew = Wlc_ObjCreate(pNtk, WLC_OBJ_BIT_CONCAT, 0, + Vec_IntSize(vConstrs) - 1, 0, vConstrs); + // create reduction-AND node + Vec_IntFill(vFanins, 1, iObjNew); + iObjNew = Wlc_ObjCreate(pNtk, WLC_OBJ_REDUCT_AND, 0, 0, 0, vFanins); + } + + // update PO + int iObjPO = Wlc_ObjId(pNtk, Wlc_NtkPo(pNtk, 0)); + Vec_IntFillTwo(vFanins, 2, iObjNew, iObjPO); + int iObjNewPO = Wlc_ObjCreate(pNtk, WLC_OBJ_LOGIC_AND, 0, 0, 0, vFanins); + + Vec_IntWriteEntry(&pNtk->vPos, 0, iObjNewPO); + Vec_IntWriteEntry(&pNtk->vCos, 0, iObjNewPO); + + Wlc_NtkObj(pNtk, iObjNewPO)->fIsPo = 1; + Wlc_NtkObj(pNtk, iObjPO)->fIsPo = 0; + + Vec_IntFree( vFanins ); +} + +static bool isPIAndDontCare(Gia_Man_t * pGia, Gia_Obj_t * pObj, Abc_Cex_t * pCare, int f) { + if(!Gia_ObjIsPi(pGia, pObj)) return false; + if(!pCare) return false; + + if(!Abc_InfoHasBit(pCare->pData, pCare->nRegs + pCare->nPis * f + pObj->Value)) + return true; + + return false; +} + +void CollectPoValuesInCex(Gia_Man_t * pGia, Abc_Cex_t * pCex, VecVecChar& po_values, bool fCexMin) +{ + assert(po_values.empty()); + + int k, iBit; + Gia_Obj_t * pObj, * pObjRi, *pObjRo; + + Abc_Cex_t * pCare = NULL; + if (fCexMin) { + pCare = Bmc_CexCareMinimizeAig(pGia, Gia_ManPiNum(pGia), pCex, 1, 0, 0); + assert(pCare != NULL); + + Gia_ManCleanValue(pGia); + Gia_ManForEachPi( pGia, pObj, k ) + pObj->Value = k; + } + + Gia_ManCleanMark0(pGia); + + iBit = pCex->nRegs; + + for ( int f = 0; f <= pCex->iFrame; f++ ) { + po_values.push_back(vector()); + Gia_ManForEachPi( pGia, pObj, k ) + pObj->fMark0 = Abc_InfoHasBit(pCex->pData, iBit++); + Gia_ManForEachAnd( pGia, pObj, k ) + pObj->fMark0 = (Gia_ObjFanin0(pObj)->fMark0 ^ Gia_ObjFaninC0(pObj)) & + (Gia_ObjFanin1(pObj)->fMark0 ^ Gia_ObjFaninC1(pObj)); + Gia_ManForEachCo( pGia, pObj, k ) { + pObj->fMark0 = Gia_ObjFanin0(pObj)->fMark0 ^ Gia_ObjFaninC0(pObj); + if (Gia_ObjIsPo(pGia, pObj)) { + if(isPIAndDontCare(pGia, Gia_ObjFanin0(pObj), pCare, f)) { + po_values.back().push_back('x'); + } else { + po_values.back().push_back(pObj->fMark0 + '0'); + } + } + } + Gia_ManForEachRiRo( pGia, pObjRi, pObjRo, k ) + pObjRo->fMark0 = pObjRi->fMark0; + } + assert( iBit == pCex->nBits ); +} + +Wlc_Ntk_t * AddAuxPOsForOperators(Wlc_Ntk_t * pNtk, std::vector& vec_ids, int max_input_bw, int max_output_bw) +{ + Wlc_Ntk_t * p = DupNtkAndUpdateIDs(pNtk, vec_ids); + + Wlc_Obj_t * pObj; + int iFin0, iFin1; + int iNew, iFin0New, iFin1New; + Vec_Int_t * vFanins = Vec_IntAlloc( 1 ); + + for(unsigned i = 0; i < vec_ids.size(); i++) { + pObj = Wlc_NtkObj(p, vec_ids[i]); + iFin0 = Wlc_ObjFaninId0(pObj); + iFin1 = Wlc_ObjFaninId1(pObj); + + Vec_IntFill(vFanins, 1, iFin0); + iFin0New = create_buffer(p, Wlc_NtkObj(p, iFin0), vFanins, max_input_bw); + Vec_IntFill(vFanins, 1, iFin1); + iFin1New = create_buffer(p, Wlc_NtkObj(p, iFin1), vFanins, max_input_bw); + Vec_IntFill(vFanins, 1, vec_ids[i]); + iNew = create_buffer(p, Wlc_NtkObj(p, vec_ids[i]), vFanins, max_output_bw); + + Wlc_NtkObj(p, iFin0New)->fIsPo = 1; + Wlc_NtkObj(p, iFin1New)->fIsPo = 1; + Wlc_NtkObj(p, iNew)->fIsPo = 1; + + Vec_IntInsert(&p->vCos, Wlc_NtkPoNum(p), iFin0New); + Vec_IntPush(&p->vPos, iFin0New); + Vec_IntInsert(&p->vCos, Wlc_NtkPoNum(p), iFin1New); + Vec_IntPush(&p->vPos, iFin1New); + Vec_IntInsert(&p->vCos, Wlc_NtkPoNum(p), iNew); + Vec_IntPush(&p->vPos, iNew); + } + + Wlc_Ntk_t * pNew = DupNtkAndUpdateIDs(p, vec_ids); + + Vec_IntFree(vFanins); + Wlc_NtkFree(p); + return pNew; +} + +Wlc_Ntk_t * RemoveAuxPOs(Wlc_Ntk_t * p, int iStart) +{ + int iObj; + while (Wlc_NtkPoNum(p) > iStart) { + iObj = Vec_IntEntry(&p->vPos, iStart); + Wlc_NtkObj(p, iObj)->fIsPo = 0; + Vec_IntDrop(&p->vPos, iStart); + Vec_IntDrop(&p->vCos, iStart); + } + + Wlc_Ntk_t * pNew = Wlc_NtkDupDfsSimple(p); + return pNew; +} + +Wlc_Ntk_t * MakeUnderApprox(Wlc_Ntk_t * pNtk, int num_bits) { + int nOrigObjNum = Wlc_NtkObjNumMax(pNtk); + Wlc_NtkCleanCopy( pNtk ); + + Wlc_Obj_t * pObj; + int i; + + Wlc_NtkForEachPi(pNtk, pObj, i) { + pObj->Mark = 1; + } + + auto create_under_pi = [&pNtk, &num_bits] (Wlc_Obj_t * pObj) { + int i = Wlc_ObjId(pNtk, pObj); + int range = Wlc_ObjRange(pObj); + if (range <= num_bits) + return i; + + Vec_Int_t * vFanins = Vec_IntAlloc( 1 ); + int isSigned = Wlc_ObjIsSigned(pObj); + + Vec_IntFill(vFanins, 1, i); + int iShrink = Wlc_ObjCreate(pNtk, WLC_OBJ_BUF, isSigned, num_bits - 1, 0, vFanins); + Vec_IntFill(vFanins, 1, iShrink); + int iObj = Wlc_ObjCreate(pNtk, WLC_OBJ_BUF, isSigned, range - 1, 0, vFanins); + + Vec_IntFree(vFanins); + + return iObj; + }; + + ModifyMarkedNodes(pNtk, nOrigObjNum, create_under_pi); + + Wlc_Ntk_t * pNew = Wlc_NtkDupDfsSimple(pNtk); + + return pNew; +} + +Wlc_Ntk_t * IntroduceBitwiseChoices( Wlc_Ntk_t * pNtk, vector& vec_ids, const vector& vec_greys, vector& vec_choice2idx ) +{ + Wlc_Ntk_t * p = DupNtkAndUpdateIDs(pNtk, vec_ids); + int nOrigObjNum = Wlc_NtkObjNumMax(p); + Wlc_NtkCleanCopy( p ); + + Wlc_Obj_t * pObj; + int iObj; + + map map_node2pi; + map map_node2idx; + map map_node2grey; + + for (size_t i = 0; i < vec_greys.size(); ++i) { + const Greyness& grey = vec_greys[i]; + if(grey.IsWhite()) continue; + + iObj = vec_ids[i]; + pObj = Wlc_NtkObj(p, iObj); + pObj->Mark = 1; + map_node2pi[iObj] = Wlc_ObjAlloc( p, WLC_OBJ_PI, Wlc_ObjIsSigned(pObj), Wlc_ObjRange(pObj) - 1, 0 ); + map_node2grey[iObj] = &grey; + map_node2idx[iObj] = int(i); + } + + auto create_output_with_bitwise_mux = [&vec_choice2idx, &map_node2grey, &map_node2idx, &map_node2pi, &p](Wlc_Obj_t * pObj) { + Vec_Int_t * vFanins = Vec_IntAlloc( 100 ); + Vec_Int_t * vFanins2 = Vec_IntAlloc( 3 ); + + int i = Wlc_ObjId(p, pObj); + bool isSigned = bool(Wlc_ObjIsSigned(pObj)); + int range = Wlc_ObjRange(pObj); + int offset = pObj->Beg; + + Vec_IntClear(vFanins); + for (int pos = 0; pos < range; ++pos) { + Vec_IntClear(vFanins2); + Vec_IntPush(vFanins2, i); + Vec_IntPushTwo(vFanins2, pos + offset, pos + offset); + int iWhiteBit = Wlc_ObjCreate(p, WLC_OBJ_BIT_SELECT, 0, 0, 0, vFanins2); + + if(map_node2grey[i]->Get(pos)) { // add choice/sel + Vec_IntClear(vFanins2); + Vec_IntPush(vFanins2, map_node2pi[i]); + Vec_IntPushTwo(vFanins2, pos, pos); + int iBlackBit = Wlc_ObjCreate(p, WLC_OBJ_BIT_SELECT, 0, 0, 0, vFanins2); + + int iSel = Wlc_ObjAlloc( p, WLC_OBJ_PI, 0, 0, 0); + Vec_IntClear(vFanins2); + Vec_IntPush(vFanins2, iSel); + Vec_IntPush(vFanins2, iBlackBit); + Vec_IntPush(vFanins2, iWhiteBit); + Vec_IntPush(vFanins, Wlc_ObjCreate(p, WLC_OBJ_MUX, 0, 0, 0, vFanins2)); + + vec_choice2idx.push_back(IntPair(map_node2idx[i], pos)); + } else { + Vec_IntPush(vFanins, iWhiteBit); + } + } + Vec_IntReverseOrder(vFanins); + + int iObj = Wlc_ObjCreate(p, WLC_OBJ_BIT_CONCAT, isSigned, range - 1, 0, vFanins); + + Vec_IntFree(vFanins); + Vec_IntFree(vFanins2); + + return iObj; + }; + + ModifyMarkedNodes(p, nOrigObjNum, create_output_with_bitwise_mux); + + Wlc_Ntk_t * pNew = Wlc_NtkDupDfsSimple(p); + Wlc_NtkFree( p ); + + return pNew; +} + +Wlc_Ntk_t * AddConstFlops( Wlc_Ntk_t * pNtk, const set& types ) +{ + Wlc_Ntk_t * p = Wlc_NtkDupDfsSimple(pNtk); + Wlc_NtkCleanCopy( p ); + int nOrigObjNum = Wlc_NtkObjNumMax(p); + Wlc_NtkTransferNames( p, pNtk ); + + Wlc_Obj_t * pObj; + int i, iObjConst0; + + { + Vec_Int_t * vFanins = Vec_IntAlloc( 2 ); + + Vec_IntFill(vFanins, 1, 0); + iObjConst0 = Wlc_ObjCreate(p, WLC_OBJ_CONST, 0, 0, 0, vFanins); + Wlc_NtkObj(p, iObjConst0)->fIsFi = 1; + nOrigObjNum++; + + Vec_IntFree( vFanins ); + } + + Wlc_NtkForEachObj( p, pObj, i ) { + if (types.count(pObj->Type)) { + pObj->Mark = 1; + } + } + + auto create_ff_and_mux = [p, iObjConst0](Wlc_Obj_t * pObj) { + Vec_Int_t * vFanins = Vec_IntAlloc( 3 ); + + int i = Wlc_ObjId(p, pObj); + int isSigned = Wlc_ObjIsSigned(pObj); + int range = Wlc_ObjRange(pObj); + + int iPPI = Wlc_ObjAlloc( p, WLC_OBJ_PI, isSigned, range - 1, 0 ); + int iObjFo = Wlc_ObjCreate( p, WLC_OBJ_FO, 0, 0, 0, vFanins ); + + Vec_IntPush(&p->vCos, iObjConst0); + + if (p->pInits == NULL) { + char * pStr = ABC_ALLOC(char, 2); + pStr[0] = '0'; + pStr[1] = '\0'; + p->pInits = pStr; + } else { + char * pStr = strcpy(ABC_ALLOC(char, strlen(p->pInits) + 2), p->pInits); + pStr[strlen(p->pInits)] = '0'; + pStr[strlen(p->pInits) + 1] = '\0'; + ABC_FREE(p->pInits); + p->pInits = pStr; + } + if ( p->vInits == NULL ) { + p->vInits = Vec_IntAlloc( 100 ); + } + // push vInits + Vec_IntPush( p->vInits, -1 ); + + int iSelID = iObjFo; + Vec_IntClear(vFanins); + Vec_IntPush(vFanins, iSelID); + Vec_IntPush(vFanins, i); + Vec_IntPush(vFanins, iPPI); + int iObj = Wlc_ObjCreate(p, WLC_OBJ_MUX, isSigned, range - 1, 0, vFanins); + + Vec_IntFree( vFanins ); + + return iObj; + }; + + ModifyMarkedNodes(p, nOrigObjNum, create_ff_and_mux); + + Wlc_Ntk_t * pNew = Wlc_NtkDupDfsSimple( p ); + Wlc_NtkTransferNames( pNew, p ); + + Wlc_NtkFree( p ); + + return pNew; +} + +Wlc_Ntk_t * IntroduceChoices( Wlc_Ntk_t * pNtk, Vec_Int_t * vNodes ) +{ + if ( vNodes == NULL ) return NULL; + + Wlc_Ntk_t * pNew; + Wlc_Obj_t * pObj; + int i, iObj; + Vec_Int_t * vFanins = Vec_IntAlloc( 3 ); + map map_node2pi; + + Wlc_Ntk_t * p = Wlc_NtkDupDfsSimple(pNtk); + int nOrigObjNum = Wlc_NtkObjNumMax(p); + Wlc_NtkForEachObjVec( vNodes, pNtk, pObj, i ) { + Vec_IntWriteEntry(vNodes, i, Wlc_ObjCopy(pNtk, Wlc_ObjId(pNtk, pObj))); + } + + // Vec_IntPrint(vNodes); + Wlc_NtkCleanCopy( p ); + + // mark nodes + Wlc_NtkForEachObjVec( vNodes, p, pObj, i ) { + iObj = Wlc_ObjId(p, pObj); + pObj->Mark = 1; + // add fresh PI with the same number of bits + map_node2pi[iObj] = Wlc_ObjAlloc( p, WLC_OBJ_PI, Wlc_ObjIsSigned(pObj), Wlc_ObjRange(pObj) - 1, 0 ); + } + + auto create_output_mux = [&p, &map_node2pi](Wlc_Obj_t * pObj) { + Vec_Int_t * vFanins = Vec_IntAlloc( 3 ); + + int i = Wlc_ObjId(p, pObj); + int isSigned = Wlc_ObjIsSigned(pObj); + int range = Wlc_ObjRange(pObj); + int iSelID = Wlc_ObjAlloc( p, WLC_OBJ_PI, 0, 0, 0); + Vec_IntClear(vFanins); + Vec_IntPush(vFanins, iSelID); + Vec_IntPush(vFanins, map_node2pi.at(i)); + Vec_IntPush(vFanins, i); + int iObj = Wlc_ObjCreate(p, WLC_OBJ_MUX, isSigned, range - 1, 0, vFanins); + + Vec_IntFree(vFanins); + + return iObj; + }; + + ModifyMarkedNodes(p, nOrigObjNum, create_output_mux); + + // DumpWlcNtk(p); + pNew = Wlc_NtkDupDfsSimple( p ); + + Vec_IntFree(vFanins); + Wlc_NtkFree( p ); + + return pNew; +} + +Wlc_Ntk_t * AbstractNodes( Wlc_Ntk_t * pNtk, Vec_Int_t * vNodesInit ) +{ + Vec_Int_t * vNodes = vNodesInit; + Wlc_Ntk_t * pNew; + Wlc_Obj_t * pObj; + int i, iObj; + map map_node2pi; + + if ( vNodes == NULL ) + return NULL; + + Wlc_Ntk_t * p = NULL; + p = Wlc_NtkDupDfsSimple(pNtk); + Wlc_NtkForEachObjVec( vNodes, pNtk, pObj, i ) { + Vec_IntWriteEntry(vNodes, i, Wlc_ObjCopy(pNtk, Wlc_ObjId(pNtk, pObj))); + } + + int nOrigObjNum = Wlc_NtkObjNumMax( p ); + // mark nodes + Wlc_NtkForEachObjVec( vNodes, p, pObj, i ) { + iObj = Wlc_ObjId(p, pObj); + pObj->Mark = 1; + // add fresh PI with the same number of bits + map_node2pi[iObj] = Wlc_ObjAlloc( p, WLC_OBJ_PI, Wlc_ObjIsSigned(pObj), Wlc_ObjRange(pObj) - 1, 0 ); + } + + auto create_black_output = [&p, &map_node2pi](Wlc_Obj_t * pObj) { + return map_node2pi[Wlc_ObjId(p, pObj)]; + }; + + Wlc_NtkCleanCopy( p ); + ModifyMarkedNodes(p, nOrigObjNum, create_black_output); + + if ( vNodes != vNodesInit ) + Vec_IntFree( vNodes ); + // reconstruct topological order + pNew = Wlc_NtkDupDfsSimple( p ); + //Wlc_NtkTransferNames( pNew, p ); + Wlc_NtkFree( p ); + return pNew; +} + +Wlc_Ntk_t * MakeUnderApprox2(Wlc_Ntk_t * pNtk, const set& types, int num_bits) { + Wlc_Obj_t *pObj; + int i, iFanin0, iFanin1; + int iNew, iFanin0New, iFanin1New; + + Vec_Int_t *vFanins = Vec_IntAlloc(2); + Wlc_NtkForEachObj(pNtk, pObj, i) { + if (types.count(pObj->Type)) { + // Already normalized + if (pObj->Mark) continue; + + iFanin0 = Wlc_ObjFaninId0(pObj); + iFanin1 = Wlc_ObjFaninId1(pObj); + + assert(Wlc_ObjIsSigned(Wlc_NtkObj(pNtk, iFanin0))); + assert(Wlc_ObjIsSigned(Wlc_NtkObj(pNtk, iFanin1))); + assert(Wlc_ObjIsSigned(pObj)); + + int range0 = Wlc_ObjRange(Wlc_NtkObj(pNtk, iFanin0)); + int range1 = Wlc_ObjRange(Wlc_NtkObj(pNtk, iFanin1)); + + unsigned op_type = pObj->Type; + + Vec_IntFill(vFanins, 1, iFanin0); + iFanin0New = Wlc_ObjCreate(pNtk, WLC_OBJ_BUF, 1, (range0 > num_bits ? num_bits : range0) - 1, 0, vFanins); + + Vec_IntFill(vFanins, 1, iFanin1); + iFanin1New = Wlc_ObjCreate(pNtk, WLC_OBJ_BUF, 1, (range1 > num_bits ? num_bits : range1) - 1, 0, vFanins); + + int lossless_range = + Wlc_ObjRange(Wlc_NtkObj(pNtk, iFanin0New)) + Wlc_ObjRange(Wlc_NtkObj(pNtk, iFanin1New)); + + Vec_IntFillTwo(vFanins, 2, iFanin0New, iFanin1New); + iNew = Wlc_ObjCreate(pNtk, op_type, 1, lossless_range - 1, 0, vFanins); + + Wlc_NtkObj(pNtk, iNew)->Mark = 1; + + Wlc_NtkObj(pNtk, i)->Type = WLC_OBJ_BUF; + Wlc_NtkObj(pNtk, i)->nFanins = 1; + Wlc_ObjFanins(Wlc_NtkObj(pNtk, i))[0] = iNew; + } + } + + Wlc_Ntk_t * p = Wlc_NtkDupDfsSimple(pNtk); + Vec_Int_t * vConstrs = Vec_IntAlloc(100); + + Wlc_NtkForEachObj(p, pObj, i) { + if (types.count(pObj->Type)) { + iFanin0 = Wlc_ObjFaninId0(pObj); + iFanin1 = Wlc_ObjFaninId1(pObj); + + int iFanin0Fanin0 = Wlc_ObjFaninId0(Wlc_NtkObj(p, iFanin0)); + int iFanin1Fanin0 = Wlc_ObjFaninId0(Wlc_NtkObj(p, iFanin1)); + + Vec_IntFillTwo(vFanins, 2, iFanin0, iFanin0Fanin0); + Vec_IntPush(vConstrs, Wlc_ObjCreate(p, WLC_OBJ_COMP_EQU, 0, 0, 0, vFanins)); + + Vec_IntFillTwo(vFanins, 2, iFanin1, iFanin1Fanin0); + Vec_IntPush(vConstrs, Wlc_ObjCreate(p, WLC_OBJ_COMP_EQU, 0, 0, 0, vFanins)); + } + } + + if (Wlc_NtkFfNum(p) == 0) + FoldCombConstraints(p, vConstrs); + else + FoldSeqConstraints(p, vConstrs); + + Wlc_Ntk_t * pNew = Wlc_NtkDupDfsSimple(p); + + Wlc_NtkFree(p); + Vec_IntFree(vFanins); + Vec_IntFree(vConstrs); + + return pNew; +} + +Wlc_Ntk_t * NormalizeDataTypes(Wlc_Ntk_t * p, const set& types, bool fUnify) +{ + Wlc_Ntk_t *pNtk, *pNew; + + pNtk = Wlc_NtkDupDfsSimple(p); + Wlc_NtkTransferNames( pNtk, p ); + + Wlc_Obj_t *pObj; + int i, iFanin0, iFanin1; + int iNew, iFanin0New, iFanin1New; + int lossless_range; + Vec_Int_t * vFanins = Vec_IntAlloc( 2 ); + vector signs; + + Wlc_NtkForEachObj( pNtk, pObj, i ) { + if ( types.count(pObj->Type) ) { + // Already normalized + if (pObj->Mark) continue; + + iFanin0 = Wlc_ObjFaninId0(pObj); + iFanin1 = Wlc_ObjFaninId1(pObj); + + signs.clear(); + signs.push_back(Wlc_ObjIsSigned(Wlc_NtkObj(pNtk, iFanin0))); + signs.push_back(Wlc_ObjIsSigned(Wlc_NtkObj(pNtk, iFanin1))); + signs.push_back(Wlc_ObjIsSigned(pObj)); + + int op_is_signed = signs[0] && signs[1]; + unsigned op_type = pObj->Type; + + Vec_IntFill(vFanins, 1, iFanin0); + if (op_is_signed) + iFanin0New = iFanin0; + else if (fUnify) + iFanin0New = Wlc_ObjCreate(pNtk, WLC_OBJ_BIT_ZEROPAD, 1, Wlc_NtkObj(pNtk, iFanin0)->End + 1, Wlc_NtkObj(pNtk, iFanin0)->Beg, vFanins); + else + iFanin0New = Wlc_ObjCreate(pNtk, WLC_OBJ_BUF, op_is_signed, Wlc_NtkObj(pNtk, iFanin0)->End, Wlc_NtkObj(pNtk, iFanin0)->Beg, vFanins); + + Vec_IntFill(vFanins, 1, iFanin1); + if (op_is_signed) + iFanin1New = iFanin1; + else if (fUnify) + iFanin1New = Wlc_ObjCreate(pNtk, WLC_OBJ_BIT_ZEROPAD, 1, Wlc_NtkObj(pNtk, iFanin1)->End + 1, Wlc_NtkObj(pNtk, iFanin1)->Beg, vFanins); + else + iFanin1New = Wlc_ObjCreate(pNtk, WLC_OBJ_BUF, op_is_signed, Wlc_NtkObj(pNtk, iFanin1)->End, Wlc_NtkObj(pNtk, iFanin1)->Beg, vFanins); + + lossless_range = Wlc_ObjRange(Wlc_NtkObj(pNtk, iFanin0New)) + Wlc_ObjRange(Wlc_NtkObj(pNtk, iFanin1New)); + + Vec_IntFillTwo(vFanins, 2, iFanin0New, iFanin1New); + if (fUnify) + iNew = Wlc_ObjCreate(pNtk, op_type, 1, lossless_range - 1, 0, vFanins); + else + iNew = Wlc_ObjCreate(pNtk, op_type, op_is_signed, lossless_range - 1, 0, vFanins); + + Wlc_NtkObj(pNtk, iNew)->Mark = 1; + + Wlc_NtkObj(pNtk, i)->Type = WLC_OBJ_BUF; + Wlc_NtkObj(pNtk, i)->nFanins = 1; + Wlc_ObjFanins(Wlc_NtkObj(pNtk, i))[0] = iNew; + } + } + Vec_IntFree(vFanins); + + pNew = Wlc_NtkDupDfsSimple(pNtk); + Wlc_NtkTransferNames( pNew, pNtk ); + Wlc_NtkFree(pNtk); + + return pNew; +} + +Wlc_Ntk_t * CreateMiter(Wlc_Ntk_t *pNtk, bool fXor) +{ + assert(Wlc_NtkPoNum(pNtk) == 2); + + Wlc_Ntk_t *pNew = Wlc_NtkDupDfsSimple(pNtk); + Wlc_NtkTransferNames( pNew, pNtk ); + + Wlc_Obj_t *pOldPo0 = Wlc_NtkPo(pNew, 0); + Wlc_Obj_t *pOldPo1 = Wlc_NtkPo(pNew, 1); + //assert(Wlc_ObjRange(pOldPo0) == Wlc_ObjRange(pOldPo1)); + + int iOldPo0 = Wlc_ObjId(pNew, pOldPo0); + int iOldPo1 = Wlc_ObjId(pNew, pOldPo1); + + Vec_Int_t *vFanins = Vec_IntAlloc( 2 ); + Vec_IntFillTwo( vFanins, 2, iOldPo0, iOldPo1 ); + int iNewPo; + if (!fXor) iNewPo = Wlc_ObjCreate( pNew, WLC_OBJ_COMP_NOTEQU, 0, 0, 0, vFanins ); + else iNewPo = Wlc_ObjCreate( pNew, WLC_OBJ_BIT_XOR, 0, Wlc_ObjRange(pOldPo0)-1, 0, vFanins ); + Vec_IntFree( vFanins ); + + Vec_IntWriteEntry( &pNew->vPos, 0, iNewPo ); + Vec_IntDrop( &pNew->vPos, 1 ); + Vec_IntWriteEntry( &pNew->vCos, 0, iNewPo ); + Vec_IntDrop( &pNew->vCos, 1 ); + + Wlc_NtkObj(pNew, iNewPo)->fIsPo = 1; + Wlc_NtkObj(pNew, iOldPo0)->fIsPo = 0; + Wlc_NtkObj(pNew, iOldPo1)->fIsPo = 0; + + Wlc_Ntk_t *pNewNew = Wlc_NtkDupDfsSimple(pNew); + Wlc_NtkTransferNames( pNewNew, pNew ); + Wlc_NtkFree(pNew); + return pNewNew; +} + +void PrintWordCEX(Wlc_Ntk_t * pNtk, Abc_Cex_t * pCex, const vector* names) +{ + // PIs at each frame + for (int f = 0; f <= pCex->iFrame; ++f) { + int pos = 0; + for (int i = 0; i < Wlc_NtkPiNum(pNtk); ++i) { + cout << "CEX:"; + for (int k = 0; k < Wlc_ObjRange(Wlc_NtkPi(pNtk, i)); ++k ) { + cout << Abc_InfoHasBit(pCex->pData, pCex->nRegs+pCex->nPis*f + (Wlc_ObjRange(Wlc_NtkPi(pNtk, i))-k-1)+pos); + } + pos += Wlc_ObjRange(Wlc_NtkPi(pNtk, i)); + cout << ":" ; + if (names) + cout << (*names)[Wlc_ObjId(pNtk, Wlc_NtkPi(pNtk, i))]; + else + cout << Wlc_ObjName(pNtk, Wlc_ObjId(pNtk, Wlc_NtkPi(pNtk, i))); + cout << "@" << f; + cout << endl; + } + } +} + +bool HasOperator(const Wlc_Ntk_t * p, const set& types) +{ + bool ret = false; + for (set::const_iterator it = types.begin(); it != types.end(); it++) { + if (p->nObjs[*it] > 0) { + ret = true; + break; + } + } + return ret; +} + +void ComputeMaxBW(Wlc_Ntk_t * p, const vector& vec_ids, int& max_input_bw, int& max_output_bw ) { + max_input_bw = max_output_bw = 0; + for (unsigned i = 0; i < vec_ids.size(); i++) { + int op_id = vec_ids[i]; + Wlc_Obj_t * pObj = Wlc_NtkObj(p, op_id); + int range = Wlc_ObjRange(pObj); + int range0 = Wlc_ObjRange(Wlc_ObjFanin0(p, pObj)); + int range1 = Wlc_ObjRange(Wlc_ObjFanin1(p, pObj)); + if (range0 > max_input_bw) + max_input_bw = range0; + if (range1 > max_input_bw) + max_input_bw = range1; + if (range > max_output_bw) + max_output_bw = range; + } +} + +static void get_par_solvers(const string& setting, vector& parSolvers) { + stringstream ss(setting); + string str; + while(getline(ss, str, ':')) { + parSolvers.push_back(str); + } +} + +static void writeCexToFile(int ret, FILE * file, Abc_Cex_t * pCex) { + if (file) { + fprintf(file, "%d\n", ret); + if (ret == 0) { + fprintf(file, "%d %d %d %d\n", pCex->nRegs, pCex->nPis, pCex->iFrame, pCex->iPo); + for (int i = pCex->nRegs; i < pCex->nBits; i++) { + fprintf(file, "%c", Abc_InfoHasBit(pCex->pData, i) ? '1' : '0'); + } + fprintf(file, "\n"); + } + } +} + +static void readCexFromFile(int& ret, FILE * file, Abc_Cex_t ** ppCex, int nOrigRegs = -1) { + int res = 0; + res = fscanf(file, "%d", &ret); + assert(res == 1); + if(ret == 0) { + int nRegs, nPis, iFrame, iPo; + res = fscanf(file, "%d %d %d %d", &nRegs, &nPis, &iFrame, &iPo); + assert(res == 4); + if (nOrigRegs != -1) nRegs = nOrigRegs; + *ppCex = Abc_CexAlloc(nRegs, nPis, iFrame + 1); + (*ppCex)->iFrame = iFrame; + (*ppCex)->iPo = iPo; + int c, i = nRegs; + while ((c = fgetc(file)) != EOF) { + if (c == '1' || c == '0') { + if (c == '1') + Abc_InfoSetBit((*ppCex)->pData, i); + ++i; + } + } + } +} + +int verify_model(Wlc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, const string* pFileName, const string* pParSetting, bool fSyn, struct timespec * timeout) { + if ( !pParSetting || pParSetting->empty() || Wlc_NtkFfNum(pNtk) == 0 ) + return bit_level_solve( pNtk, ppCex, pFileName, pParSetting, fSyn ); + + if(*ppCex) { + Abc_CexFree(*ppCex); + *ppCex = NULL; + } + + int ret = -1; + + vector parSolvers; + parSolvers.push_back("pdr"); + get_par_solvers(*pParSetting, parSolvers); + + ret = RunConcurrentSolver( pNtk, parSolvers, ppCex, timeout ); + + return ret; +} + + +int bit_level_solve(Wlc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, const string* pFileName, const string* pParSetting, bool fSyn) { + if(*ppCex) { + Abc_CexFree(*ppCex); + *ppCex = NULL; + } + + int ret = -1; + Gia_Man_t * pGia = BitBlast(pNtk); + + //int num_orig_regs = Gia_ManRegNum(pGia); + + if(pParSetting && fSyn) { + int num_orig_pis = Gia_ManPiNum(pGia); + pGia = Gia_ManSeqStructSweep(pGia, 1, 1, 0); + assert(num_orig_pis == Gia_ManPiNum(pGia)); + } + + Aig_Man_t * pAig = Gia_ManToAig(pGia, 0); + Abc_Ntk_t * pAbcNtk = Abc_NtkFromAigPhase(pAig); + + if (pFileName && !pFileName->empty()) + Gia_AigerWriteSimple(pGia, &((*pFileName + ".aig")[0u])); + + if (Gia_ManRegNum(pGia) == 0) { + Prove_Params_t Params, *pParams = &Params; + Prove_ParamsSetDefault(pParams); + pParams->fUseRewriting = 0; + pParams->fVerbose = 0; + ret = Abc_NtkIvyProve(&pAbcNtk, pParams); + if (ret == 0) { + *ppCex = Abc_CexDeriveFromCombModel(pAbcNtk->pModel, Abc_NtkPiNum(pAbcNtk), 0, 0); + } + } else { + auto runPDR = [&](FILE * file){ + Pdr_Par_t PdrPars, *pPdrPars = &PdrPars; + Pdr_ManSetDefaultParams(pPdrPars); + pPdrPars->nConfLimit = 0; + //pPdrPars->fDumpInv = 1; + Abc_FrameReadGlobalFrame()->pNtkCur = pAbcNtk; + int res = Abc_NtkDarPdr(pAbcNtk, pPdrPars); + Abc_FrameReadGlobalFrame()->pNtkCur = NULL; + + writeCexToFile(res, file, pAbcNtk->pSeqModel); + + return res; + }; + + /* + auto runBMC3 = [&](FILE * file){ + Saig_ParBmc_t BmcPars, *pBmcPars = &BmcPars; + Saig_ParBmcSetDefaultParams(pBmcPars); + int res = Abc_NtkDarBmc3(pAbcNtk, pBmcPars, 0); + + writeCexToFile(res, file, pAbcNtk->pSeqModel); + + return res; + }; + auto runWla = [&](FILE * file){ + Wlc_Par_t WlcPars, *pWlcPars = &WlcPars; + Wlc_ManSetDefaultParams( pWlcPars ); + pWlcPars->nBitsAdd = 8; + pWlcPars->nBitsMul = 4; + pWlcPars->nBitsMux = 8; + pWlcPars->fXorOutput = 0; + pWlcPars->nLimit = 50; + pWlcPars->fVerbose = 1; + pWlcPars->fProofRefine = 1; + pWlcPars->fHybrid = 0; + pWlcPars->fCheckCombUnsat = 1; + + Wla_Man_t * pWla = Wla_ManStart( pNtk, pWlcPars ); + int RetValue = Wla_ManSolve( pWla, pWlcPars ); + + writeCexToFile(RetValue, file, pWla->pCex); + + Wla_ManStop( pWla ); + return RetValue; + }; + + if(pParSetting && !pParSetting->empty()) { + vector parSolvers; + parSolvers.push_back("pdr"); + get_par_solvers(*pParSetting, parSolvers); + vector childs (parSolvers.size()); + vector files (parSolvers.size()); + + auto runChildTask = [&](const string& engine, int i) { + kill_on_parent_death(SIGQUIT); + LOG(4) << "In process (" << getpid() << ") : running " << engine << "..."; + if(engine=="bmc3") { + ret = runBMC3(files[i]); + } else if(engine=="pdr") { + ret = runPDR(files[i]); + } else if(engine=="treb" || engine=="pdra") { + ret = run_zz(pGia, files[i], engine); + } else if(engine=="wla") { + ret = runWla(files[i]); + } else { + assert(false); + } + }; + + for(int i = 0; i < childs.size(); i++) { + files[i] = tmpfile(); + if((childs[i] = fork()) < 0) { + perror("fork"); + abort(); + } else if (childs[i] == 0) { // in child process + runChildTask(parSolvers[i], i); + exit(0); + } + } + + int status; + pid_t wpid = wait(&status); + + for(unsigned i = 0; i < childs.size(); i++) { + if(wpid == childs[i]) { + LOG(2) << "BLS::" << parSolvers[i] << " returns. Kill others."; + for(unsigned j = 0; j < childs.size(); j++) { + if(i == j) continue; + kill(childs[j], SIGQUIT); + LOG(3) << "BLS::Kill " << parSolvers[j] << "."; + wpid = wait(&status); + } + + rewind(files[i]); + readCexFromFile(ret, files[i], ppCex, num_orig_regs); + + break; + } + } + + for(auto x : files) fclose(x); + + } else */ + { + ret = runPDR(NULL); + if (ret == 0) { + *ppCex = Abc_CexDup(pAbcNtk->pSeqModel, -1); + } + } + } + + Gia_ManStop(pGia); + Aig_ManStop(pAig); + Abc_NtkDelete(pAbcNtk); + + return ret; +} + + +Gia_Man_t * GetInvMiter(Wlc_Ntk_t * pNtk, char * nameInv) { + auto check_pla = [](char * name) { + string line; + ifstream ifs (name, std::ifstream::in); + if(ifs) { + while(getline(ifs, line)) { + if (line == ".p 0") { + return false; + } + } + ifs.close(); + } + return true; + }; + + Gia_Man_t * pMiter = NULL; + Gia_Man_t * pGia = BitBlast(pNtk); + printf("Gia stat: pi = %d, ff = %d, and = %d, po = %d\n", Gia_ManPiNum(pGia), Gia_ManRegNum(pGia), Gia_ManAndNum(pGia), Gia_ManPoNum(pGia)); + + if (!check_pla(nameInv)) { + LOG(2) << "Empty invariant. Changed to combinational SAT."; + pMiter = Gia_ManDupInvMiter(pGia, NULL); + } else { + Abc_Ntk_t *pAbcNtk = Io_Read(nameInv, Io_ReadFileType(nameInv), 1, 0); + Abc_Ntk_t *pStrash = Abc_NtkStrash(pAbcNtk, 0, 1, 0); + Aig_Man_t *pAig = Abc_NtkToDar(pStrash, 0, 0); + Gia_Man_t *pInv = Gia_ManFromAig(pAig); + printf("Inv stat: pi = %d, ff = %d, and = %d, po = %d\n", Gia_ManPiNum(pInv), Gia_ManRegNum(pInv), + Gia_ManAndNum(pInv), Gia_ManPoNum(pInv)); + pMiter = Gia_ManDupInvMiter(pGia, pInv); + + Abc_NtkDelete(pAbcNtk); + Abc_NtkDelete(pStrash); + Aig_ManStop(pAig); + Gia_ManStop(pInv); + } + + Gia_ManStop(pGia); + + return pMiter; +} + +void TestInvariant(string& nameNtk, string& nameInv) { + Gia_Man_t * pGia = Gia_AigerRead( &nameNtk[0u], 0, 0, 0 ); + printf("Gia stat: pi = %d, ff = %d, and = %d, po = %d\n", Gia_ManPiNum(pGia), Gia_ManRegNum(pGia), Gia_ManAndNum(pGia), Gia_ManPoNum(pGia)); + + Abc_Ntk_t * pAbcNtk = Io_Read( &nameInv[0u], Io_ReadFileType(&nameInv[0u]), 1, 0 ); + Abc_Ntk_t * pStrash = Abc_NtkStrash( pAbcNtk, 0, 1, 0 ); + Aig_Man_t * pAig = Abc_NtkToDar( pStrash, 0, 0 ); + Gia_Man_t * pInv = Gia_ManFromAig( pAig ); + printf("Inv stat: pi = %d, ff = %d, and = %d, po = %d\n", Gia_ManPiNum(pInv), Gia_ManRegNum(pInv), Gia_ManAndNum(pInv), Gia_ManPoNum(pInv)); + + Gia_Man_t * pMiter = Gia_ManDupInvMiter(pGia, pInv); + Gia_AigerWrite(pMiter, "miter.aig", 0, 0, 0); + + + // TODO : flip Node1, Abc_LitNot(Node2) + // Gia_Man_t * Gia_ManDupInvMiter( Gia_Man_t * p, Gia_Man_t * pInv ) + + // TODO : flip fUseSupp + // void Pdr_ManDumpClauses( Pdr_Man_t * p, char * pFileName, int fProved ) + + Abc_NtkDelete(pAbcNtk); + Abc_NtkDelete(pStrash); + Aig_ManStop(pAig); + Gia_ManStop(pInv); + Gia_ManStop(pGia); + Gia_ManStop(pMiter); +} + + +} diff --git a/src/opt/untk/NtkNtk.h b/src/opt/untk/NtkNtk.h new file mode 100755 index 000000000..816a9c61f --- /dev/null +++ b/src/opt/untk/NtkNtk.h @@ -0,0 +1,150 @@ +/* + * NtkNtk.h + * + * Created on: Aug 25, 2015 + * Author: Yen-Sheng Ho + */ + +#ifndef SRC_EXT2_NTK_NTKNTK_H_ +#define SRC_EXT2_NTK_NTKNTK_H_ + +#include +#include +#include +#include +#include +#include +#include +#include "Netlist.h" + +typedef struct Wlc_Ntk_t_ Wlc_Ntk_t; +typedef struct Abc_Cex_t_ Abc_Cex_t; +typedef struct Vec_Int_t_ Vec_Int_t; +typedef struct Gia_Man_t_ Gia_Man_t; + +namespace UFAR { + +using VecVecChar = std::vector >; +using VecVecInt = std::vector >; +using IntPair = std::pair; + +struct OperatorID { + OperatorID() : idx(-1), timediff(0) {} + OperatorID(int i) : idx(i), timediff(0) {} + OperatorID(int i, int t) : idx(i), timediff(t) {} + bool operator< (const OperatorID& rhs) const { + if (timediff != rhs.timediff) return (timediff < rhs.timediff); + return (idx < rhs.idx); + } + bool operator!= (const OperatorID& rhs) const { + return ((idx != rhs.idx) || (timediff != rhs.timediff)); + } + int idx; + int timediff; +}; + +std::ostream& operator<<(std::ostream& os, const OperatorID& obj); + +struct UifPair { + UifPair(const OperatorID& first_, const OperatorID& second_) : first(first_), second(second_), fMark(false) {} + UifPair(const OperatorID& first_, const OperatorID& second_, bool fMark_) : first(first_), second(second_), fMark(fMark_) {} + bool operator< (const UifPair& rhs) const { + if(first != rhs.first) return (first < rhs.first); + if(second != rhs.second) return (second < rhs.second); + return (!fMark && rhs.fMark); + } + OperatorID first; + OperatorID second; + bool fMark; +}; + +class Greyness { +public: + Greyness(Wlc_Ntk_t * pNtk, Wlc_Obj_t * pObj); + + std::string to_string() const { + std::ostringstream oss; + for (size_t i = 0; i < _vec_black_bits.size(); ++i) + oss << ( _vec_black_bits[i] ? '0' : '1' ); + return oss.str(); + } + + bool IsGrey() const { + return (!IsBlack() && !IsWhite()); + } + bool IsWhite() const { + return (std::find(_vec_black_bits.begin(), _vec_black_bits.end(), true) == _vec_black_bits.end()); + } + bool IsBlack() const { + return (std::find(_vec_black_bits.begin(), _vec_black_bits.end(), false) == _vec_black_bits.end()); + } + unsigned TotalCost() const { return _total_cost; } + unsigned CurrentCost() const { return _current_cost; } + void UpdateCost(); + + size_t Size() const { return _vec_black_bits.size(); } + bool Get(size_t pos) const { return _vec_black_bits[pos]; } + void Set(size_t pos, bool val) { _vec_black_bits[pos] = val; } + + void SetWhite() { + _vec_black_bits = std::vector(_vec_black_bits.size(), false); + _current_cost = _total_cost; + } + bool IsTooWhite(float threshold) const { + float ratio = float(_current_cost) / float(_total_cost); + if ((ratio > threshold) || (_current_cost == _total_cost)) + return true; + if (ratio > 0.5 && _total_cost < 1000) + return true; + return false; + } + +private: + std::vector _vec_black_bits; + unsigned _total_cost; + unsigned _current_cost; + unsigned _orig_size; + WNetlist _N_white; + int _iOpId; +}; + +Wlc_Ntk_t * AbstractNodes( Wlc_Ntk_t * p, Vec_Int_t * vNodesInit ); +Wlc_Ntk_t * AddConstFlops( Wlc_Ntk_t * p, const std::set& types ); +Wlc_Ntk_t * IntroduceChoices( Wlc_Ntk_t * p, Vec_Int_t * vNodes ); +Wlc_Ntk_t * IntroduceBitwiseChoices( Wlc_Ntk_t * pNtk, std::vector& vec_ids, const std::vector& vec_greys, std::vector& vec_choice2idx ); +Wlc_Ntk_t * CreateMiter(Wlc_Ntk_t *pNtk, bool fXor); +Wlc_Ntk_t * NormalizeDataTypes(Wlc_Ntk_t * p, const std::set& types, bool fUnify); +Wlc_Ntk_t * AddAuxPOsForOperators(Wlc_Ntk_t * p, std::vector& vec_ids, int max_input_bw = -1, int max_output_bw = -1); +Wlc_Ntk_t * RemoveAuxPOs(Wlc_Ntk_t * p, int iStart); +Wlc_Ntk_t * DupNtkAndUpdateIDs(Wlc_Ntk_t * p, std::vector& vec_ids); +Wlc_Ntk_t * ApplyGreynessConstraints(Wlc_Ntk_t * pNtk, const std::vector& vec_grey, std::vector& vec_ids); +Wlc_Ntk_t * IntroducePrevOperators(Wlc_Ntk_t * pNtk, std::vector& vec_ids, const std::set& set_prev_ops, VecVecInt& vv_op_ffs); +Wlc_Ntk_t * ApplyGreyConstraints(Wlc_Ntk_t * pNtk, std::vector& vec_ids, bool fSeq); +int AddOneFanoutFF(Wlc_Ntk_t * pNtk, int obj_id, unsigned& count_bits); +int AddOneUifImplication(Wlc_Ntk_t * pNtk, const std::array& wires1, const std::array& wires2); +void FoldCombConstraints(Wlc_Ntk_t *pNtk, Vec_Int_t *vConstrs); +void FoldSeqConstraints(Wlc_Ntk_t *pNtk, Vec_Int_t *vConstrs); +void CollectPoValuesInCex(Gia_Man_t * pGia, Abc_Cex_t * pCex, VecVecChar& po_values, bool fCexMin); +bool HasOperator(const Wlc_Ntk_t * p, const std::set& types); + +void ComputeMaxBW(Wlc_Ntk_t * p, const std::vector& vec_ids, int& max_input_bw, int& max_output_bw ); + +void PrintWordCEX(Wlc_Ntk_t * pNtk, Abc_Cex_t * pCex, const std::vector* names = nullptr); + +unsigned compute_bit_level_pi_num(Wlc_Ntk_t * pNtk); + +int verify_model(Wlc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, const std::string* pFileName = NULL, const std::string* pParSetting = NULL, bool fSyn = false, struct timespec * timeout = NULL); +int bit_level_solve(Wlc_Ntk_t * pNtk, Abc_Cex_t ** ppCex, const std::string* pFileName = NULL, const std::string* pParSetting = NULL, bool fSyn = false); + +Gia_Man_t * BitBlast(Wlc_Ntk_t * pNtk); + +Gia_Man_t * GetInvMiter(Wlc_Ntk_t * pNtk, char * nameInv); +void TestInvariant(std::string& nameNtk, std::string& nameInv); + +Wlc_Ntk_t * MakeUnderApprox(Wlc_Ntk_t * pNtk, int num_bits); +Wlc_Ntk_t * MakeUnderApprox2(Wlc_Ntk_t * pNtk, const std::set& types, int num_bits); + + +} + +#endif /* SRC_EXT2_NTK_NTKNTK_H_ */ diff --git a/src/opt/untk/module.make b/src/opt/untk/module.make new file mode 100755 index 000000000..35a9eac9f --- /dev/null +++ b/src/opt/untk/module.make @@ -0,0 +1,6 @@ +SRC += src/opt/untk/NtkCmd.cpp \ + src/opt/untk/NtkCmd.h \ + src/opt/untk/Netlist.cpp \ + src/opt/untk/Netlist.h \ + src/opt/untk/NtkNtk.cpp \ + src/opt/untk/NtkNtk.h diff --git a/src/opt/util/module.make b/src/opt/util/module.make new file mode 100755 index 000000000..45e46c251 --- /dev/null +++ b/src/opt/util/module.make @@ -0,0 +1,2 @@ +SRC += src/opt/util/util.cpp \ + src/opt/util/util.h diff --git a/src/opt/util/util.cpp b/src/opt/util/util.cpp new file mode 100755 index 000000000..3b5f1802c --- /dev/null +++ b/src/opt/util/util.cpp @@ -0,0 +1,119 @@ +/* + * util.cpp + * + * Created on: Aug 31, 2015 + * Author: Yen-Sheng Ho + */ + +#include +#include +#include + +#include "util.h" + +using namespace std; + +unsigned LogT::loglevel = 0; +string LogT::prefix = "LOG"; + +bool OptMgr::Parse(int argc, char * argv[]) { + if(argc <= 1) return true; + + for(int i = 1; i < argc; i++) { + if(_map.count(argv[i]) == 0) + return false; + + Option& opt = _map[argv[i]]; + opt._val = "yes"; + + if(!opt.isBool()) { + if (i + 1 == argc) + return false; + opt._val = argv[++i]; + } + } + + return true; +} + +void OptMgr::PrintUsage() { + cout << "usage: " << _cmd << endl; + cout << " Uninterpreted Function Abstraction and Refinement (UFAR)\n"; + for (auto x : _map) { + ostringstream ss; + ss << x.first << " " << (x.second.isBool() ? "" : x.second._arg_type); + cout << " " << setw(10) << left << ss.str(); + cout << " : " << x.second._help << " [default = " << x.second._default << "]" << endl; + } +} + +#ifdef __linux__ + +#include + +void kill_on_parent_death(int sig) +{ + // kill process if parent dies + prctl(PR_SET_PDEATHSIG, sig); + + // the parent might have died before calling prctl + // in that case, it would be adopted by init, whose pid is 1 + if (getppid() == 1) + { + raise(sig); + } +} + +#elif defined(__APPLE__) + +#include + +#include + +#include +#include +#include + +void kill_on_parent_death(int sig) +{ + const int ppid = getppid(); + + std::thread monitor_thread([ppid, sig](){ + + struct kevent change; + EV_SET(&change, ppid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, nullptr); + + int kq = kqueue(); + assert( kq >= 0 ); + + struct kevent event; + struct timespec ts = {0, 0}; + + // start listening, we are guaranteed to receive a notification if ppid is dead + kevent(kq, &change, 1, &event, 1, &ts); + + // however, if ppid died before the call to kevent, ppid might not be the pid of the parent + // in that case, the process it would be adopted by init, whose pid is 1 + if( getppid() == 1 ) + { + raise(sig); + } + + // now block on kevent until the the parent process dies + retry_eintr([&](){ + return kevent(kq, &change, 1, &event, 1, nullptr); + }); + + raise(sig); + }); + + monitor_thread.detach(); +} + +#else // neither linux or OS X + +void kill_on_parent_death(int sig) +{ +} + +#endif diff --git a/src/opt/util/util.h b/src/opt/util/util.h new file mode 100755 index 000000000..747177266 --- /dev/null +++ b/src/opt/util/util.h @@ -0,0 +1,85 @@ +/* + * util.h + * + * Created on: Aug 31, 2015 + * Author: Yen-Sheng Ho + */ + +#ifndef SRC_EXT2_UTIL_UTIL_H_ +#define SRC_EXT2_UTIL_UTIL_H_ + +/* http://stackoverflow.com/questions/6168107/how-to-implement-a-good-debug-logging-feature-in-a-project */ + +#include +#include +#include +#include +#include + +#include + +class LogT { + public: + LogT(unsigned _loglevel = 0) { + // _buffer << "LOG" << _loglevel << " :" << std::string(_loglevel > 0 ? _loglevel * 4 : 1, ' '); + _buffer << prefix << " :" << std::string(_loglevel > 0 ? _loglevel * 4 : 1, ' '); + } + + template + LogT & operator<<(T const & value) { + _buffer << value; + return *this; + } + + ~LogT() { + std::cout << _buffer.str() << std::endl; + } + + static unsigned loglevel; + static std::string prefix; + private: + std::ostringstream _buffer; +}; + +#define LOG(level) \ +if (level > LogT::loglevel) ; \ +else LogT(level) + +class OptMgr { + public: + OptMgr(const std::string& cmd) : _cmd(cmd) {} + bool Parse(int argc, char * argv[]); + void AddOpt(const std::string& opt, const std::string& default_val, const std::string& arg_type, const std::string& help) { + _map[opt] = Option(default_val, arg_type, help); + } + std::string GetOptVal(const std::string& opt) {return _map[opt]._val;} + bool operator[](const std::string& opt) {return _has_opt(opt);} + + void PrintUsage(); + private: + struct Option { + Option(){} + Option(const std::string& val, const std::string& arg_type, const std::string& help) : + _default(val), _help(help), _arg_type(arg_type){} + bool isBool() {return _arg_type == "";} + std::string _default; + std::string _help; + std::string _arg_type; + std::string _val; + }; + + bool _has_opt(const std::string& opt) {return !_map[opt]._val.empty();} + + std::map _map; + std::string _cmd; +}; + +static inline unsigned elapsed_time_usec(const timeval& tBefore, const timeval& tAfter) { + return (tAfter.tv_sec - tBefore.tv_sec)*1000000 + (tAfter.tv_usec - tBefore.tv_usec); +} + +void kill_on_parent_death(int sig); + +int call_python(const char* modulename, const char* funcname, const char* aig, std::vector& cex); + +#endif /* SRC_EXT2_UTIL_UTIL_H_ */ From a38d012563fa071ca1f91dd490b9995d5a6dd81a Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 19:47:46 -0800 Subject: [PATCH 20/36] Added proper guards to new files. --- src/opt/ufar/UfarCmd.cpp | 4 +++- src/opt/ufar/UfarCmd.h | 8 ++------ src/opt/ufar/UfarMgr.cpp | 4 ++++ src/opt/ufar/UfarMgr.h | 6 ++++++ src/opt/ufar/UfarPth.cpp | 6 ++++++ src/opt/ufar/UfarPth.h | 4 ++++ src/opt/untk/Netlist.cpp | 4 ++++ src/opt/untk/Netlist.h | 6 ++++++ src/opt/untk/NtkCmd.cpp | 4 ++++ src/opt/untk/NtkCmd.h | 4 ++++ src/opt/untk/NtkNtk.cpp | 4 ++++ src/opt/untk/NtkNtk.h | 4 ++++ src/opt/util/util.cpp | 5 +++++ src/opt/util/util.h | 4 ++++ 14 files changed, 60 insertions(+), 7 deletions(-) diff --git a/src/opt/ufar/UfarCmd.cpp b/src/opt/ufar/UfarCmd.cpp index 33b0cc3f7..a42a6a536 100755 --- a/src/opt/ufar/UfarCmd.cpp +++ b/src/opt/ufar/UfarCmd.cpp @@ -16,6 +16,8 @@ #include #include +ABC_NAMESPACE_IMPL_START + using namespace std; static UFAR::UfarManager ufar_manager; @@ -333,4 +335,4 @@ static int Abc_CommandTest( Abc_Frame_t * pAbc, int argc, char ** argv ) #endif } - +ABC_NAMESPACE_IMPL_END diff --git a/src/opt/ufar/UfarCmd.h b/src/opt/ufar/UfarCmd.h index 40bdd5cf6..d2b775269 100755 --- a/src/opt/ufar/UfarCmd.h +++ b/src/opt/ufar/UfarCmd.h @@ -10,14 +10,10 @@ #include "base/main/mainInt.h" -#ifdef __cplusplus -extern "C" { -#endif +ABC_NAMESPACE_HEADER_START void Ufar_Init(Abc_Frame_t *pAbc); -#ifdef __cplusplus -} -#endif +ABC_NAMESPACE_HEADER_END #endif /* SRC_EXT2_UIF_UIFCMD_H_ */ diff --git a/src/opt/ufar/UfarMgr.cpp b/src/opt/ufar/UfarMgr.cpp index 071cfcf9e..6f956fdaf 100755 --- a/src/opt/ufar/UfarMgr.cpp +++ b/src/opt/ufar/UfarMgr.cpp @@ -16,6 +16,8 @@ #include #include +ABC_NAMESPACE_IMPL_START + extern "C" { Abc_Ntk_t * Abc_NtkFromAigPhase( Aig_Man_t * pMan ); void Wlc_NtkSimulatePrint( Wlc_Ntk_t * p, Vec_Int_t * vNodes, Vec_Ptr_t * vRes, int nWords, int nFrames ); @@ -1661,3 +1663,5 @@ void UfarManager::_read_states(const std::string &file) { } } + +ABC_NAMESPACE_IMPL_END diff --git a/src/opt/ufar/UfarMgr.h b/src/opt/ufar/UfarMgr.h index 823e47a18..74f024752 100755 --- a/src/opt/ufar/UfarMgr.h +++ b/src/opt/ufar/UfarMgr.h @@ -17,10 +17,14 @@ #include #include +#include "misc/util/abc_namespaces.h" + typedef struct Wlc_Ntk_t_ Wlc_Ntk_t; typedef struct Abc_Cex_t_ Abc_Cex_t; typedef struct Gia_Man_t_ Gia_Man_t; +ABC_NAMESPACE_CXX_HEADER_START + namespace UFAR { using VecVecInt = std::vector >; @@ -141,4 +145,6 @@ class UfarManager { } +ABC_NAMESPACE_CXX_HEADER_END + #endif /* SRC_EXT2_UIF_UIFMGR_H_ */ diff --git a/src/opt/ufar/UfarPth.cpp b/src/opt/ufar/UfarPth.cpp index 45adea846..2a01bcc56 100755 --- a/src/opt/ufar/UfarPth.cpp +++ b/src/opt/ufar/UfarPth.cpp @@ -5,9 +5,13 @@ #include "proof/pdr/pdr.h" #include "aig/gia/giaAig.h" +#include "misc/util/abc_namespaces.h" + #include #include +ABC_NAMESPACE_IMPL_START + extern "C" { Abc_Ntk_t * Abc_NtkFromAigPhase( Aig_Man_t * pAig ); int Abc_NtkDarBmc3( Abc_Ntk_t * pAbcNtk, Saig_ParBmc_t * pBmcPars, int fOrDecomp ); @@ -287,3 +291,5 @@ int RunConcurrentSolver( Wlc_Ntk_t * pNtk, const vector& vSolvers, Abc_C } + +ABC_NAMESPACE_IMPL_END diff --git a/src/opt/ufar/UfarPth.h b/src/opt/ufar/UfarPth.h index d05da25fc..029a24d3a 100755 --- a/src/opt/ufar/UfarPth.h +++ b/src/opt/ufar/UfarPth.h @@ -9,9 +9,13 @@ #include #include +ABC_NAMESPACE_CXX_HEADER_START + namespace UFAR { int RunConcurrentSolver( Wlc_Ntk_t * pNtk, const std::vector& vSolvers, Abc_Cex_t ** ppCex, struct timespec * timeout ); } +ABC_NAMESPACE_CXX_HEADER_END + #endif //SRC_EXT2_UFAR_PTH_H diff --git a/src/opt/untk/Netlist.cpp b/src/opt/untk/Netlist.cpp index c6643e443..e06be9661 100755 --- a/src/opt/untk/Netlist.cpp +++ b/src/opt/untk/Netlist.cpp @@ -8,6 +8,8 @@ #include +ABC_NAMESPACE_IMPL_START + using namespace std; namespace UFAR { @@ -61,3 +63,5 @@ void WNetlist::Reset(Wlc_Ntk_t *pNtk) { } } + +ABC_NAMESPACE_IMPL_END diff --git a/src/opt/untk/Netlist.h b/src/opt/untk/Netlist.h index 07b6032c5..e47d03ff8 100755 --- a/src/opt/untk/Netlist.h +++ b/src/opt/untk/Netlist.h @@ -7,8 +7,12 @@ #include +#include "misc/util/abc_namespaces.h" + typedef struct Wlc_Ntk_t_ Wlc_Ntk_t; +ABC_NAMESPACE_CXX_HEADER_START + namespace UFAR { class WNetlist { @@ -30,4 +34,6 @@ private: } +ABC_NAMESPACE_CXX_HEADER_END + #endif //ABC_WAR_NETLIST_H diff --git a/src/opt/untk/NtkCmd.cpp b/src/opt/untk/NtkCmd.cpp index 19f73f87f..a31534ed2 100755 --- a/src/opt/untk/NtkCmd.cpp +++ b/src/opt/untk/NtkCmd.cpp @@ -7,6 +7,10 @@ #include "opt/untk/NtkCmd.h" +ABC_NAMESPACE_IMPL_START + void Ntk_Init (Abc_Frame_t *pAbc) { } + +ABC_NAMESPACE_IMPL_END diff --git a/src/opt/untk/NtkCmd.h b/src/opt/untk/NtkCmd.h index 77790a083..916d2875c 100755 --- a/src/opt/untk/NtkCmd.h +++ b/src/opt/untk/NtkCmd.h @@ -10,6 +10,10 @@ #include "base/main/mainInt.h" +ABC_NAMESPACE_CXX_HEADER_START + void Ntk_Init(Abc_Frame_t *pAbc); +ABC_NAMESPACE_CXX_HEADER_END + #endif /* SRC_EXT2_NTK_NTKCMD_H_ */ diff --git a/src/opt/untk/NtkNtk.cpp b/src/opt/untk/NtkNtk.cpp index 2c94bcd16..bd81a362b 100755 --- a/src/opt/untk/NtkNtk.cpp +++ b/src/opt/untk/NtkNtk.cpp @@ -20,6 +20,8 @@ #include #include +ABC_NAMESPACE_IMPL_START + extern "C" { Abc_Ntk_t * Abc_NtkFromAigPhase( Aig_Man_t * pMan ); int Abc_NtkDarPdr( Abc_Ntk_t * pNtk, Pdr_Par_t * pPars ); @@ -1485,3 +1487,5 @@ void TestInvariant(string& nameNtk, string& nameInv) { } + +ABC_NAMESPACE_IMPL_END diff --git a/src/opt/untk/NtkNtk.h b/src/opt/untk/NtkNtk.h index 816a9c61f..14103bed9 100755 --- a/src/opt/untk/NtkNtk.h +++ b/src/opt/untk/NtkNtk.h @@ -17,6 +17,8 @@ #include #include "Netlist.h" +ABC_NAMESPACE_CXX_HEADER_START + typedef struct Wlc_Ntk_t_ Wlc_Ntk_t; typedef struct Abc_Cex_t_ Abc_Cex_t; typedef struct Vec_Int_t_ Vec_Int_t; @@ -147,4 +149,6 @@ Wlc_Ntk_t * MakeUnderApprox2(Wlc_Ntk_t * pNtk, const std::set& types, } +ABC_NAMESPACE_CXX_HEADER_END + #endif /* SRC_EXT2_NTK_NTKNTK_H_ */ diff --git a/src/opt/util/util.cpp b/src/opt/util/util.cpp index 3b5f1802c..9fc5ea1a0 100755 --- a/src/opt/util/util.cpp +++ b/src/opt/util/util.cpp @@ -9,8 +9,11 @@ #include #include +#include "misc/util/abc_namespaces.h" #include "util.h" +ABC_NAMESPACE_IMPL_START + using namespace std; unsigned LogT::loglevel = 0; @@ -117,3 +120,5 @@ void kill_on_parent_death(int sig) } #endif + +ABC_NAMESPACE_IMPL_END diff --git a/src/opt/util/util.h b/src/opt/util/util.h index 747177266..b2b3a508c 100755 --- a/src/opt/util/util.h +++ b/src/opt/util/util.h @@ -18,6 +18,8 @@ #include +ABC_NAMESPACE_CXX_HEADER_START + class LogT { public: LogT(unsigned _loglevel = 0) { @@ -82,4 +84,6 @@ void kill_on_parent_death(int sig); int call_python(const char* modulename, const char* funcname, const char* aig, std::vector& cex); +ABC_NAMESPACE_CXX_HEADER_END + #endif /* SRC_EXT2_UTIL_UTIL_H_ */ From 645d8667c3b3a5ec6b78d6e3809f54857c7c1409 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 19:59:20 -0800 Subject: [PATCH 21/36] Fixing misplaced guard. --- src/opt/ufar/UfarMgr.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/opt/ufar/UfarMgr.cpp b/src/opt/ufar/UfarMgr.cpp index 6f956fdaf..d60148054 100755 --- a/src/opt/ufar/UfarMgr.cpp +++ b/src/opt/ufar/UfarMgr.cpp @@ -16,13 +16,6 @@ #include #include -ABC_NAMESPACE_IMPL_START - -extern "C" { - Abc_Ntk_t * Abc_NtkFromAigPhase( Aig_Man_t * pMan ); - void Wlc_NtkSimulatePrint( Wlc_Ntk_t * p, Vec_Int_t * vNodes, Vec_Ptr_t * vRes, int nWords, int nFrames ); -} - #include #include @@ -32,6 +25,8 @@ extern "C" { #include #include +ABC_NAMESPACE_IMPL_START + using namespace std; namespace UFAR { From 00cee5f2f5c834eb5c726a44e090a7a7991704f5 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 20:10:08 -0800 Subject: [PATCH 22/36] Reordering includes. --- src/opt/ufar/UfarCmd.cpp | 10 +++++----- src/opt/ufar/UfarMgr.cpp | 20 +++++++++----------- src/opt/ufar/UfarPth.cpp | 13 +++++++------ src/opt/ufar/UfarPth.h | 2 +- src/opt/untk/Netlist.cpp | 5 ++--- src/opt/untk/NtkNtk.cpp | 14 +++++++------- src/opt/untk/NtkNtk.h | 1 + 7 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/opt/ufar/UfarCmd.cpp b/src/opt/ufar/UfarCmd.cpp index a42a6a536..162105c40 100755 --- a/src/opt/ufar/UfarCmd.cpp +++ b/src/opt/ufar/UfarCmd.cpp @@ -5,17 +5,17 @@ * Author: Yen-Sheng Ho */ +#include +#include +#include +#include + #include "base/wlc/wlc.h" #include "opt/ufar/UfarCmd.h" #include "opt/ufar/UfarMgr.h" #include "opt/untk/NtkNtk.h" #include "opt/util/util.h" -#include -#include -#include -#include - ABC_NAMESPACE_IMPL_START using namespace std; diff --git a/src/opt/ufar/UfarMgr.cpp b/src/opt/ufar/UfarMgr.cpp index d60148054..373e1e322 100755 --- a/src/opt/ufar/UfarMgr.cpp +++ b/src/opt/ufar/UfarMgr.cpp @@ -7,17 +7,6 @@ #include #include - -#include "UfarMgr.h" -#include "opt/untk/NtkNtk.h" -#include "opt/util/util.h" - -#include -#include -#include - -#include - #include #include #include @@ -25,6 +14,15 @@ #include #include +#include + +#include +#include +#include +#include "opt/untk/NtkNtk.h" +#include "opt/util/util.h" +#include "UfarMgr.h" + ABC_NAMESPACE_IMPL_START using namespace std; diff --git a/src/opt/ufar/UfarPth.cpp b/src/opt/ufar/UfarPth.cpp index 2a01bcc56..085614275 100755 --- a/src/opt/ufar/UfarPth.cpp +++ b/src/opt/ufar/UfarPth.cpp @@ -1,15 +1,16 @@ -#include "UfarPth.h" -#include "opt/util/util.h" -#include "opt/untk/NtkNtk.h" -#include "sat/bmc/bmc.h" -#include "proof/pdr/pdr.h" -#include "aig/gia/giaAig.h" #include "misc/util/abc_namespaces.h" #include #include +#include "sat/bmc/bmc.h" +#include "proof/pdr/pdr.h" +#include "aig/gia/giaAig.h" +#include "opt/util/util.h" +#include "opt/untk/NtkNtk.h" +#include "UfarPth.h" + ABC_NAMESPACE_IMPL_START extern "C" { diff --git a/src/opt/ufar/UfarPth.h b/src/opt/ufar/UfarPth.h index 029a24d3a..41716ebe9 100755 --- a/src/opt/ufar/UfarPth.h +++ b/src/opt/ufar/UfarPth.h @@ -5,9 +5,9 @@ #ifndef SRC_EXT2_UFAR_PTH_H #define SRC_EXT2_UFAR_PTH_H -#include "base/wlc/wlc.h" #include #include +#include "base/wlc/wlc.h" ABC_NAMESPACE_CXX_HEADER_START diff --git a/src/opt/untk/Netlist.cpp b/src/opt/untk/Netlist.cpp index e06be9661..896bab39a 100755 --- a/src/opt/untk/Netlist.cpp +++ b/src/opt/untk/Netlist.cpp @@ -2,11 +2,10 @@ // Created by Yen-Sheng Ho on 8/9/16. // -#include "Netlist.h" +#include #include - -#include +#include "Netlist.h" ABC_NAMESPACE_IMPL_START diff --git a/src/opt/untk/NtkNtk.cpp b/src/opt/untk/NtkNtk.cpp index bd81a362b..64dcd613d 100755 --- a/src/opt/untk/NtkNtk.cpp +++ b/src/opt/untk/NtkNtk.cpp @@ -5,10 +5,9 @@ * Author: Yen-Sheng Ho */ -#include "NtkNtk.h" -#include "Netlist.h" -#include "opt/util/util.h" -#include "opt/ufar/UfarPth.h" +#include +#include +#include #include #include @@ -16,9 +15,10 @@ #include #include -#include -#include -#include +#include "opt/util/util.h" +#include "opt/ufar/UfarPth.h" +#include "NtkNtk.h" +#include "Netlist.h" ABC_NAMESPACE_IMPL_START diff --git a/src/opt/untk/NtkNtk.h b/src/opt/untk/NtkNtk.h index 14103bed9..e3323f123 100755 --- a/src/opt/untk/NtkNtk.h +++ b/src/opt/untk/NtkNtk.h @@ -14,6 +14,7 @@ #include #include #include + #include #include "Netlist.h" From d2c15a04db80f3bb78d555575afd0234df494ef6 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 20:38:07 -0800 Subject: [PATCH 23/36] Commenting out conflicting declarations. --- src/opt/untk/NtkNtk.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/opt/untk/NtkNtk.cpp b/src/opt/untk/NtkNtk.cpp index 64dcd613d..ada25c2b2 100755 --- a/src/opt/untk/NtkNtk.cpp +++ b/src/opt/untk/NtkNtk.cpp @@ -26,16 +26,16 @@ extern "C" { Abc_Ntk_t * Abc_NtkFromAigPhase( Aig_Man_t * pMan ); int Abc_NtkDarPdr( Abc_Ntk_t * pNtk, Pdr_Par_t * pPars ); int Abc_NtkDarBmc3( Abc_Ntk_t * pNtk, Saig_ParBmc_t * pPars, int fOrDecomp ); - int Wlc_ObjDup( Wlc_Ntk_t * pNew, Wlc_Ntk_t * p, int iObj, Vec_Int_t * vFanins ); + //int Wlc_ObjDup( Wlc_Ntk_t * pNew, Wlc_Ntk_t * p, int iObj, Vec_Int_t * vFanins ); void Wlc_NtkDupDfs_rec( Wlc_Ntk_t * pNew, Wlc_Ntk_t * p, int iObj, Vec_Int_t * vFanins ); - void Wlc_ObjSetCo( Wlc_Ntk_t * p, Wlc_Obj_t * pObj, int fFlopInput ); + //void Wlc_ObjSetCo( Wlc_Ntk_t * p, Wlc_Obj_t * pObj, int fFlopInput ); Aig_Man_t * Abc_NtkToDar( Abc_Ntk_t * pNtk, int fExors, int fRegisters ); Gia_Man_t * Gia_ManDupInvMiter( Gia_Man_t * p, Gia_Man_t * pInv ); Wla_Man_t * Wla_ManStart( Wlc_Ntk_t * pNtk, Wlc_Par_t * pPars ); void Wla_ManStop( Wla_Man_t * pWla ); int Wla_ManSolve( Wla_Man_t * pWla, Wlc_Par_t * pPars ); Gia_Man_t * Wlc_NtkBitBlast2( Wlc_Ntk_t * p, Vec_Int_t * vBoxIds ); - void Gia_ManPrintStats( Gia_Man_t * p, Gps_Par_t * pPars ); + //void Gia_ManPrintStats( Gia_Man_t * p, Gps_Par_t * pPars ); } using namespace std; From 2accf61bcd8c8f08ea5c3f590e3f03f4cf3307fc Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 20:49:55 -0800 Subject: [PATCH 24/36] Adding undefined procedure. --- src/opt/util/util.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/opt/util/util.cpp b/src/opt/util/util.cpp index 9fc5ea1a0..b06e2ba7e 100755 --- a/src/opt/util/util.cpp +++ b/src/opt/util/util.cpp @@ -72,11 +72,22 @@ void kill_on_parent_death(int sig) #include #include +#include #include #include #include +template +static auto retry_eintr(Func &&fn) -> decltype(fn()) +{ + decltype(fn()) rc; + do { + rc = fn(); + } while (rc == -1 && errno == EINTR); + return rc; +} + void kill_on_parent_death(int sig) { const int ppid = getppid(); From 52741b9123b427586ca1091ac716120c2f8e5f29 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 21:02:20 -0800 Subject: [PATCH 25/36] Adjusting guards to avoid compiler problems. --- src/opt/ufar/UfarMgr.h | 4 ++-- src/opt/untk/Netlist.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/opt/ufar/UfarMgr.h b/src/opt/ufar/UfarMgr.h index 74f024752..d094db4e0 100755 --- a/src/opt/ufar/UfarMgr.h +++ b/src/opt/ufar/UfarMgr.h @@ -19,12 +19,12 @@ #include "misc/util/abc_namespaces.h" +ABC_NAMESPACE_CXX_HEADER_START + typedef struct Wlc_Ntk_t_ Wlc_Ntk_t; typedef struct Abc_Cex_t_ Abc_Cex_t; typedef struct Gia_Man_t_ Gia_Man_t; -ABC_NAMESPACE_CXX_HEADER_START - namespace UFAR { using VecVecInt = std::vector >; diff --git a/src/opt/untk/Netlist.h b/src/opt/untk/Netlist.h index e47d03ff8..9b95bf342 100755 --- a/src/opt/untk/Netlist.h +++ b/src/opt/untk/Netlist.h @@ -9,10 +9,10 @@ #include "misc/util/abc_namespaces.h" -typedef struct Wlc_Ntk_t_ Wlc_Ntk_t; - ABC_NAMESPACE_CXX_HEADER_START +typedef struct Wlc_Ntk_t_ Wlc_Ntk_t; + namespace UFAR { class WNetlist { From 997619d33e2f7163a9ffe0762ed0b312f2488e6f Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 21:08:18 -0800 Subject: [PATCH 26/36] Moving guards to proper places. --- src/opt/util/util.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/opt/util/util.cpp b/src/opt/util/util.cpp index b06e2ba7e..f20588466 100755 --- a/src/opt/util/util.cpp +++ b/src/opt/util/util.cpp @@ -50,10 +50,14 @@ void OptMgr::PrintUsage() { } } +ABC_NAMESPACE_IMPL_END + #ifdef __linux__ #include +ABC_NAMESPACE_IMPL_START + void kill_on_parent_death(int sig) { // kill process if parent dies @@ -67,6 +71,8 @@ void kill_on_parent_death(int sig) } } +ABC_NAMESPACE_IMPL_END + #elif defined(__APPLE__) #include @@ -78,6 +84,8 @@ void kill_on_parent_death(int sig) #include #include +ABC_NAMESPACE_IMPL_START + template static auto retry_eintr(Func &&fn) -> decltype(fn()) { @@ -124,12 +132,16 @@ void kill_on_parent_death(int sig) monitor_thread.detach(); } +ABC_NAMESPACE_IMPL_END + #else // neither linux or OS X +ABC_NAMESPACE_IMPL_START + void kill_on_parent_death(int sig) { } -#endif - ABC_NAMESPACE_IMPL_END + +#endif From 95b8d573315f9a8fb5ba26a1d5d0685fad2137b2 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 21:34:38 -0800 Subject: [PATCH 27/36] Updating declarations. --- src/opt/ufar/UfarPth.cpp | 8 ++++---- src/opt/untk/NtkNtk.cpp | 8 ++++---- src/opt/util/util.cpp | 40 ++++++++++++++-------------------------- 3 files changed, 22 insertions(+), 34 deletions(-) mode change 100755 => 100644 src/opt/util/util.cpp diff --git a/src/opt/ufar/UfarPth.cpp b/src/opt/ufar/UfarPth.cpp index 085614275..6df45657f 100755 --- a/src/opt/ufar/UfarPth.cpp +++ b/src/opt/ufar/UfarPth.cpp @@ -11,15 +11,15 @@ #include "opt/untk/NtkNtk.h" #include "UfarPth.h" -ABC_NAMESPACE_IMPL_START - -extern "C" { +ABC_NAMESPACE_HEADER_START Abc_Ntk_t * Abc_NtkFromAigPhase( Aig_Man_t * pAig ); int Abc_NtkDarBmc3( Abc_Ntk_t * pAbcNtk, Saig_ParBmc_t * pBmcPars, int fOrDecomp ); Wla_Man_t * Wla_ManStart( Wlc_Ntk_t * pNtk, Wlc_Par_t * pPars ); void Wla_ManStop( Wla_Man_t * pWla ); int Wla_ManSolve( Wla_Man_t * pWla, Wlc_Par_t * pPars ); -} +ABC_NAMESPACE_HEADER_END + +ABC_NAMESPACE_IMPL_START static volatile int g_nRunIds = 0; // the number of the last prover instance int Ufar_CallBackToStop( int RunId ) { assert( RunId <= g_nRunIds ); return RunId < g_nRunIds; } diff --git a/src/opt/untk/NtkNtk.cpp b/src/opt/untk/NtkNtk.cpp index ada25c2b2..0ee5e5fd7 100755 --- a/src/opt/untk/NtkNtk.cpp +++ b/src/opt/untk/NtkNtk.cpp @@ -20,9 +20,7 @@ #include "NtkNtk.h" #include "Netlist.h" -ABC_NAMESPACE_IMPL_START - -extern "C" { +ABC_NAMESPACE_HEADER_START Abc_Ntk_t * Abc_NtkFromAigPhase( Aig_Man_t * pMan ); int Abc_NtkDarPdr( Abc_Ntk_t * pNtk, Pdr_Par_t * pPars ); int Abc_NtkDarBmc3( Abc_Ntk_t * pNtk, Saig_ParBmc_t * pPars, int fOrDecomp ); @@ -36,7 +34,9 @@ extern "C" { int Wla_ManSolve( Wla_Man_t * pWla, Wlc_Par_t * pPars ); Gia_Man_t * Wlc_NtkBitBlast2( Wlc_Ntk_t * p, Vec_Int_t * vBoxIds ); //void Gia_ManPrintStats( Gia_Man_t * p, Gps_Par_t * pPars ); -} +ABC_NAMESPACE_HEADER_END + +ABC_NAMESPACE_IMPL_START using namespace std; diff --git a/src/opt/util/util.cpp b/src/opt/util/util.cpp old mode 100755 new mode 100644 index f20588466..47101c36b --- a/src/opt/util/util.cpp +++ b/src/opt/util/util.cpp @@ -12,6 +12,17 @@ #include "misc/util/abc_namespaces.h" #include "util.h" +#ifdef __linux__ +#include +#elif defined(__APPLE__) +#include +#include +#include +#include +#include +#include +#endif + ABC_NAMESPACE_IMPL_START using namespace std; @@ -50,14 +61,8 @@ void OptMgr::PrintUsage() { } } -ABC_NAMESPACE_IMPL_END - #ifdef __linux__ -#include - -ABC_NAMESPACE_IMPL_START - void kill_on_parent_death(int sig) { // kill process if parent dies @@ -71,21 +76,8 @@ void kill_on_parent_death(int sig) } } -ABC_NAMESPACE_IMPL_END - #elif defined(__APPLE__) -#include - -#include -#include - -#include -#include -#include - -ABC_NAMESPACE_IMPL_START - template static auto retry_eintr(Func &&fn) -> decltype(fn()) { @@ -115,7 +107,7 @@ void kill_on_parent_death(int sig) kevent(kq, &change, 1, &event, 1, &ts); // however, if ppid died before the call to kevent, ppid might not be the pid of the parent - // in that case, the process it would be adopted by init, whose pid is 1 + // in that case, it would be adopted by init, whose pid is 1 if( getppid() == 1 ) { raise(sig); @@ -132,16 +124,12 @@ void kill_on_parent_death(int sig) monitor_thread.detach(); } -ABC_NAMESPACE_IMPL_END - #else // neither linux or OS X -ABC_NAMESPACE_IMPL_START - void kill_on_parent_death(int sig) { } -ABC_NAMESPACE_IMPL_END - #endif + +ABC_NAMESPACE_IMPL_END From 99ca3e442822b5ed8dc860a1aabc32dc3d9d52fc Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 21:49:23 -0800 Subject: [PATCH 28/36] Commenting out troublesome code. --- src/opt/util/util.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/opt/util/util.cpp b/src/opt/util/util.cpp index 47101c36b..a1846d72b 100644 --- a/src/opt/util/util.cpp +++ b/src/opt/util/util.cpp @@ -76,6 +76,8 @@ void kill_on_parent_death(int sig) } } +/* + #elif defined(__APPLE__) template @@ -123,6 +125,8 @@ void kill_on_parent_death(int sig) monitor_thread.detach(); } + +*/ #else // neither linux or OS X From 8d42237589c14b8e5451d4e5b0c14c5837be425f Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 22:26:01 -0800 Subject: [PATCH 29/36] Fixing continues. --- src/misc/util/abc_global.h | 4 ++-- src/misc/util/utilDouble.h | 2 ++ src/misc/util/utilTruth.h | 3 +++ src/misc/vec/vecMem.h | 3 +-- src/opt/ufar/UfarMgr.cpp | 3 ++- src/opt/ufar/UfarMgr.h | 2 +- 6 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/misc/util/abc_global.h b/src/misc/util/abc_global.h index b666fca01..5e1651d2e 100644 --- a/src/misc/util/abc_global.h +++ b/src/misc/util/abc_global.h @@ -292,8 +292,8 @@ static inline int Abc_Base2LogW( word n ) { int r; if ( n < static inline int Abc_Base10LogW( word n ) { int r; if ( n < 2 ) return (int)n; for ( r = 0, n--; n; n /= 10, r++ ) {}; return r; } static inline int Abc_Base16LogW( word n ) { int r; if ( n < 2 ) return (int)n; for ( r = 0, n--; n; n /= 16, r++ ) {}; return r; } static inline char * Abc_UtilStrsav( char * s ) { return s ? strcpy(ABC_ALLOC(char, strlen(s)+1), s) : NULL; } -static inline char * Abc_UtilStrsavTwo( char * s, char * a ){ char * r; if (!a) return Abc_UtilStrsav(s); r = ABC_ALLOC(char, strlen(s)+strlen(a)+1); sprintf(r, "%s%s", s, a ); return r; } -static inline char * Abc_UtilStrsavNum( char * s, int n ) { char * r; if (!s) return NULL; r = ABC_ALLOC(char, strlen(s)+12+1); sprintf(r, "%s%d", s, n ); return r; } +static inline char * Abc_UtilStrsavTwo( char * s, char * a ){ char * r; if (!a) return Abc_UtilStrsav(s); r = ABC_ALLOC(char, strlen(s)+strlen(a)+1); snprintf(r, strlen(s)+strlen(a)+1, "%s%s", s, a ); return r; } +static inline char * Abc_UtilStrsavNum( char * s, int n ) { char * r; if (!s) return NULL; r = ABC_ALLOC(char, strlen(s)+12+1); snprintf(r, strlen(s)+12+1, "%s%d", s, n ); return r; } static inline int Abc_BitByteNum( int nBits ) { return (nBits>>3) + ((nBits&7) > 0); } static inline int Abc_BitWordNum( int nBits ) { return (nBits>>5) + ((nBits&31) > 0); } static inline int Abc_Bit6WordNum( int nBits ) { return (nBits>>6) + ((nBits&63) > 0); } diff --git a/src/misc/util/utilDouble.h b/src/misc/util/utilDouble.h index 0d0237818..406ddf5c7 100644 --- a/src/misc/util/utilDouble.h +++ b/src/misc/util/utilDouble.h @@ -183,6 +183,8 @@ static inline void Xdbl_Test() xdbl ten100_ = ABC_CONST(0x014c924d692ca61b); assert( ten100 == ten100_ ); + (void)ten100; + (void)ten100_; // float f1 = Xdbl_ToDouble(c1); // Extra_PrintBinary( stdout, (int *)&c1, 32 ); printf( "\n" ); diff --git a/src/misc/util/utilTruth.h b/src/misc/util/utilTruth.h index 2d0422401..2d5282f96 100644 --- a/src/misc/util/utilTruth.h +++ b/src/misc/util/utilTruth.h @@ -1595,6 +1595,7 @@ static inline int Abc_TtReadBin( word * pWords, int nWords, char * pString ) { int i, Len = (int)strlen(pString), nWords2 = (Len+63)/64; assert( nWords2 <= nWords ); + (void)nWords2; memset( pWords, 0, sizeof(word)*nWords ); for ( i = 0; i < Len; i++ ) if ( pString[i] == '1' ) @@ -1608,6 +1609,7 @@ static inline word Abc_TtReadBin64( char * pString ) word Word = 0; int Len = (int)strlen(pString); assert( Len <= 64 ); + (void)Len; int Res = Abc_TtReadBin( &Word, 1, pString ); if ( Res == 0 ) { printf( "Reading binary string \"%s\" has failed.\n", pString ); @@ -1842,6 +1844,7 @@ static inline int Abc_TtCheckCondDep( word * pTruth, int nVars, int nSuppLim ) word Cof0[128], Cof1[128]; // pow( 2, nVarsMax-6 ) int v, d, nWords = Abc_TtWordNum(nVars); assert( nVars <= nVarsMax ); + (void)nVarsMax; if ( nVars <= nSuppLim + 1 ) return 0; for ( v = 0; v < nVars; v++ ) diff --git a/src/misc/vec/vecMem.h b/src/misc/vec/vecMem.h index eedd14f22..115478e9b 100644 --- a/src/misc/vec/vecMem.h +++ b/src/misc/vec/vecMem.h @@ -458,7 +458,7 @@ static inline void Vec_MemDumpTruthTables( Vec_Mem_t * p, char * pName, int nLut { FILE * pFile; char pFileName[1000]; - sprintf( pFileName, "tt_%s_%02d.txt", pName ? pName : NULL, nLutSize ); + snprintf( pFileName, sizeof(pFileName), "tt_%s_%02d.txt", pName ? pName : "", nLutSize ); pFile = pName ? fopen( pFileName, "wb" ) : stdout; Vec_MemDump( pFile, p ); if ( pFile != stdout ) @@ -475,4 +475,3 @@ ABC_NAMESPACE_HEADER_END //////////////////////////////////////////////////////////////////////// /// END OF FILE /// //////////////////////////////////////////////////////////////////////// - diff --git a/src/opt/ufar/UfarMgr.cpp b/src/opt/ufar/UfarMgr.cpp index 373e1e322..ea565b5e4 100755 --- a/src/opt/ufar/UfarMgr.cpp +++ b/src/opt/ufar/UfarMgr.cpp @@ -525,6 +525,7 @@ Vec_Int_t * SimUifPairFinder::_collect_sim_nodes(Wlc_Ntk_t * pNtk) { int i; Wlc_Obj_t * pPo; int range = Wlc_ObjRange(Wlc_NtkPo(pNtk, 1)); + (void)range; Wlc_NtkForEachPo(pNtk, pPo, i) { if ((i%3)==0) continue; assert(range == Wlc_ObjRange(pPo)); @@ -1419,7 +1420,7 @@ int UfarManager::PerformUIFProve(const timeval& timer) { auto print_stat = [&]() { gettimeofday(&curTime, NULL); elapsedTime = elapsed_time_usec(timer, curTime)/1000000.0; - sprintf(buffer, "Iteration[%2u][%3u]: %4lu White boxes\t%4lu UIF constraints (time = %.4f)", n_iter_wb, n_iter_uif, count(_vec_op_blackbox_marks.begin(), _vec_op_blackbox_marks.end(), false), _set_uif_pairs.size(), elapsedTime); + snprintf(buffer, sizeof(buffer), "Iteration[%2u][%3u]: %4lu White boxes\t%4lu UIF constraints (time = %.4f)", n_iter_wb, n_iter_uif, count(_vec_op_blackbox_marks.begin(), _vec_op_blackbox_marks.end(), false), _set_uif_pairs.size(), elapsedTime); LOG(1) << buffer << _get_profile_uf_wb(); dump_grey_stat(); if(!params.fileStatesOut.empty()) _dump_states(params.fileStatesOut); diff --git a/src/opt/ufar/UfarMgr.h b/src/opt/ufar/UfarMgr.h index d094db4e0..dad82f6a2 100755 --- a/src/opt/ufar/UfarMgr.h +++ b/src/opt/ufar/UfarMgr.h @@ -35,7 +35,7 @@ using IntPair = std::pair; struct OperatorID; struct UifPair; -struct Greyness; +class Greyness; using UIF_PAIR = UifPair; class SimUifPairFinder; From 8b79876f17c147729c794bf04e1d48c242b8daf8 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 22:37:14 -0800 Subject: [PATCH 30/36] Fixing continues. --- src/opt/util/util.cpp | 3 +-- src/opt/util/util.h | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/opt/util/util.cpp b/src/opt/util/util.cpp index a1846d72b..d5f698337 100644 --- a/src/opt/util/util.cpp +++ b/src/opt/util/util.cpp @@ -9,7 +9,6 @@ #include #include -#include "misc/util/abc_namespaces.h" #include "util.h" #ifdef __linux__ @@ -125,7 +124,7 @@ void kill_on_parent_death(int sig) monitor_thread.detach(); } - + */ #else // neither linux or OS X diff --git a/src/opt/util/util.h b/src/opt/util/util.h index b2b3a508c..24dd153a1 100755 --- a/src/opt/util/util.h +++ b/src/opt/util/util.h @@ -18,6 +18,8 @@ #include +#include "misc/util/abc_namespaces.h" + ABC_NAMESPACE_CXX_HEADER_START class LogT { From 28cc76119c97cb4b9642d2badc188228bbbba223 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Wed, 24 Dec 2025 22:45:37 -0800 Subject: [PATCH 31/36] Fixing continues. --- src/opt/untk/module.make | 5 +---- src/opt/util/module.make | 3 +-- src/opt/util/util.cpp | 8 ++------ 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/opt/untk/module.make b/src/opt/untk/module.make index 35a9eac9f..25af35c41 100755 --- a/src/opt/untk/module.make +++ b/src/opt/untk/module.make @@ -1,6 +1,3 @@ SRC += src/opt/untk/NtkCmd.cpp \ - src/opt/untk/NtkCmd.h \ src/opt/untk/Netlist.cpp \ - src/opt/untk/Netlist.h \ - src/opt/untk/NtkNtk.cpp \ - src/opt/untk/NtkNtk.h + src/opt/untk/NtkNtk.cpp \ No newline at end of file diff --git a/src/opt/util/module.make b/src/opt/util/module.make index 45e46c251..f939ff08c 100755 --- a/src/opt/util/module.make +++ b/src/opt/util/module.make @@ -1,2 +1 @@ -SRC += src/opt/util/util.cpp \ - src/opt/util/util.h +SRC += src/opt/util/util.cpp diff --git a/src/opt/util/util.cpp b/src/opt/util/util.cpp index d5f698337..df5de2fd8 100644 --- a/src/opt/util/util.cpp +++ b/src/opt/util/util.cpp @@ -9,8 +9,6 @@ #include #include -#include "util.h" - #ifdef __linux__ #include #elif defined(__APPLE__) @@ -22,6 +20,8 @@ #include #endif +#include "util.h" + ABC_NAMESPACE_IMPL_START using namespace std; @@ -75,8 +75,6 @@ void kill_on_parent_death(int sig) } } -/* - #elif defined(__APPLE__) template @@ -125,8 +123,6 @@ void kill_on_parent_death(int sig) monitor_thread.detach(); } -*/ - #else // neither linux or OS X void kill_on_parent_death(int sig) From 0ff43a13cbab43181cfff37b4a38d1de281084c4 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Thu, 25 Dec 2025 11:13:38 -0800 Subject: [PATCH 32/36] Command "aigsim". --- src/base/abci/abc.c | 64 ++++ src/misc/util/module.make | 3 +- src/misc/util/utilAigSim.c | 657 +++++++++++++++++++++++++++++++++++++ 3 files changed, 723 insertions(+), 1 deletion(-) create mode 100644 src/misc/util/utilAigSim.c diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 4016ff3d8..1ab933ad8 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -278,6 +278,7 @@ static int Abc_CommandSimSec ( Abc_Frame_t * pAbc, int argc, cha static int Abc_CommandMatch ( Abc_Frame_t * pAbc, int argc, char ** argv ); //static int Abc_CommandHaig ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandQbf ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandAigSim ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandFraig ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandFraigTrust ( Abc_Frame_t * pAbc, int argc, char ** argv ); @@ -1107,6 +1108,7 @@ void Abc_Init( Abc_Frame_t * pAbc ) Cmd_CommandAdd( pAbc, "New AIG", "csweep", Abc_CommandCSweep, 1 ); // Cmd_CommandAdd( pAbc, "New AIG", "haig", Abc_CommandHaig, 1 ); Cmd_CommandAdd( pAbc, "New AIG", "qbf", Abc_CommandQbf, 0 ); + Cmd_CommandAdd( pAbc, "New AIG", "aigsim", Abc_CommandAigSim, 0 ); Cmd_CommandAdd( pAbc, "Fraiging", "fraig", Abc_CommandFraig, 1 ); Cmd_CommandAdd( pAbc, "Fraiging", "fraig_trust", Abc_CommandFraigTrust, 1 ); @@ -19269,6 +19271,68 @@ usage: return 1; } + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandAigSim( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + extern int SimulateAigTop( char *fname1, char *fname2, char * mask, int verbose ); + char * pMask = NULL; + char ** pArgvNew = NULL; + int nArgcNew = 0; + int c, fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "Mvh" ) ) != EOF ) + { + switch ( c ) + { + case 'M': + if ( globalUtilOptind >= argc ) + { + Abc_Print( -1, "Command line switch \"-M\" should be followed by a string.\n" ); + goto usage; + } + pMask = argv[globalUtilOptind]; + globalUtilOptind++; + break; + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + pArgvNew = argv + globalUtilOptind; + nArgcNew = argc - globalUtilOptind; + if ( nArgcNew != 2 ) { + Abc_Print( -1, "Expecting two files names on the command line.\n" ); + return 1; + } + SimulateAigTop( pArgvNew[0], pArgvNew[1], pMask, fVerbose ); + return 0; + +usage: + Abc_Print( -2, "usage: aigsim -M [-vh] \n" ); + Abc_Print( -2, "\t combinational AIG simulation\n" ); + Abc_Print( -2, "\t-M : mask to select inputs for simulation [default = unused]\n" ); + Abc_Print( -2, "\t-v : toggle verbose output [default = %s]\n", fVerbose? "yes": "no" ); + Abc_Print( -2, "\t-h : print the command usage\n"); + Abc_Print( -2, "\t : the first file to simulate\n"); + Abc_Print( -2, "\t : the second file to simulate\n"); + return 1; +} + /**Function************************************************************* Synopsis [] diff --git a/src/misc/util/module.make b/src/misc/util/module.make index e77877881..701c1c251 100644 --- a/src/misc/util/module.make +++ b/src/misc/util/module.make @@ -1,4 +1,5 @@ -SRC += src/misc/util/utilBridge.c \ +SRC += src/misc/util/utilAigSim.c \ + src/misc/util/utilBridge.c \ src/misc/util/utilBipart.c \ src/misc/util/utilBSet.c \ src/misc/util/utilCex.c \ diff --git a/src/misc/util/utilAigSim.c b/src/misc/util/utilAigSim.c new file mode 100644 index 000000000..36deb1f0a --- /dev/null +++ b/src/misc/util/utilAigSim.c @@ -0,0 +1,657 @@ +/**CFile**************************************************************** + + FileName [utilAigSim.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [AIG simulation.] + + Synopsis [AIG simulation.] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - Feburary 13, 2011.] + + Revision [$Id: utilAigSim.c,v 1.00 2011/02/11 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include // mkstemp(), close(), unlink() + +#define AIGSIM_LIBRARY_ONLY + +#ifdef AIGSIM_LIBRARY_ONLY +#include "misc/util/abc_namespaces.h" +#endif + +ABC_NAMESPACE_IMPL_START + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +#define ABC_ALLOC(type, n) ((type*)malloc((size_t)(n) * sizeof(type))) +#define ABC_CALLOC(type, n) ((type*)calloc((size_t)(n), sizeof(type))) +#define ABC_FREE(p) do { free(p); (p) = NULL; } while (0) + +typedef struct AigNode_ { uint32_t f0, f1; } AigNode; +typedef struct AigMan_ { + AigNode *nodes; + int nObjs, nCis, nAnds, nCos; +} AigMan; + +static inline int Aig_LitId(uint32_t lit) { return (int)(lit >> 1); } +static inline int Aig_LitCompl(uint32_t lit) { return (int)(lit & 1u); } +static inline uint32_t Aig_Lit(int id, int c) { return ((uint32_t)id << 1) | (uint32_t)(c & 1); } + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +static AigMan* Aig_ManStartCompact(int nCis, int nAnds, int nCos) { + AigMan *p = ABC_CALLOC(AigMan, 1); + p->nCis = nCis; p->nAnds = nAnds; p->nCos = nCos; + p->nObjs = 1 + nCis + nAnds + nCos; + p->nodes = ABC_CALLOC(AigNode, p->nObjs); + return p; +} +static void Aig_ManStop(AigMan *p) { ABC_FREE(p->nodes); ABC_FREE(p); } + +/* ---------------------- AIGER binary loader ----------------------- */ +/* Simple combinational (.aig) reader: header + O ASCII lines + A delta-encoded ANDs */ + +static int read_line(FILE *f, char *buf, size_t cap) { + if (!fgets(buf, (int)cap, f)) return 0; + size_t n = strlen(buf); + while (n && (buf[n-1] == '\n' || buf[n-1] == '\r')) buf[--n] = 0; + return 1; +} +static int parse_header(const char *s, unsigned *M, unsigned *I, unsigned *L, unsigned *O, unsigned *A) { + if (strncmp(s, "aig ", 4) != 0) return 0; + const char *p = s + 4; + *M = (unsigned)strtoul(p, (char**)&p, 10); + *I = (unsigned)strtoul(p, (char**)&p, 10); + *L = (unsigned)strtoul(p, (char**)&p, 10); + *O = (unsigned)strtoul(p, (char**)&p, 10); + *A = (unsigned)strtoul(p, (char**)&p, 10); + return 1; +} +static int read_ascii_uint_line(FILE *f, unsigned *out) { + char buf[128]; if (!read_line(f, buf, sizeof(buf))) return 0; + char *p = buf; while (*p && isspace((unsigned char)*p)) p++; + *out = (unsigned)strtoul(p, NULL, 10); + return 1; +} +static int read_uleb128(FILE *f, unsigned *out) { + unsigned x = 0, shift = 0; + for (;;) { + int ch = fgetc(f); if (ch == EOF) return 0; + x |= ((unsigned)(ch & 0x7F)) << shift; + if (!(ch & 0x80)) break; + shift += 7; if (shift > 28) return 0; + } + *out = x; return 1; +} +static AigMan* Aig_ManLoadAigerBinary(const char *filename, int verbose) { + FILE *f = fopen(filename, "rb"); + if (!f) { fprintf(stderr, "Error: cannot open '%s'\n", filename); return NULL; } + char hdr[256]; + if (!read_line(f, hdr, sizeof(hdr))) { fprintf(stderr, "Error: cannot read header\n"); fclose(f); return NULL; } + unsigned M=0,I=0,L=0,O=0,A=0; + if (!parse_header(hdr, &M,&I,&L,&O,&A)) { fprintf(stderr, "Error: bad header: %s\n", hdr); fclose(f); return NULL; } + if (verbose) printf("[aiger] %s : M=%u I=%u L=%u O=%u A=%u\n", filename, M,I,L,O,A); + if (L != 0) { fprintf(stderr, "Error: latches (L=%u) not supported.\n", L); fclose(f); return NULL; } + + unsigned *outs = ABC_ALLOC(unsigned, O); + for (unsigned i = 0; i < O; i++) if (!read_ascii_uint_line(f, &outs[i])) { + fprintf(stderr, "Error: cannot read output literal %u\n", i); ABC_FREE(outs); fclose(f); return NULL; + } + + AigMan *p = Aig_ManStartCompact((int)I, (int)A, (int)O); + + for (unsigned k = 0; k < A; k++) { + unsigned lhs_var = I + k + 1; + unsigned lhs_lit = lhs_var << 1; + unsigned d1=0, d2=0; + if (!read_uleb128(f, &d1) || !read_uleb128(f, &d2)) { + fprintf(stderr, "Error: cannot decode AND gate %u\n", k); + ABC_FREE(outs); Aig_ManStop(p); fclose(f); return NULL; + } + unsigned rhs0 = lhs_lit - d1; + unsigned rhs1 = rhs0 - d2; + p->nodes[(int)lhs_var].f0 = (uint32_t)rhs0; + p->nodes[(int)lhs_var].f1 = (uint32_t)rhs1; + } + + for (unsigned i = 0; i < O; i++) { + int id = p->nCis + p->nAnds + 1 + (int)i; // CO objects at the end + p->nodes[id].f0 = (uint32_t)outs[i]; + } + + ABC_FREE(outs); + fclose(f); + return p; +} + +/* ---------------------- sim helpers ---------------------- */ + +static inline uint64_t u64_mask_n(int nBits) { + return (nBits >= 64) ? ~0ull : ((nBits <= 0) ? 0ull : ((1ull << nBits) - 1ull)); +} +static void u128_to_dec(unsigned __int128 x, char *buf, size_t cap) { + char tmp[64]; int n = 0; + if (!x) { snprintf(buf, cap, "0"); return; } + while (x && n < (int)sizeof(tmp)) { tmp[n++] = (char)('0' + (unsigned)(x % 10)); x /= 10; } + int k = 0; while (n && k + 1 < (int)cap) buf[k++] = tmp[--n]; + buf[k] = 0; +} + +enum { NW = 64, BATCH = NW * 64 }; // 4096 patterns/round + +static inline uint64_t SimLit(const uint64_t *S, uint32_t lit, int w) { + uint64_t v = S[(size_t)Aig_LitId(lit) * NW + (size_t)w]; + return Aig_LitCompl(lit) ? ~v : v; +} + +static inline void SimulateOne(const AigMan *p, uint64_t *S) { + const int firstAnd = p->nCis + 1; + const int lastAnd = p->nCis + p->nAnds; + for (int id = firstAnd; id <= lastAnd; ++id) { + uint32_t f0 = p->nodes[id].f0, f1 = p->nodes[id].f1; + size_t off = (size_t)id * NW; + for (int w = 0; w < NW; ++w) + S[off + (size_t)w] = SimLit(S, f0, w) & SimLit(S, f1, w); + } +} + +/* ---------------------- mask string parser ---------------------- */ +/* maskStr grammar: sequence of tokens N or (N) + N : N individual bits (each enumerated independently) + (N) : N bits grouped together (enumerated as all-0 or all-1) + Total widths must sum to nCis. + Output: varMasks[0..nVars-1] (each is a bitmask of input bits controlled by that var). +*/ + +static void PrintMaskSegmentsOneLine(const char *maskStr, + const int *segS, const int *segW, const int *segG, int nSeg, + int nVars, int nCis) +{ + char buf[1024]; + int n = 0; + n += snprintf(buf + n, sizeof(buf) - (size_t)n, "[mask] \"%s\" segs:", maskStr ? maskStr : ""); + for (int i = 0; i < nSeg; ++i) { + int s = segS[i], e = s + segW[i] - 1; + if (segG[i]) n += snprintf(buf + n, sizeof(buf) - (size_t)n, " (%d-%d)", s, e); + else n += snprintf(buf + n, sizeof(buf) - (size_t)n, " %d-%d", s, e); + } + n += snprintf(buf + n, sizeof(buf) - (size_t)n, " vars=%d nCis=%d", nVars, nCis); + printf("%s\n", buf); +} + +static int ParseMaskString(const char *maskStr, int nCis, uint64_t *varMasks, int *pnVars, int verbose) { + if (!maskStr || !*maskStr) { + for (int i = 0; i < nCis; ++i) varMasks[i] = 1ull << i; + *pnVars = nCis; + if (verbose) PrintMaskSegmentsOneLine("default", + (int[]){0}, (int[]){nCis}, (int[]){0}, 1, *pnVars, nCis); + return 1; + } + + // For printing segments: each token becomes one segment + int segS[128], segW[128], segG[128], nSeg = 0; + + const char *p = maskStr; + int off = 0, nVars = 0; + + while (*p) { + while (*p && isspace((unsigned char)*p)) p++; + if (!*p) break; + + int grouped = 0; + if (*p == '(') { grouped = 1; p++; while (*p && isspace((unsigned char)*p)) p++; } + + if (!isdigit((unsigned char)*p)) { + fprintf(stderr, "Error: bad mask near '%s'\n", p); + return 0; + } + + char *end = NULL; + long w = strtol(p, &end, 10); + if (w <= 0 || w > 64) { + fprintf(stderr, "Error: bad width %ld in mask\n", w); + return 0; + } + p = end; + + while (*p && isspace((unsigned char)*p)) p++; + if (grouped) { + if (*p != ')') { fprintf(stderr, "Error: missing ')' in mask\n"); return 0; } + p++; + } + + if (off + (int)w > nCis) { + fprintf(stderr, "Error: mask widths exceed nCis (%d)\n", nCis); + return 0; + } + + segS[nSeg] = off; + segW[nSeg] = (int)w; + segG[nSeg] = grouped; + nSeg++; + + if (grouped) { + uint64_t m = ((w == 64) ? ~0ull : (((1ull << (unsigned)w) - 1ull) << (unsigned)off)); + if (nVars >= 64) { fprintf(stderr, "Error: too many vars in mask\n"); return 0; } + varMasks[nVars++] = m; + } else { + for (int t = 0; t < (int)w; ++t) { + if (nVars >= 64) { fprintf(stderr, "Error: too many vars in mask\n"); return 0; } + varMasks[nVars++] = 1ull << (unsigned)(off + t); + } + } + off += (int)w; + } + + if (off != nCis) { + fprintf(stderr, "Error: mask widths sum to %d but nCis=%d\n", off, nCis); + return 0; + } + + *pnVars = nVars; + if (verbose) PrintMaskSegmentsOneLine(maskStr, segS, segW, segG, nSeg, nVars, nCis); + return 1; +} + +/* ---------------------- file/binary helpers ---------------------- */ + +static int ends_with(const char *s, const char *suf) { + size_t ns = strlen(s), nf = strlen(suf); + return (ns >= nf) && !strcmp(s + (ns - nf), suf); +} + +static int make_tmp_file(char *path, size_t cap, const char *prefix) { + // Creates an existing temp file (for input) + snprintf(path, cap, "/tmp/%sXXXXXX", prefix); + int fd = mkstemp(path); + if (fd < 0) return 0; + close(fd); + return 1; +} + +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) + snprintf(path, cap, "/tmp/%sXXXXXX", prefix); + int fd = mkstemp(path); + if (fd < 0) return 0; + close(fd); + unlink(path); + return 1; +} + +static int write_words(const char *fname, const uint64_t *data, size_t nWords) { + FILE *f = fopen(fname, "wb"); + if (!f) return 0; + size_t wr = fwrite(data, sizeof(uint64_t), nWords, f); + fclose(f); + return wr == nWords; +} + +static int read_words(const char *fname, uint64_t *data, size_t nWords) { + FILE *f = fopen(fname, "rb"); + if (!f) return 0; + size_t rd = fread(data, sizeof(uint64_t), nWords, f); + fclose(f); + return rd == nWords; +} + +/* ---------------------- compare: AIG vs AIG ---------------------- */ + +static int SimulateCompareAigAig(const AigMan *p1, const AigMan *p2, + const uint64_t *varMasks, int nVars, int verbose) +{ + if (p1->nCis != p2->nCis || p1->nCos != p2->nCos) { + fprintf(stderr, "Error: interface mismatch: (I,O)=(%d,%d) vs (%d,%d)\n", + p1->nCis, p1->nCos, p2->nCis, p2->nCos); + return 0; + } + if (p1->nCis > 64 || p1->nCos > 64) { + fprintf(stderr, "Error: supports nCis<=64 and nCos<=64 (got I=%d O=%d)\n", p1->nCis, p1->nCos); + return 0; + } + + const uint64_t inMask = u64_mask_n(p1->nCis); + const uint64_t outMask = u64_mask_n(p1->nCos); + const unsigned __int128 combs = ((unsigned __int128)1) << (unsigned)nVars; + + uint32_t *co1 = ABC_ALLOC(uint32_t, p1->nCos); + uint32_t *co2 = ABC_ALLOC(uint32_t, p2->nCos); + for (int o = 0; o < p1->nCos; ++o) { + co1[o] = p1->nodes[p1->nCis + p1->nAnds + 1 + o].f0; + co2[o] = p2->nodes[p2->nCis + p2->nAnds + 1 + o].f0; + } + + uint64_t *S1 = ABC_CALLOC(uint64_t, (size_t)p1->nObjs * NW); + uint64_t *S2 = ABC_CALLOC(uint64_t, (size_t)p2->nObjs * NW); + + uint64_t inVec[BATCH], valid[NW]; + unsigned long long rounds = 0; + unsigned __int128 patsDone = 0; + + clock_t t0 = clock(); + + for (unsigned __int128 base = 0; base < combs; base += BATCH) { + unsigned __int128 remain = combs - base; + int nThis = (remain < BATCH) ? (int)remain : BATCH; + + int left = nThis; + for (int w = 0; w < NW; ++w) { + if (left >= 64) { valid[w] = ~0ull; left -= 64; } + else if (left > 0) { valid[w] = ((left == 64) ? ~0ull : ((1ull << left) - 1ull)); left = 0; } + else valid[w] = 0ull; + } + + // Clear CI words in both sims + for (int i = 0; i < p1->nCis; ++i) { + size_t off1 = (size_t)(1 + i) * NW; + size_t off2 = (size_t)(1 + i) * NW; + for (int w = 0; w < NW; ++w) S1[off1 + (size_t)w] = 0ull, S2[off2 + (size_t)w] = 0ull; + } + + // Fill CIs + for (int ptn = 0; ptn < nThis; ++ptn) { + uint64_t idx = (uint64_t)(base + (unsigned)ptn); + uint64_t in = 0; + for (int j = 0; j < nVars; ++j) if ((idx >> j) & 1ull) in |= varMasks[j]; + in &= inMask; + inVec[ptn] = in; + + int w = ptn >> 6; + uint64_t bit = 1ull << (ptn & 63); + uint64_t x = in; + while (x) { + int i = __builtin_ctzll(x); + x &= x - 1; + S1[(size_t)(1 + i) * NW + (size_t)w] |= bit; + S2[(size_t)(1 + i) * NW + (size_t)w] |= bit; + } + } + + // Simulate AIG #1 then AIG #2 + SimulateOne(p1, S1); + SimulateOne(p2, S2); + + // Compare COs + for (int o = 0; o < p1->nCos; ++o) { + for (int w = 0; w < NW; ++w) { + uint64_t y1 = SimLit(S1, co1[o], w); + uint64_t y2 = SimLit(S2, co2[o], w); + uint64_t diff = (y1 ^ y2) & valid[w]; + if (!diff) continue; + + int bit = __builtin_ctzll(diff); + int ptn = (w << 6) | bit; + + uint64_t out1 = 0, out2 = 0; + for (int oo = 0; oo < p1->nCos; ++oo) { + out1 |= ((SimLit(S1, co1[oo], w) >> bit) & 1ull) << oo; + out2 |= ((SimLit(S2, co2[oo], w) >> bit) & 1ull) << oo; + } + + int inHex = (p1->nCis + 3) / 4; + int outHex = (p1->nCos + 3) / 4; + char gbuf[64]; u128_to_dec(patsDone + (unsigned)ptn, gbuf, sizeof(gbuf)); + + printf("FAIL pattern=%s out_bit=%d\n", gbuf, o); + printf(" in = 0x%0*llx\n", inHex, (unsigned long long)(inVec[ptn] & inMask)); + printf(" y1 = 0x%0*llx\n", outHex, (unsigned long long)(out1 & outMask)); + printf(" y2 = 0x%0*llx\n", outHex, (unsigned long long)(out2 & outMask)); + + ABC_FREE(S1); ABC_FREE(S2); ABC_FREE(co1); ABC_FREE(co2); + return 0; + } + } + + rounds++; + patsDone += (unsigned)nThis; + if (verbose && (rounds % 256ull) == 0ull) { + char buf[64]; u128_to_dec(patsDone, buf, sizeof(buf)); + printf("[sim] rounds=%llu patterns=%s\n", rounds, buf); + } + } + + clock_t t1 = clock(); + double sec = (double)(t1 - t0) / (double)CLOCKS_PER_SEC; + + char totBuf[64]; u128_to_dec(combs, totBuf, sizeof(totBuf)); + printf("OK patterns=%s rounds=%llu time=%.3fs\n", totBuf, rounds, sec); + + ABC_FREE(S1); ABC_FREE(S2); ABC_FREE(co1); ABC_FREE(co2); + return 1; +} + +/* ---------------------- compare: AIG vs external binary ---------------------- */ + +static int SimulateCompareAigBin(const AigMan *p1, const char *bin, + const uint64_t *varMasks, int nVars, int verbose) +{ + if (p1->nCis > 64 || p1->nCos > 64) { + fprintf(stderr, "Error: supports nCis<=64 and nCos<=64 (got I=%d O=%d)\n", p1->nCis, p1->nCos); + return 0; + } + + const uint64_t inMask = u64_mask_n(p1->nCis); + const uint64_t outMask = u64_mask_n(p1->nCos); + const unsigned __int128 combs = ((unsigned __int128)1) << (unsigned)nVars; + + // Precompute AIG CO literals + uint32_t *co1 = ABC_ALLOC(uint32_t, p1->nCos); + for (int o = 0; o < p1->nCos; ++o) + co1[o] = p1->nodes[p1->nCis + p1->nAnds + 1 + o].f0; + + uint64_t *S1 = ABC_CALLOC(uint64_t, (size_t)p1->nObjs * NW); + uint64_t *out2 = ABC_ALLOC(uint64_t, (size_t)p1->nCos * NW); + + // Temp IO files for the binary + char inFile[128], outFile[128], cmd[512]; + if (!make_tmp_file(inFile, sizeof(inFile), "aigsim_in_") || + !make_tmp_path_noexist(outFile, sizeof(outFile), "aigsim_out_")) { + fprintf(stderr, "Error: cannot create temp files\n"); + ABC_FREE(S1); ABC_FREE(co1); ABC_FREE(out2); + return 0; + } + + uint64_t inVec[BATCH], valid[NW]; + unsigned long long rounds = 0; + unsigned __int128 patsDone = 0; + + clock_t t0 = clock(); + + for (unsigned __int128 base = 0; base < combs; base += BATCH) { + unsigned __int128 remain = combs - base; + int nThis = (remain < BATCH) ? (int)remain : BATCH; + + int left = nThis; + for (int w = 0; w < NW; ++w) { + if (left >= 64) { valid[w] = ~0ull; left -= 64; } + else if (left > 0) { valid[w] = ((left == 64) ? ~0ull : ((1ull << left) - 1ull)); left = 0; } + else valid[w] = 0ull; + } + + // Clear CI words + for (int i = 0; i < p1->nCis; ++i) { + size_t off = (size_t)(1 + i) * NW; + for (int w = 0; w < NW; ++w) S1[off + (size_t)w] = 0ull; + } + + // Fill CIs for this batch + for (int ptn = 0; ptn < nThis; ++ptn) { + uint64_t idx = (uint64_t)(base + (unsigned)ptn); + uint64_t in = 0; + for (int j = 0; j < nVars; ++j) if ((idx >> j) & 1ull) in |= varMasks[j]; + in &= inMask; + inVec[ptn] = in; + + int w = ptn >> 6; + uint64_t bit = 1ull << (ptn & 63); + + uint64_t x = in; + while (x) { + int i = __builtin_ctzll(x); + x &= x - 1; + S1[(size_t)(1 + i) * NW + (size_t)w] |= bit; + } + } + + // Simulate AIG + SimulateOne(p1, S1); + + // Write input sim-info file: nCis * NW words, CI major, word minor + if (!write_words(inFile, &S1[1 * NW], (size_t)p1->nCis * NW)) { + fprintf(stderr, "Error: cannot write %s\n", inFile); + goto fail; + } + + // Run external binary: " " + remove(outFile); + snprintf(cmd, sizeof(cmd), "%s %s %s", bin, inFile, outFile); + int rc = system(cmd); + if (rc != 0) { + fprintf(stderr, "Error: system() failed (rc=%d): %s\n", rc, cmd); + goto fail; + } + + // Read output sim-info file: nCos * NW words + if (!read_words(outFile, out2, (size_t)p1->nCos * NW)) { + fprintf(stderr, "Error: cannot read %s (expected %d*%d words)\n", outFile, p1->nCos, NW); + goto fail; + } + + // Compare AIG outputs vs binary outputs + for (int o = 0; o < p1->nCos; ++o) { + for (int w = 0; w < NW; ++w) { + uint64_t y1 = SimLit(S1, co1[o], w); + uint64_t y2 = out2[(size_t)o * NW + (size_t)w]; + uint64_t diff = (y1 ^ y2) & valid[w]; + if (!diff) continue; + + int bit = __builtin_ctzll(diff); + int ptn = (w << 6) | bit; + + uint64_t out1 = 0, outB = 0; + for (int oo = 0; oo < p1->nCos; ++oo) { + out1 |= ((SimLit(S1, co1[oo], w) >> bit) & 1ull) << oo; + outB |= ((out2[(size_t)oo * NW + (size_t)w] >> bit) & 1ull) << oo; + } + + int inHex = (p1->nCis + 3) / 4; + int outHex = (p1->nCos + 3) / 4; + char gbuf[64]; u128_to_dec(patsDone + (unsigned)ptn, gbuf, sizeof(gbuf)); + + printf("FAIL pattern=%s out_bit=%d\n", gbuf, o); + printf(" in = 0x%0*llx\n", inHex, (unsigned long long)(inVec[ptn] & inMask)); + printf(" aig = 0x%0*llx\n", outHex, (unsigned long long)(out1 & outMask)); + printf(" bin = 0x%0*llx\n", outHex, (unsigned long long)(outB & outMask)); + goto fail; + } + } + + rounds++; + patsDone += (unsigned)nThis; + if (verbose && (rounds % 256ull) == 0ull) { + char buf[64]; u128_to_dec(patsDone, buf, sizeof(buf)); + printf("[sim] rounds=%llu patterns=%s\n", rounds, buf); + } + } + + { + clock_t t1 = clock(); + double sec = (double)(t1 - t0) / (double)CLOCKS_PER_SEC; + char totBuf[64]; u128_to_dec(combs, totBuf, sizeof(totBuf)); + printf("OK patterns=%s rounds=%llu time=%.3fs\n", totBuf, rounds, sec); + } + + remove(inFile); + remove(outFile); + ABC_FREE(S1); ABC_FREE(co1); ABC_FREE(out2); + return 1; + +fail: + remove(inFile); + remove(outFile); + ABC_FREE(S1); ABC_FREE(co1); ABC_FREE(out2); + return 0; +} + +/* ---------------------- top API ---------------------- */ + +int SimulateAigTop( char *fname1, char *fname2, char *mask, int verbose ) +{ + AigMan *p1 = Aig_ManLoadAigerBinary(fname1, verbose); + if (!p1) return 2; + + uint64_t varMasks[64]; + int nVars = 0; + if (!ParseMaskString(mask, p1->nCis, varMasks, &nVars, verbose)) { + Aig_ManStop(p1); + return 2; + } + + int ok = 0; + + if (ends_with(fname2, ".aig")) { + AigMan *p2 = Aig_ManLoadAigerBinary(fname2, verbose); + if (!p2) { Aig_ManStop(p1); return 2; } + ok = SimulateCompareAigAig(p1, p2, varMasks, nVars, verbose); + Aig_ManStop(p2); + } else { + // fname2 is an external binary: bin + ok = SimulateCompareAigBin(p1, fname2, varMasks, nVars, verbose); + } + + Aig_ManStop(p1); + return ok ? 0 : 3; +} + +/* ---------------------- main ---------------------- */ +/* Usage: + ./aig_equiv_2aigs [-v] file1.aig file2.aig_or_binary [mask_str] + + mask_str example for 16 inputs: + "2(4)10" -> 2 individual bits, then 4 grouped bits (all-0/all-1), then 10 individual bits +*/ +#ifndef AIGSIM_LIBRARY_ONLY +int main(int argc, char **argv) { + int verbose = 0; + const char *f1 = NULL, *f2 = NULL; + const char *mask = NULL; + + int ai = 1; + if (ai < argc && !strcmp(argv[ai], "-v")) { verbose = 1; ai++; } + if (ai + 1 >= argc) { + fprintf(stderr, "Usage: %s [-v] file1.aig file2.aig_or_binary [mask_str]\n", argv[0]); + return 1; + } + f1 = argv[ai++]; + f2 = argv[ai++]; + if (ai < argc) mask = argv[ai++]; + + return SimulateAigTop((char*)f1, (char*)f2, (char*)mask, verbose); +} +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + + +ABC_NAMESPACE_IMPL_END + From 8d3ba7bd7d0b1f9d3db3fde0090c2fd665a38705 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Thu, 25 Dec 2025 11:22:11 -0800 Subject: [PATCH 33/36] Compiler problem. --- src/misc/util/utilAigSim.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/misc/util/utilAigSim.c b/src/misc/util/utilAigSim.c index 36deb1f0a..a4dcdd1e0 100644 --- a/src/misc/util/utilAigSim.c +++ b/src/misc/util/utilAigSim.c @@ -201,8 +201,12 @@ static int ParseMaskString(const char *maskStr, int nCis, uint64_t *varMasks, in if (!maskStr || !*maskStr) { for (int i = 0; i < nCis; ++i) varMasks[i] = 1ull << i; *pnVars = nCis; - if (verbose) PrintMaskSegmentsOneLine("default", - (int[]){0}, (int[]){nCis}, (int[]){0}, 1, *pnVars, nCis); + if (verbose) { + int segS[1] = {0}; + int segW[1] = {nCis}; + int segG[1] = {0}; + PrintMaskSegmentsOneLine("default", segS, segW, segG, 1, *pnVars, nCis); + } return 1; } From 291e0a2c8330fce86f54ded00360c8c14efb661c Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Thu, 25 Dec 2025 13:16:37 -0800 Subject: [PATCH 34/36] Updating &write_ver. --- src/aig/gia/giaMan.c | 136 +++++-- src/base/abc/abcHieGia.c | 742 ++++++++++++++++++++++++++++++++++++++- src/base/abci/abc.c | 49 ++- 3 files changed, 886 insertions(+), 41 deletions(-) diff --git a/src/aig/gia/giaMan.c b/src/aig/gia/giaMan.c index 84a41d1dd..b60874601 100644 --- a/src/aig/gia/giaMan.c +++ b/src/aig/gia/giaMan.c @@ -1866,6 +1866,72 @@ void Gia_ManDumpIoList( Gia_Man_t * p, FILE * pFile, int fOuts, int fReverse ) Vec_IntFree( vArray ); } } +static Vec_Bit_t * Gia_ManCollectMultiBits( Vec_Ptr_t * vNames, int n ) +{ + Vec_Bit_t * vBits = Vec_BitStart( n ); + if ( n == 0 ) + return vBits; + if ( vNames == NULL ) + { + if ( n > 1 ) + { + int i; + for ( i = 0; i < n; i++ ) + Vec_BitWriteEntry( vBits, i, 1 ); + } + return vBits; + } + { + Vec_Int_t * vArray = Gia_ManCountSymbsAll( vNames ); + int iName, Size, i; + int nNames = Vec_PtrSize( vNames ); + Vec_IntForEachEntryDouble( vArray, iName, Size, i ) + { + int iNameNext = Vec_IntSize(vArray) > i+2 ? Vec_IntEntry(vArray, i+2) : nNames; + int k; + if ( iNameNext - iName <= 1 ) + continue; + for ( k = iName; k < iNameNext && k < n; k++ ) + Vec_BitWriteEntry( vBits, k, 1 ); + } + Vec_IntFree( vArray ); + } + return vBits; +} +static int Gia_ManDumpIoListMulti( Gia_Man_t * p, FILE * pFile, int fOuts, int fReverse ) +{ + Vec_Ptr_t * vNames = fOuts ? p->vNamesOut : p->vNamesIn; + int nNames = vNames ? Vec_PtrSize(vNames) : (fOuts ? Gia_ManCoNum(p) : Gia_ManCiNum(p)); + if ( vNames == NULL ) + { + if ( nNames > 1 ) + { + fprintf( pFile, "_%c_", fOuts ? 'o' : 'i' ); + return 1; + } + return 0; + } + { + Vec_Int_t * vArray = Gia_ManCountSymbsAll( vNames ); + int nGroups = Vec_IntSize(vArray) / 2; + int idx, fFirst = 1; + for ( idx = 0; idx < nGroups; idx++ ) + { + int g = fReverse ? (nGroups - 1 - idx) : idx; + int iName = Vec_IntEntry(vArray, 2*g); + int Size = Vec_IntEntry(vArray, 2*g + 1); + int iNameNext = (g + 1 < nGroups) ? Vec_IntEntry(vArray, 2*(g + 1)) : nNames; + if ( iNameNext - iName <= 1 ) + continue; + if ( !fFirst ) + fprintf( pFile, ", " ); + Gia_ManPrintOneName( pFile, (char *)Vec_PtrEntry(vNames, iName), Size ); + fFirst = 0; + } + Vec_IntFree( vArray ); + return !fFirst; + } +} void Gia_ManDumpIoRanges( Gia_Man_t * p, FILE * pFile, int fOuts ) { Vec_Ptr_t * vNames = fOuts ? p->vNamesOut : p->vNamesIn; @@ -1943,17 +2009,30 @@ void Gia_ManDumpInterface( Gia_Man_t * p, char * pFileName ) Gia_ManWriteNames( pFile, 'z', Gia_ManPoNum(p), p->vNamesOut, 9, 4, NULL, 0 ); fprintf( pFile, ";\n\n" ); - fprintf( pFile, " assign { " ); - Gia_ManWriteNames( pFile, 'x', Gia_ManCiNum(p), p->vNamesIn, 8, 4, NULL, 1 ); - fprintf( pFile, " } = { " ); - Gia_ManDumpIoList( p, pFile, 0, 1 ); - fprintf( pFile, " };\n\n" ); - - fprintf( pFile, " assign { " ); - Gia_ManDumpIoList( p, pFile, 1, 1 ); - fprintf( pFile, " } = { " ); - Gia_ManWriteNames( pFile, 'z', Gia_ManCoNum(p), p->vNamesOut, 9, 4, NULL, 1 ); - fprintf( pFile, " };\n\n" ); + { + Vec_Bit_t * vMultiIn = Gia_ManCollectMultiBits( p->vNamesIn, Gia_ManCiNum(p) ); + Vec_Bit_t * vMultiOut = Gia_ManCollectMultiBits( p->vNamesOut, Gia_ManCoNum(p) ); + int fHasMultiIn = Vec_BitCount( vMultiIn ); + int fHasMultiOut = Vec_BitCount( vMultiOut ); + if ( fHasMultiIn ) + { + fprintf( pFile, " assign { " ); + Gia_ManWriteNames( pFile, 'x', Gia_ManCiNum(p), p->vNamesIn, 8, 4, vMultiIn, 1 ); + fprintf( pFile, " } = { " ); + Gia_ManDumpIoListMulti( p, pFile, 0, 1 ); + fprintf( pFile, " };\n\n" ); + } + if ( fHasMultiOut ) + { + fprintf( pFile, " assign { " ); + Gia_ManDumpIoListMulti( p, pFile, 1, 1 ); + fprintf( pFile, " } = { " ); + Gia_ManWriteNames( pFile, 'z', Gia_ManCoNum(p), p->vNamesOut, 9, 4, vMultiOut, 1 ); + fprintf( pFile, " };\n\n" ); + } + Vec_BitFree( vMultiIn ); + Vec_BitFree( vMultiOut ); + } if ( Vec_BitCount(vUsed) ) { @@ -2054,17 +2133,30 @@ void Gia_ManDumpInterfaceAssign( Gia_Man_t * p, char * pFileName ) Gia_ManWriteNames( pFile, 'z', Gia_ManPoNum(p), p->vNamesOut, 9, 4, NULL, 0 ); fprintf( pFile, ";\n\n" ); - fprintf( pFile, " assign { " ); - Gia_ManWriteNames( pFile, 'x', Gia_ManCiNum(p), p->vNamesIn, 8, 4, NULL, 1 ); - fprintf( pFile, " } = { " ); - Gia_ManDumpIoList( p, pFile, 0, 1 ); - fprintf( pFile, " };\n\n" ); - - fprintf( pFile, " assign { " ); - Gia_ManDumpIoList( p, pFile, 1, 1 ); - fprintf( pFile, " } = { " ); - Gia_ManWriteNames( pFile, 'z', Gia_ManCoNum(p), p->vNamesOut, 9, 4, NULL, 1 ); - fprintf( pFile, " };\n\n" ); + { + Vec_Bit_t * vMultiIn = Gia_ManCollectMultiBits( p->vNamesIn, Gia_ManCiNum(p) ); + Vec_Bit_t * vMultiOut = Gia_ManCollectMultiBits( p->vNamesOut, Gia_ManCoNum(p) ); + int fHasMultiIn = Vec_BitCount( vMultiIn ); + int fHasMultiOut = Vec_BitCount( vMultiOut ); + if ( fHasMultiIn ) + { + fprintf( pFile, " assign { " ); + Gia_ManWriteNames( pFile, 'x', Gia_ManCiNum(p), p->vNamesIn, 8, 4, vMultiIn, 1 ); + fprintf( pFile, " } = { " ); + Gia_ManDumpIoListMulti( p, pFile, 0, 1 ); + fprintf( pFile, " };\n\n" ); + } + if ( fHasMultiOut ) + { + fprintf( pFile, " assign { " ); + Gia_ManDumpIoListMulti( p, pFile, 1, 1 ); + fprintf( pFile, " } = { " ); + Gia_ManWriteNames( pFile, 'z', Gia_ManCoNum(p), p->vNamesOut, 9, 4, vMultiOut, 1 ); + fprintf( pFile, " };\n\n" ); + } + Vec_BitFree( vMultiIn ); + Vec_BitFree( vMultiOut ); + } if ( Vec_BitCount(vUsed) ) { diff --git a/src/base/abc/abcHieGia.c b/src/base/abc/abcHieGia.c index ca30459dc..239f73ef1 100644 --- a/src/base/abc/abcHieGia.c +++ b/src/base/abc/abcHieGia.c @@ -19,6 +19,7 @@ ***********************************************************************/ #include "abc.h" +#include ABC_NAMESPACE_IMPL_START @@ -573,10 +574,749 @@ void Abc_NtkInsertHierarchyGia( Abc_Ntk_t * pNtk, Abc_Ntk_t * pNew, int fVerbose Gia_ManInsertOne( pModel, pNew ); } +static Vec_Bit_t * GiaHie_GenUsed( Gia_Man_t * p, int fBuf ) +{ + Gia_Obj_t * pObj; int i; + Vec_Bit_t * vUsed = Vec_BitStart( Gia_ManObjNum(p) ); + Gia_ManForEachAnd( p, pObj, i ) + { + if ( fBuf ) + Vec_BitWriteEntry( vUsed, i, 1 ); + if ( Gia_ObjFaninC0(pObj) ^ fBuf ) + Vec_BitWriteEntry( vUsed, Gia_ObjFaninId0(pObj, i), 1 ); + if ( Gia_ObjFaninC1(pObj) ^ fBuf ) + Vec_BitWriteEntry( vUsed, Gia_ObjFaninId1(pObj, i), 1 ); + } + Gia_ManForEachCo( p, pObj, i ) + if ( Gia_ObjFaninC0(pObj) ^ fBuf ) + Vec_BitWriteEntry( vUsed, Gia_ObjFaninId0p(p, pObj), 1 ); + Vec_BitWriteEntry( vUsed, 0, 0 ); // clean zero + return vUsed; +} +static int GiaHie_NameIsLegalInVerilog( char * pName ) +{ + // identifier ::= simple_identifier | escaped_identifier + // simple_identifier ::= [a-zA-Z_][a-zA-Z0-9_$] + // escaped_identifier ::= \ {Any_ASCII_character_except_white_space} white_space + // white_space ::= space | tab | newline + assert( pName != NULL && *pName != '\0' ); + if ( *pName == '\\' ) + return 1; + if ( (*pName < 'a' || *pName > 'z') && (*pName < 'A' || *pName > 'Z') && *pName != '_' ) + return 0; + while ( *(++pName) ) + if ( (*pName < 'a' || *pName > 'z') && (*pName < 'A' || *pName > 'Z') && (*pName < '0' || *pName > '9') && *pName != '_' && *pName != '$' ) + return 0; + return 1; +} +static char * GiaHie_ObjGetDumpName( Vec_Ptr_t * vNames, char c, int i, int d ) +{ + static char pBuffer[10000]; + if ( vNames ) + { + char * pName = (char *)Vec_PtrEntry(vNames, i); + if ( GiaHie_NameIsLegalInVerilog(pName) ) + sprintf( pBuffer, "%s", pName ); + else + sprintf( pBuffer, "\\%s ", pName ); + } + else + sprintf( pBuffer, "%c%0*d%c", c, d, i, c ); + return pBuffer; +} +static void GiaHie_WriteNames( FILE * pFile, char c, int n, Vec_Ptr_t * vNames, int Start, int Skip, Vec_Bit_t * vObjs, int fReverse ) +{ + int Digits = Abc_Base10Log( n ); + int Length = Start, i, fFirst = 1; + char * pName; + for ( i = 0; i < n; i++ ) + { + int k = fReverse ? n-1-i : i; + if ( vObjs && !Vec_BitEntry(vObjs, k) ) + continue; + pName = GiaHie_ObjGetDumpName( vNames, c, k, Digits ); + Length += strlen(pName) + 2; + if ( Length > 60 ) + { + fprintf( pFile, ",\n " ); + Length = Skip; + fFirst = 1; + } + fprintf( pFile, "%s%s", fFirst ? "":", ", pName ); + fFirst = 0; + } +} +static void GiaHie_PrintOneName( FILE * pFile, char * pName, int Size ) +{ + int i; + for ( i = 0; i < Size; i++ ) + fprintf( pFile, "%c", pName[i] ); +} +static int GiaHie_CountSymbs( char * pName ) +{ + int i; + for ( i = 0; pName[i]; i++ ) + if ( pName[i] == '[' ) + break; + return i; +} +static int GiaHie_ReadRangeNum( char * pName, int Size ) +{ + if ( pName[Size] == 0 ) + return -1; + assert( pName[Size] == '[' ); + return atoi(pName+Size+1); +} +static Vec_Int_t * GiaHie_CountSymbsAll( Vec_Ptr_t * vNames ) +{ + char * pNameLast = (char *)Vec_PtrEntry(vNames, 0), * pName; + int i, nSymbsLast = GiaHie_CountSymbs(pNameLast); + Vec_Int_t * vArray = Vec_IntAlloc( Vec_PtrSize(vNames) * 2 ); + Vec_IntPush( vArray, 0 ); + Vec_IntPush( vArray, nSymbsLast ); + Vec_PtrForEachEntryStart( char *, vNames, pName, i, 1 ) + { + int nSymbs = GiaHie_CountSymbs(pName); + if ( nSymbs == nSymbsLast && !strncmp(pName, pNameLast, nSymbsLast) ) + continue; + Vec_IntPush( vArray, i ); + Vec_IntPush( vArray, nSymbs ); + pNameLast = pName; + nSymbsLast = nSymbs; + } + return vArray; +} +static void GiaHie_DumpIoList( Gia_Man_t * p, FILE * pFile, int fOuts, int fReverse ) +{ + Vec_Ptr_t * vNames = fOuts ? p->vNamesOut : p->vNamesIn; + if ( vNames == NULL ) + fprintf( pFile, "_%c_", fOuts ? 'o' : 'i' ); + else + { + Vec_Int_t * vArray = GiaHie_CountSymbsAll( vNames ); + int iName, Size, i; + Vec_IntForEachEntryDouble( vArray, iName, Size, i ) + { + if ( fReverse ) + { + iName = Vec_IntEntry(vArray, Vec_IntSize(vArray)-2-i); + Size = Vec_IntEntry(vArray, Vec_IntSize(vArray)-1-i); + } + if ( i ) fprintf( pFile, ", " ); + GiaHie_PrintOneName( pFile, (char *)Vec_PtrEntry(vNames, iName), Size ); + } + Vec_IntFree( vArray ); + } +} +static Vec_Bit_t * GiaHie_CollectMultiBits( Vec_Ptr_t * vNames, int n ) +{ + Vec_Bit_t * vBits = Vec_BitStart( n ); + if ( n == 0 ) + return vBits; + if ( vNames == NULL ) + { + if ( n > 1 ) + { + int i; + for ( i = 0; i < n; i++ ) + Vec_BitWriteEntry( vBits, i, 1 ); + } + return vBits; + } + { + Vec_Int_t * vArray = GiaHie_CountSymbsAll( vNames ); + int iName, Size, i; + int nNames = Vec_PtrSize( vNames ); + Vec_IntForEachEntryDouble( vArray, iName, Size, i ) + { + int iNameNext = Vec_IntSize(vArray) > i+2 ? Vec_IntEntry(vArray, i+2) : nNames; + int k; + if ( iNameNext - iName <= 1 ) + continue; + for ( k = iName; k < iNameNext && k < n; k++ ) + Vec_BitWriteEntry( vBits, k, 1 ); + } + Vec_IntFree( vArray ); + } + return vBits; +} +static int GiaHie_DumpIoListMulti( Gia_Man_t * p, FILE * pFile, int fOuts, int fReverse ) +{ + Vec_Ptr_t * vNames = fOuts ? p->vNamesOut : p->vNamesIn; + int nNames = vNames ? Vec_PtrSize(vNames) : (fOuts ? Gia_ManCoNum(p) : Gia_ManCiNum(p)); + if ( vNames == NULL ) + { + if ( nNames > 1 ) + { + fprintf( pFile, "_%c_", fOuts ? 'o' : 'i' ); + return 1; + } + return 0; + } + { + Vec_Int_t * vArray = GiaHie_CountSymbsAll( vNames ); + int nGroups = Vec_IntSize(vArray) / 2; + int idx, fFirst = 1; + for ( idx = 0; idx < nGroups; idx++ ) + { + int g = fReverse ? (nGroups - 1 - idx) : idx; + int iName = Vec_IntEntry(vArray, 2*g); + int Size = Vec_IntEntry(vArray, 2*g + 1); + int iNameNext = (g + 1 < nGroups) ? Vec_IntEntry(vArray, 2*(g + 1)) : nNames; + if ( iNameNext - iName <= 1 ) + continue; + if ( !fFirst ) + fprintf( pFile, ", " ); + GiaHie_PrintOneName( pFile, (char *)Vec_PtrEntry(vNames, iName), Size ); + fFirst = 0; + } + Vec_IntFree( vArray ); + return !fFirst; + } +} +static void GiaHie_DumpIoRanges( Gia_Man_t * p, FILE * pFile, int fOuts ) +{ + Vec_Ptr_t * vNames = fOuts ? p->vNamesOut : p->vNamesIn; + if ( p->vNamesOut == NULL ) + fprintf( pFile, "%s [%d:0] _%c_;\n", fOuts ? "output" : "input", fOuts ? Gia_ManPoNum(p)-1 : Gia_ManPiNum(p)-1, fOuts ? 'o' : 'i' ); + else + { + Vec_Int_t * vArray = GiaHie_CountSymbsAll( vNames ); + int iName, Size, i; + Vec_IntForEachEntryDouble( vArray, iName, Size, i ) + { + int iNameNext = Vec_IntSize(vArray) > i+2 ? Vec_IntEntry(vArray, i+2) : Vec_PtrSize(vNames); + char * pName = (char *)Vec_PtrEntry(vNames, iName); + char * pNameLast = (char *)Vec_PtrEntry(vNames, iNameNext-1); + assert( !strncmp(pName, pNameLast, Size) ); + int NumBeg = GiaHie_ReadRangeNum( pName, Size ); + int NumEnd = GiaHie_ReadRangeNum( pNameLast, Size ); + fprintf( pFile, " %s ", fOuts ? "output" : "input" ); + if ( NumBeg != -1 && iName < iNameNext-1 ) + fprintf( pFile, "[%d:%d] ", NumEnd, NumBeg ); + GiaHie_PrintOneName( pFile, pName, Size ); + fprintf( pFile, ";\n" ); + } + Vec_IntFree( vArray ); + } +} +static void GiaHie_DumpModuleName( FILE * pFile, char * pName ) +{ + int i; + if ( pName == NULL || pName[0] == '\0' ) + { + fprintf( pFile, "top" ); + return; + } + if ( !isalpha(pName[0]) && pName[0] != '_' ) + fprintf( pFile, "m" ); + for ( i = 0; i < (int)strlen(pName); i++ ) + if ( isalpha(pName[i]) || isdigit(pName[i]) || pName[i] == '_' ) + fprintf( pFile, "%c", pName[i] ); + else + fprintf( pFile, "_" ); +} +static void GiaHie_DumpInterfaceGates( Gia_Man_t * p, char * pFileName ) +{ + Gia_Obj_t * pObj; + Vec_Bit_t * vInvs, * vUsed; + int nDigits = Abc_Base10Log( Gia_ManObjNum(p) ); + int nDigitsI = Abc_Base10Log( Gia_ManPiNum(p) ); + int nDigitsO = Abc_Base10Log( Gia_ManPoNum(p) ); + int i; + + FILE * pFile = fopen( pFileName, "wb" ); + if ( pFile == NULL ) + { + printf( "Cannot open output file \"%s\".\n", pFileName ); + return; + } + + vInvs = GiaHie_GenUsed( p, 0 ); + vUsed = GiaHie_GenUsed( p, 1 ); + + fprintf( pFile, "module " ); + GiaHie_DumpModuleName( pFile, p->pName ); + fprintf( pFile, " ( " ); + GiaHie_DumpIoList( p, pFile, 0, 0 ); + fprintf( pFile, ", " ); + GiaHie_DumpIoList( p, pFile, 1, 0 ); + fprintf( pFile, " );\n\n" ); + GiaHie_DumpIoRanges( p, pFile, 0 ); + GiaHie_DumpIoRanges( p, pFile, 1 ); + fprintf( pFile, "\n" ); + + fprintf( pFile, " wire " ); + GiaHie_WriteNames( pFile, 'x', Gia_ManPiNum(p), p->vNamesIn, 8, 4, NULL, 0 ); + fprintf( pFile, ";\n\n" ); + + fprintf( pFile, " wire " ); + GiaHie_WriteNames( pFile, 'z', Gia_ManPoNum(p), p->vNamesOut, 9, 4, NULL, 0 ); + fprintf( pFile, ";\n\n" ); + + { + Vec_Bit_t * vMultiIn = GiaHie_CollectMultiBits( p->vNamesIn, Gia_ManCiNum(p) ); + Vec_Bit_t * vMultiOut = GiaHie_CollectMultiBits( p->vNamesOut, Gia_ManCoNum(p) ); + int fHasMultiIn = Vec_BitCount( vMultiIn ); + int fHasMultiOut = Vec_BitCount( vMultiOut ); + if ( fHasMultiIn ) + { + fprintf( pFile, " assign { " ); + GiaHie_WriteNames( pFile, 'x', Gia_ManCiNum(p), p->vNamesIn, 8, 4, vMultiIn, 1 ); + fprintf( pFile, " } = { " ); + GiaHie_DumpIoListMulti( p, pFile, 0, 1 ); + fprintf( pFile, " };\n\n" ); + } + if ( fHasMultiOut ) + { + fprintf( pFile, " assign { " ); + GiaHie_DumpIoListMulti( p, pFile, 1, 1 ); + fprintf( pFile, " } = { " ); + GiaHie_WriteNames( pFile, 'z', Gia_ManCoNum(p), p->vNamesOut, 9, 4, vMultiOut, 1 ); + fprintf( pFile, " };\n\n" ); + } + Vec_BitFree( vMultiIn ); + Vec_BitFree( vMultiOut ); + } + + if ( Vec_BitCount(vUsed) ) + { + fprintf( pFile, " wire " ); + GiaHie_WriteNames( pFile, 'n', Gia_ManObjNum(p), NULL, 7, 4, vUsed, 0 ); + fprintf( pFile, ";\n\n" ); + } + + if ( Vec_BitCount(vInvs) ) + { + fprintf( pFile, " wire " ); + GiaHie_WriteNames( pFile, 'i', Gia_ManObjNum(p), NULL, 7, 4, vInvs, 0 ); + fprintf( pFile, ";\n\n" ); + } + + // input inverters + Gia_ManForEachCi( p, pObj, i ) + { + if ( Vec_BitEntry(vUsed, Gia_ObjId(p, pObj)) ) + { + fprintf( pFile, " buf ( %s,", GiaHie_ObjGetDumpName(NULL, 'n', Gia_ObjId(p, pObj), nDigits) ); + fprintf( pFile, " %s );\n", GiaHie_ObjGetDumpName(p->vNamesIn, 'x', i, nDigitsI) ); + } + if ( Vec_BitEntry(vInvs, Gia_ObjId(p, pObj)) ) + { + fprintf( pFile, " not ( %s,", GiaHie_ObjGetDumpName(NULL, 'i', Gia_ObjId(p, pObj), nDigits) ); + fprintf( pFile, " %s );\n", GiaHie_ObjGetDumpName(p->vNamesIn, 'x', i, nDigitsI) ); + } + } + + // internal nodes and their inverters + fprintf( pFile, "\n" ); + Gia_ManForEachAnd( p, pObj, i ) + { + fprintf( pFile, " and ( %s,", GiaHie_ObjGetDumpName(NULL, 'n', i, nDigits) ); + fprintf( pFile, " %s,", GiaHie_ObjGetDumpName(NULL, (char)(Gia_ObjFaninC0(pObj)? 'i':'n'), Gia_ObjFaninId0(pObj, i), nDigits) ); + fprintf( pFile, " %s );\n", GiaHie_ObjGetDumpName(NULL, (char)(Gia_ObjFaninC1(pObj)? 'i':'n'), Gia_ObjFaninId1(pObj, i), nDigits) ); + if ( Vec_BitEntry(vInvs, i) ) + { + fprintf( pFile, " not ( %s,", GiaHie_ObjGetDumpName(NULL, 'i', i, nDigits) ); + fprintf( pFile, " %s );\n", GiaHie_ObjGetDumpName(NULL, 'n', i, nDigits) ); + } + } + + // output drivers + fprintf( pFile, "\n" ); + Gia_ManForEachCo( p, pObj, i ) + { + fprintf( pFile, " buf ( %s, ", GiaHie_ObjGetDumpName(p->vNamesOut, 'z', i, nDigitsO) ); + if ( Gia_ObjIsConst0(Gia_ObjFanin0(pObj)) ) + fprintf( pFile, "1\'b%d );\n", Gia_ObjFaninC0(pObj) ); + else + fprintf( pFile, "%s );\n", GiaHie_ObjGetDumpName(NULL, (char)(Gia_ObjFaninC0(pObj)? 'i':'n'), Gia_ObjFaninId0p(p, pObj), nDigits) ); + } + + fprintf( pFile, "\nendmodule\n\n" ); + fclose( pFile ); + + Vec_BitFree( vInvs ); + Vec_BitFree( vUsed ); +} + +static void GiaHie_PrintObjName( FILE * pFile, int ObjId, int nDigits ) +{ + fprintf( pFile, "n%0*d", nDigits, ObjId ); +} +static void GiaHie_PrintObjLit( FILE * pFile, int ObjId, int fCompl, int nDigits ) +{ + fprintf( pFile, "%c", fCompl ? '~' : ' ' ); + GiaHie_PrintObjName( pFile, ObjId, nDigits ); +} +static void GiaHie_WriteObjRange( FILE * pFile, Gia_Man_t * p, int iStart, int iStop, int nDigits, int Start, int Skip, int fReverse, int fCis ) +{ + int Length = Start, i, fFirst = 1; + int n = iStop - iStart; + for ( i = 0; i < n; i++ ) + { + int Idx = fReverse ? (iStop - 1 - i) : (iStart + i); + int ObjId = fCis ? Gia_ManCiIdToId(p, Idx) : Gia_ManCoIdToId(p, Idx); + char pName[64]; + sprintf( pName, "n%0*d", nDigits, ObjId ); + Length += strlen(pName) + 2; + if ( Length > 100 ) + { + fprintf( pFile, ",\n " ); + Length = Skip; + fFirst = 1; + } + fprintf( pFile, "%s%s", fFirst ? "":", ", pName ); + fFirst = 0; + } +} +static void GiaHie_WritePiPoNames( FILE * pFile, const char * pPrefix, int nBits, int nDigits, int Start, int Skip, int fReverse ) +{ + int Length = Start, i, fFirst = 1; + for ( i = 0; i < nBits; i++ ) + { + int Idx = fReverse ? (nBits - 1 - i) : i; + char pName[64]; + sprintf( pName, "%s%0*d", pPrefix, nDigits, Idx ); + Length += strlen(pName) + 2; + if ( Length > 100 ) + { + fprintf( pFile, ",\n " ); + Length = Skip; + fFirst = 1; + } + fprintf( pFile, "%s%s", fFirst ? "":", ", pName ); + fFirst = 0; + } +} +static int GiaHie_IsBitLevelNames( Vec_Ptr_t * vNames ) +{ + int nNames, nGroups, idx; + Vec_Int_t * vArray; + if ( vNames == NULL ) + return 1; + nNames = Vec_PtrSize( vNames ); + if ( nNames == 0 ) + return 1; + vArray = GiaHie_CountSymbsAll( vNames ); + nGroups = Vec_IntSize(vArray) / 2; + for ( idx = 0; idx < nGroups; idx++ ) + { + int iName = Vec_IntEntry(vArray, 2*idx); + int iNameNext = (idx + 1 < nGroups) ? Vec_IntEntry(vArray, 2*(idx + 1)) : nNames; + if ( iNameNext - iName > 1 ) + { + Vec_IntFree( vArray ); + return 0; + } + } + Vec_IntFree( vArray ); + return 1; +} +static void GiaHie_DumpPortDeclsOne( Gia_Man_t * p, FILE * pFile, int fOuts, int * pfFirst ) +{ + Vec_Ptr_t * vNames = fOuts ? p->vNamesOut : p->vNamesIn; + int nBits = fOuts ? Gia_ManPoNum(p) : Gia_ManPiNum(p); + int fUsePiPo = (nBits > 2) && GiaHie_IsBitLevelNames( vNames ); + if ( nBits == 0 ) + return; + if ( fUsePiPo ) + { + int nDigits = Abc_Base10Log( nBits ); + if ( nDigits < 2 ) + nDigits = 2; + if ( !(*pfFirst) ) + fprintf( pFile, ",\n" ); + fprintf( pFile, " %s ", fOuts ? "output" : "input" ); + GiaHie_WritePiPoNames( pFile, fOuts ? "po" : "pi", nBits, nDigits, 8, 4, 0 ); + *pfFirst = 0; + return; + } + if ( vNames == NULL ) + { + if ( !(*pfFirst) ) + fprintf( pFile, ",\n" ); + fprintf( pFile, " %s [%d:0] _%c_", fOuts ? "output" : "input", nBits-1, fOuts ? 'o' : 'i' ); + *pfFirst = 0; + return; + } + { + Vec_Int_t * vArray = GiaHie_CountSymbsAll( vNames ); + int iName, Size, i; + Vec_IntForEachEntryDouble( vArray, iName, Size, i ) + { + int iNameNext = Vec_IntSize(vArray) > i+2 ? Vec_IntEntry(vArray, i+2) : Vec_PtrSize(vNames); + char * pName = (char *)Vec_PtrEntry(vNames, iName); + char * pNameLast = (char *)Vec_PtrEntry(vNames, iNameNext-1); + assert( !strncmp(pName, pNameLast, Size) ); + int NumBeg = GiaHie_ReadRangeNum( pName, Size ); + int NumEnd = GiaHie_ReadRangeNum( pNameLast, Size ); + if ( !(*pfFirst) ) + fprintf( pFile, ",\n" ); + fprintf( pFile, " %s ", fOuts ? "output" : "input" ); + if ( NumBeg != -1 && iName < iNameNext-1 ) + fprintf( pFile, "[%d:%d] ", NumEnd, NumBeg ); + GiaHie_PrintOneName( pFile, pName, Size ); + *pfFirst = 0; + } + Vec_IntFree( vArray ); + } +} +static void GiaHie_DumpPortDecls( Gia_Man_t * p, FILE * pFile ) +{ + int fFirst = 1; + GiaHie_DumpPortDeclsOne( p, pFile, 0, &fFirst ); + GiaHie_DumpPortDeclsOne( p, pFile, 1, &fFirst ); +} +static int GiaHie_ConstUsed( Gia_Man_t * p ) +{ + Gia_Obj_t * pObj; int i; + Gia_ManForEachAnd( p, pObj, i ) + if ( Gia_ObjFaninId0(pObj, i) == 0 || Gia_ObjFaninId1(pObj, i) == 0 ) + return 1; + Gia_ManForEachCo( p, pObj, i ) + if ( Gia_ObjIsConst0(Gia_ObjFanin0(pObj)) ) + return 1; + return 0; +} +static void GiaHie_DumpInputAssigns( Gia_Man_t * p, FILE * pFile, int nDigits ) +{ + Vec_Ptr_t * vNames = p->vNamesIn; + int nCis = Gia_ManCiNum(p); + int fUsePiPo = (nCis > 2) && GiaHie_IsBitLevelNames( vNames ); + if ( nCis == 0 ) + return; + if ( fUsePiPo ) + { + int nDigitsPi = Abc_Base10Log( nCis ); + if ( nDigitsPi < 2 ) + nDigitsPi = 2; + fprintf( pFile, " assign { " ); + GiaHie_WriteObjRange( pFile, p, 0, nCis, nDigits, 11, 4, 1, 1 ); + fprintf( pFile, " } =\n { " ); + GiaHie_WritePiPoNames( pFile, "pi", nCis, nDigitsPi, 18, 4, 1 ); + fprintf( pFile, " };\n" ); + return; + } + if ( vNames == NULL ) + { + if ( nCis == 1 ) + { + fprintf( pFile, " assign " ); + GiaHie_PrintObjName( pFile, Gia_ManCiIdToId(p, 0), nDigits ); + fprintf( pFile, " = _i_;\n" ); + } + else + { + fprintf( pFile, " assign { " ); + GiaHie_WriteObjRange( pFile, p, 0, nCis, nDigits, 11, 4, 1, 1 ); + fprintf( pFile, " } = { _i_ };\n" ); + } + return; + } + { + Vec_Int_t * vArray = GiaHie_CountSymbsAll( vNames ); + int iName, Size, i; + Vec_IntForEachEntryDouble( vArray, iName, Size, i ) + { + int iNameNext = Vec_IntSize(vArray) > i+2 ? Vec_IntEntry(vArray, i+2) : Vec_PtrSize(vNames); + int nBits = iNameNext - iName; + char * pName = (char *)Vec_PtrEntry(vNames, iName); + if ( nBits > 1 ) + { + fprintf( pFile, " assign { " ); + GiaHie_WriteObjRange( pFile, p, iName, iNameNext, nDigits, 11, 4, 1, 1 ); + fprintf( pFile, " } =\n { " ); + GiaHie_PrintOneName( pFile, pName, Size ); + fprintf( pFile, " };\n" ); + } + else + { + fprintf( pFile, " assign " ); + GiaHie_PrintObjName( pFile, Gia_ManCiIdToId(p, iName), nDigits ); + fprintf( pFile, " = " ); + GiaHie_PrintOneName( pFile, pName, Size ); + fprintf( pFile, ";\n" ); + } + } + Vec_IntFree( vArray ); + } +} +static void GiaHie_DumpOutputAssigns( Gia_Man_t * p, FILE * pFile, int nDigits ) +{ + Vec_Ptr_t * vNames = p->vNamesOut; + int nCos = Gia_ManCoNum(p); + int fUsePiPo = (nCos > 2) && GiaHie_IsBitLevelNames( vNames ); + if ( nCos == 0 ) + return; + if ( fUsePiPo ) + { + int nDigitsPo = Abc_Base10Log( nCos ); + if ( nDigitsPo < 2 ) + nDigitsPo = 2; + fprintf( pFile, " assign { " ); + GiaHie_WritePiPoNames( pFile, "po", nCos, nDigitsPo, 11, 4, 1 ); + fprintf( pFile, " } =\n { " ); + GiaHie_WriteObjRange( pFile, p, 0, nCos, nDigits, 18, 4, 1, 0 ); + fprintf( pFile, " };\n" ); + return; + } + if ( vNames == NULL ) + { + if ( nCos == 1 ) + { + fprintf( pFile, " assign _o_ = " ); + GiaHie_PrintObjName( pFile, Gia_ManCoIdToId(p, 0), nDigits ); + fprintf( pFile, ";\n" ); + } + else + { + fprintf( pFile, " assign { _o_ } = { " ); + GiaHie_WriteObjRange( pFile, p, 0, nCos, nDigits, 18, 4, 1, 0 ); + fprintf( pFile, " };\n" ); + } + return; + } + { + Vec_Int_t * vArray = GiaHie_CountSymbsAll( vNames ); + int iName, Size, i; + Vec_IntForEachEntryDouble( vArray, iName, Size, i ) + { + int iNameNext = Vec_IntSize(vArray) > i+2 ? Vec_IntEntry(vArray, i+2) : Vec_PtrSize(vNames); + int nBits = iNameNext - iName; + char * pName = (char *)Vec_PtrEntry(vNames, iName); + if ( nBits > 1 ) + { + fprintf( pFile, " assign { " ); + GiaHie_PrintOneName( pFile, pName, Size ); + fprintf( pFile, " } =\n { " ); + GiaHie_WriteObjRange( pFile, p, iName, iNameNext, nDigits, 18, 4, 1, 0 ); + fprintf( pFile, " };\n" ); + } + else + { + fprintf( pFile, " assign " ); + GiaHie_PrintOneName( pFile, pName, Size ); + fprintf( pFile, " = " ); + GiaHie_PrintObjName( pFile, Gia_ManCoIdToId(p, iName), nDigits ); + fprintf( pFile, ";\n" ); + } + } + Vec_IntFree( vArray ); + } +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +static void GiaHie_DumpInterfaceAssigns( Gia_Man_t * p, char * pFileName ) +{ + Gia_Obj_t * pObj; + int nDigits = Abc_Base10Log( Gia_ManObjNum(p) ); + int nPerLine = 4; + int nOnLine = 0; + int i; + FILE * pFile = fopen( pFileName, "wb" ); + if ( pFile == NULL ) + { + printf( "Cannot open output file \"%s\".\n", pFileName ); + return; + } + + fprintf( pFile, "module " ); + GiaHie_DumpModuleName( pFile, p->pName ); + fprintf( pFile, " (\n" ); + GiaHie_DumpPortDecls( p, pFile ); + fprintf( pFile, "\n);\n\n" ); + + if ( Gia_ManCiNum(p) ) + { + fprintf( pFile, " wire " ); + GiaHie_WriteObjRange( pFile, p, 0, Gia_ManCiNum(p), nDigits, 7, 4, 0, 1 ); + fprintf( pFile, ";\n\n" ); + GiaHie_DumpInputAssigns( p, pFile, nDigits ); + fprintf( pFile, "\n" ); + } + + if ( Gia_ManCoNum(p) ) + { + fprintf( pFile, " wire " ); + GiaHie_WriteObjRange( pFile, p, 0, Gia_ManCoNum(p), nDigits, 7, 4, 0, 0 ); + fprintf( pFile, ";\n\n" ); + GiaHie_DumpOutputAssigns( p, pFile, nDigits ); + fprintf( pFile, "\n" ); + } + + if ( GiaHie_ConstUsed(p) ) + fprintf( pFile, " wire n%0*d = 1'b0;\n\n", nDigits, 0 ); + + Gia_ManForEachAnd( p, pObj, i ) + { + if ( nOnLine == 0 ) + fprintf( pFile, " " ); + fprintf( pFile, "wire n%0*d = ", nDigits, i ); + GiaHie_PrintObjLit( pFile, Gia_ObjFaninId0(pObj, i), Gia_ObjFaninC0(pObj), nDigits ); + fprintf( pFile, " & " ); + GiaHie_PrintObjLit( pFile, Gia_ObjFaninId1(pObj, i), Gia_ObjFaninC1(pObj), nDigits ); + fprintf( pFile, "; " ); + nOnLine++; + if ( nOnLine == nPerLine ) + { + fprintf( pFile, "\n" ); + nOnLine = 0; + } + else + fprintf( pFile, " " ); + } + if ( nOnLine != 0 ) + fprintf( pFile, "\n" ); + if ( Gia_ManAndNum(p) ) + fprintf( pFile, "\n" ); + + Gia_ManForEachCo( p, pObj, i ) + { + fprintf( pFile, " assign n%0*d = ", nDigits, Gia_ManCoIdToId(p, i) ); + GiaHie_PrintObjLit( pFile, Gia_ObjFaninId0p(p, pObj), Gia_ObjFaninC0(pObj), nDigits ); + fprintf( pFile, ";\n" ); + } + + fprintf( pFile, "\nendmodule\n\n" ); + fclose( pFile ); +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void Gia_WriteVerilog( char * pFileName, Gia_Man_t * pGia, int fUseGates, int fVerbose ) +{ + (void)fVerbose; + if ( pFileName == NULL || pGia == NULL ) + return; + if ( fUseGates ) + GiaHie_DumpInterfaceGates( pGia, pFileName ); + else + GiaHie_DumpInterfaceAssigns( pGia, pFileName ); +} + //////////////////////////////////////////////////////////////////////// /// END OF FILE /// //////////////////////////////////////////////////////////////////////// ABC_NAMESPACE_IMPL_END - diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 1ab933ad8..b104f618a 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -35066,14 +35066,16 @@ usage: ***********************************************************************/ int Abc_CommandAbc9WriteVer( Abc_Frame_t * pAbc, int argc, char ** argv ) { + extern void Gia_WriteVerilog( char * pFileName, Gia_Man_t * pGia, int fUseGates, int fVerbose ); char * pFileSpec = NULL; Abc_Ntk_t * pNtkSpec = NULL; char * pFileName; char ** pArgvNew; int c, nArgcNew; + int fUseGates = 0; int fVerbose = 0; Extra_UtilGetoptReset(); - while ( ( c = Extra_UtilGetopt( argc, argv, "Svh" ) ) != EOF ) + while ( ( c = Extra_UtilGetopt( argc, argv, "Sgvh" ) ) != EOF ) { switch ( c ) { @@ -35086,6 +35088,9 @@ int Abc_CommandAbc9WriteVer( Abc_Frame_t * pAbc, int argc, char ** argv ) pFileSpec = argv[globalUtilOptind]; globalUtilOptind++; break; + case 'g': + fUseGates ^= 1; + break; case 'v': fVerbose ^= 1; break; @@ -35103,31 +35108,39 @@ int Abc_CommandAbc9WriteVer( Abc_Frame_t * pAbc, int argc, char ** argv ) return 1; } pFileName = argv[globalUtilOptind]; - if ( pAbc->pNtkCur == NULL ) - { - Abc_Print( -1, "There is no mapped file to write.\n" ); - return 1; - } if ( pFileSpec == NULL ) { - Abc_Print( -1, "The specification file is not given.\n" ); - return 1; + if ( pAbc->pGia == NULL ) + { + Abc_Print( -1, "There is no AIG to write.\n" ); + return 1; + } + Gia_WriteVerilog( pFileName, pAbc->pGia, fUseGates, fVerbose ); } - pNtkSpec = Io_ReadNetlist( pFileSpec, Io_ReadFileType(pFileSpec), 0 ); - if ( pNtkSpec == NULL ) + else { - Abc_Print( -1, "Reading hierarchical Verilog for the specification has failed.\n" ); - return 1; + if ( pAbc->pNtkCur == NULL ) + { + Abc_Print( -1, "There is no mapped file to write.\n" ); + return 1; + } + pNtkSpec = Io_ReadNetlist( pFileSpec, Io_ReadFileType(pFileSpec), 0 ); + if ( pNtkSpec == NULL ) + { + Abc_Print( -1, "Reading hierarchical Verilog for the specification has failed.\n" ); + return 1; + } + Abc_NtkInsertHierarchyGia( pNtkSpec, pAbc->pNtkCur, fVerbose ); + Io_WriteVerilog( pNtkSpec, pFileName, 0, 0 ); + Abc_NtkDelete( pNtkSpec ); } - Abc_NtkInsertHierarchyGia( pNtkSpec, pAbc->pNtkCur, fVerbose ); - Io_WriteVerilog( pNtkSpec, pFileName, 0, 0 ); - Abc_NtkDelete( pNtkSpec ); return 0; usage: - Abc_Print( -2, "usage: &write_ver [-S ] [-vh] \n" ); - Abc_Print( -2, "\t writes hierarchical Verilog after mapping\n" ); - Abc_Print( -2, "\t-S file : file name for the original hierarchical design (required)\n" ); + Abc_Print( -2, "usage: &write_ver [-S ] [-gvh] \n" ); + Abc_Print( -2, "\t writes hierarchical Verilog\n" ); + Abc_Print( -2, "\t-S file : file name for the original design (required when hierarchy is present)\n" ); + Abc_Print( -2, "\t-g : toggle output gates vs assign-statements [default = %s]\n", fUseGates? "gates": "assigns" ); Abc_Print( -2, "\t-v : toggle verbose output [default = %s]\n", fVerbose? "yes": "no" ); Abc_Print( -2, "\t-h : print the command usage\n"); Abc_Print( -2, "\t : the file name\n"); From 1f13c88bfd5ceee2da431ebd48a945e68a78dc59 Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Fri, 26 Dec 2025 08:08:31 -0800 Subject: [PATCH 35/36] Creating commands to match popular scripts. --- src/base/abci/abc.c | 147 ++++++++++++++++++++++++ src/base/abci/abcDar.c | 40 ++++++- src/opt/dar/darScript.c | 239 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 424 insertions(+), 2 deletions(-) diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index b104f618a..4eee95227 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -468,6 +468,9 @@ static int Abc_CommandAbc9Times ( Abc_Frame_t * pAbc, int argc, cha static int Abc_CommandAbc9Frames ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9Retime ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9Enable ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandAbc9Resyn3 ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandAbc9Resyn3rs ( Abc_Frame_t * pAbc, int argc, char ** argv ); +static int Abc_CommandAbc9Compress3rs ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9Dc2 ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9Dsd ( Abc_Frame_t * pAbc, int argc, char ** argv ); static int Abc_CommandAbc9Bidec ( Abc_Frame_t * pAbc, int argc, char ** argv ); @@ -1299,6 +1302,9 @@ void Abc_Init( Abc_Frame_t * pAbc ) Cmd_CommandAdd( pAbc, "ABC9", "&frames", Abc_CommandAbc9Frames, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&retime", Abc_CommandAbc9Retime, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&enable", Abc_CommandAbc9Enable, 0 ); + Cmd_CommandAdd( pAbc, "ABC9", "&resyn3", Abc_CommandAbc9Resyn3, 0 ); + Cmd_CommandAdd( pAbc, "ABC9", "&resyn3rs", Abc_CommandAbc9Resyn3rs, 0 ); + Cmd_CommandAdd( pAbc, "ABC9", "&compress3rs", Abc_CommandAbc9Compress3rs, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&dc2", Abc_CommandAbc9Dc2, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&dsd", Abc_CommandAbc9Dsd, 0 ); Cmd_CommandAdd( pAbc, "ABC9", "&bidec", Abc_CommandAbc9Bidec, 0 ); @@ -39197,6 +39203,147 @@ usage: return 1; } +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandAbc9Resyn3( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + extern Gia_Man_t * Gia_ManResyn3( Gia_Man_t * pGia, int fVerbose ); + Gia_Man_t * pTemp; + int c, fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "vh" ) ) != EOF ) + { + switch ( c ) + { + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pAbc->pGia == NULL ) + { + Abc_Print( -1, "Abc_CommandAbc9Resyn3(): There is no AIG.\n" ); + return 1; + } + pTemp = Gia_ManResyn3( pAbc->pGia, fVerbose ); + Abc_FrameUpdateGia( pAbc, pTemp ); + return 0; + +usage: + Abc_Print( -2, "usage: &resyn3 [-vh]\n" ); + Abc_Print( -2, "\t performs rewriting of the AIG while preserving logic level\n" ); + Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); + Abc_Print( -2, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandAbc9Resyn3rs( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + extern Gia_Man_t * Gia_ManCompress3rs( Gia_Man_t * pGia, int fUpdateLevel, int fVerbose ); + Gia_Man_t * pTemp; + int c, fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "vh" ) ) != EOF ) + { + switch ( c ) + { + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pAbc->pGia == NULL ) + { + Abc_Print( -1, "Abc_CommandAbc9Resyn3rs(): There is no AIG.\n" ); + return 1; + } + pTemp = Gia_ManCompress3rs( pAbc->pGia, 1, fVerbose ); + Abc_FrameUpdateGia( pAbc, pTemp ); + return 0; + +usage: + Abc_Print( -2, "usage: &resyn3rs [-vh]\n" ); + Abc_Print( -2, "\t performs rewriting of the AIG while preserving logic level\n" ); + Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); + Abc_Print( -2, "\t-h : print the command usage\n"); + return 1; +} + +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int Abc_CommandAbc9Compress3rs( Abc_Frame_t * pAbc, int argc, char ** argv ) +{ + extern Gia_Man_t * Gia_ManCompress3rs( Gia_Man_t * pGia, int fUpdateLevel, int fVerbose ); + Gia_Man_t * pTemp; + int c, fVerbose = 0; + Extra_UtilGetoptReset(); + while ( ( c = Extra_UtilGetopt( argc, argv, "vh" ) ) != EOF ) + { + switch ( c ) + { + case 'v': + fVerbose ^= 1; + break; + case 'h': + goto usage; + default: + goto usage; + } + } + if ( pAbc->pGia == NULL ) + { + Abc_Print( -1, "Abc_CommandAbc9Compress3rs(): There is no AIG.\n" ); + return 1; + } + pTemp = Gia_ManCompress3rs( pAbc->pGia, 0, fVerbose ); + Abc_FrameUpdateGia( pAbc, pTemp ); + return 0; + +usage: + Abc_Print( -2, "usage: &compress3rs [-vh]\n" ); + Abc_Print( -2, "\t performs rewriting of the AIG without preserving logic level\n" ); + Abc_Print( -2, "\t-v : toggle printing verbose information [default = %s]\n", fVerbose? "yes": "no" ); + Abc_Print( -2, "\t-h : print the command usage\n"); + return 1; +} + /**Function************************************************************* Synopsis [] diff --git a/src/base/abci/abcDar.c b/src/base/abci/abcDar.c index e7874b0ea..6b0322361 100644 --- a/src/base/abci/abcDar.c +++ b/src/base/abci/abcDar.c @@ -662,6 +662,45 @@ Abc_Ntk_t * Abc_NtkFromAigPhase( Aig_Man_t * pMan ) } +/**Function************************************************************* + + Synopsis [] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Aig_Man_t * Dar_ManResub( Aig_Man_t * pMan, int nCutsMax, int nNodesMax, int fUpdateLevel, int fUseZeros, int fVerbose ) +{ + extern int Abc_NtkResubstitute( Abc_Ntk_t * pNtk, int nCutMax, int nStepsMax, int nMinSaved, int nLevelsOdc, int fUpdateLevel, int fVerbose, int fVeryVerbose, int Log2Probs, int Log2Divs ); + Abc_Ntk_t * pNtk = Abc_NtkFromAigPhase( pMan ); + Aig_Man_t * pRes = NULL; + char * pName = NULL, * pSpec = NULL; + int nMinSaved = fUseZeros ? 0 : 1; + if ( pMan->pName ) + pName = Abc_UtilStrsav( pMan->pName ); + if ( pMan->pSpec ) + pSpec = Abc_UtilStrsav( pMan->pSpec ); + if ( pName ) + { + ABC_FREE( pNtk->pName ); + pNtk->pName = pName; + } + if ( pSpec ) + { + ABC_FREE( pNtk->pSpec ); + pNtk->pSpec = pSpec; + } + if ( !Abc_NtkResubstitute( pNtk, nCutsMax, nNodesMax, nMinSaved, 0, fUpdateLevel, fVerbose, 0, 0, 0 ) ) + Abc_Print( 0, "Dar_ManResub(): Resubstitution has failed.\n" ); + pRes = Abc_NtkToDar( pNtk, 0, 1 ); + Abc_NtkDelete( pNtk ); + return pRes; +} + /**Function************************************************************* Synopsis [] @@ -5181,4 +5220,3 @@ Gia_Man_t * Abc_NtkDarTestFiles() #include "abcDarUnfold2.c" ABC_NAMESPACE_IMPL_END - diff --git a/src/opt/dar/darScript.c b/src/opt/dar/darScript.c index 8cb30bddb..9bb1965f9 100644 --- a/src/opt/dar/darScript.c +++ b/src/opt/dar/darScript.c @@ -907,6 +907,244 @@ pPars->timeSynth = Abc_Clock() - clk; return pMan; } +/**Function************************************************************* + + Synopsis [Reproduces script "resyn2".] + + Description [Equivalent to: b; rw; rf; b; rw; rwz; b; rfz; rwz; b.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Gia_Man_t * Gia_ManResyn3( Gia_Man_t * pGia, int fVerbose ) +{ + Gia_Man_t * pGiaRes; + Aig_Man_t * pAig, * pTemp; + Dar_RwrPar_t ParsRwr, * pParsRwr = &ParsRwr; + Dar_RefPar_t ParsRef, * pParsRef = &ParsRef; + int fUpdateLevel = 1; + + if ( pGia->pManTime && pGia->vLevels == NULL ) + Gia_ManLevelWithBoxes( pGia ); + + Dar_ManDefaultRwrParams( pParsRwr ); + Dar_ManDefaultRefParams( pParsRef ); + pParsRwr->nCutsMax = 250; + pParsRef->nLeafMax = 10; + pParsRef->nMffcMin = 1; + pParsRwr->fUpdateLevel = fUpdateLevel; + pParsRef->fUpdateLevel = fUpdateLevel; + pParsRwr->fVerbose = 0; + pParsRef->fVerbose = 0; + + pAig = Gia_ManToAig( pGia, 0 ); + if ( fVerbose ) printf( "Starting: " ), Aig_ManPrintStats( pAig ); + + // b + pAig = Dar_ManBalance( pTemp = pAig, fUpdateLevel ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Balance: " ), Aig_ManPrintStats( pAig ); + + // rw + Dar_ManRewrite( pAig, pParsRwr ); + pAig = Aig_ManDupDfs( pTemp = pAig ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Rewrite: " ), Aig_ManPrintStats( pAig ); + + // rf + Dar_ManRefactor( pAig, pParsRef ); + pAig = Aig_ManDupDfs( pTemp = pAig ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Refactor: " ), Aig_ManPrintStats( pAig ); + + // b + pAig = Dar_ManBalance( pTemp = pAig, fUpdateLevel ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Balance: " ), Aig_ManPrintStats( pAig ); + + // rw + Dar_ManRewrite( pAig, pParsRwr ); + pAig = Aig_ManDupDfs( pTemp = pAig ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Rewrite: " ), Aig_ManPrintStats( pAig ); + + // rwz + pParsRwr->fUseZeros = 1; + pParsRef->fUseZeros = 1; + Dar_ManRewrite( pAig, pParsRwr ); + pAig = Aig_ManDupDfs( pTemp = pAig ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "RewriteZ: " ), Aig_ManPrintStats( pAig ); + + // b + pAig = Dar_ManBalance( pTemp = pAig, fUpdateLevel ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Balance: " ), Aig_ManPrintStats( pAig ); + + // rfz + Dar_ManRefactor( pAig, pParsRef ); + pAig = Aig_ManDupDfs( pTemp = pAig ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "RefactorZ: " ), Aig_ManPrintStats( pAig ); + + // rwz + Dar_ManRewrite( pAig, pParsRwr ); + pAig = Aig_ManDupDfs( pTemp = pAig ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "RewriteZ: " ), Aig_ManPrintStats( pAig ); + + // b + pAig = Dar_ManBalance( pTemp = pAig, fUpdateLevel ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Balance: " ), Aig_ManPrintStats( pAig ); + + pGiaRes = Gia_ManFromAig( pAig ); + Aig_ManStop( pAig ); + Gia_ManTransferTiming( pGiaRes, pGia ); + return pGiaRes; +} + +/**Function************************************************************* + + Synopsis [Reproduces script "compress2rs".] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Gia_Man_t * Gia_ManCompress3rs( Gia_Man_t * pGia, int fUpdateLevel, int fVerbose ) +{ + Gia_Man_t * pGiaRes; + Aig_Man_t * pAig, * pTemp; + Dar_RwrPar_t ParsRwr, * pParsRwr = &ParsRwr; + Dar_RefPar_t ParsRef, * pParsRef = &ParsRef; + extern Aig_Man_t * Dar_ManResub( Aig_Man_t * pAig, int nCutsMax, int nNodesMax, int fUpdateLevel, int fUseZeros, int fVerbose ); + + if ( pGia->pManTime && pGia->vLevels == NULL ) + Gia_ManLevelWithBoxes( pGia ); + + Dar_ManDefaultRwrParams( pParsRwr ); + Dar_ManDefaultRefParams( pParsRef ); + pParsRwr->nCutsMax = 250; + pParsRef->nLeafMax = 10; + pParsRef->nMffcMin = 1; + pParsRwr->fUpdateLevel = fUpdateLevel; + pParsRef->fUpdateLevel = fUpdateLevel; + pParsRwr->fVerbose = 0; + pParsRef->fVerbose = 0; + + pAig = Gia_ManToAig( pGia, 0 ); + if ( fVerbose ) printf( "Starting: " ), Aig_ManPrintStats( pAig ); + + // b -l + pAig = Dar_ManBalance( pTemp = pAig, fUpdateLevel ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Balance: " ), Aig_ManPrintStats( pAig ); + + // rs -K 6 -l + pAig = Dar_ManResub( pTemp = pAig, 6, 1, fUpdateLevel, 0, 0 ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Resub6: " ), Aig_ManPrintStats( pAig ); + + // rw -l + Dar_ManRewrite( pAig, pParsRwr ); + pAig = Aig_ManDupDfs( pTemp = pAig ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Rewrite: " ), Aig_ManPrintStats( pAig ); + + // rs -K 6 -N 2 -l + pAig = Dar_ManResub( pTemp = pAig, 6, 2, fUpdateLevel, 0, 0 ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Resub6x2: " ), Aig_ManPrintStats( pAig ); + + // rf -l + Dar_ManRefactor( pAig, pParsRef ); + pAig = Aig_ManDupDfs( pTemp = pAig ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Refactor: " ), Aig_ManPrintStats( pAig ); + + // rs -K 8 -l + pAig = Dar_ManResub( pTemp = pAig, 8, 1, fUpdateLevel, 0, 0 ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Resub8: " ), Aig_ManPrintStats( pAig ); + + // b -l + pAig = Dar_ManBalance( pTemp = pAig, fUpdateLevel ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Balance: " ), Aig_ManPrintStats( pAig ); + + // rs -K 8 -N 2 -l + pAig = Dar_ManResub( pTemp = pAig, 8, 2, fUpdateLevel, 0, 0 ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Resub8x2: " ), Aig_ManPrintStats( pAig ); + + // rw -l + Dar_ManRewrite( pAig, pParsRwr ); + pAig = Aig_ManDupDfs( pTemp = pAig ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Rewrite: " ), Aig_ManPrintStats( pAig ); + + // rs -K 10 -l + pAig = Dar_ManResub( pTemp = pAig, 10, 1, fUpdateLevel, 0, 0 ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Resub10: " ), Aig_ManPrintStats( pAig ); + + // rwz -l + pParsRwr->fUseZeros = 1; + pParsRef->fUseZeros = 1; + Dar_ManRewrite( pAig, pParsRwr ); + pAig = Aig_ManDupDfs( pTemp = pAig ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "RewriteZ: " ), Aig_ManPrintStats( pAig ); + + // rs -K 10 -N 2 -l + pAig = Dar_ManResub( pTemp = pAig, 10, 2, fUpdateLevel, 0, 0 ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Resub10x2: " ), Aig_ManPrintStats( pAig ); + + // b -l + pAig = Dar_ManBalance( pTemp = pAig, fUpdateLevel ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Balance: " ), Aig_ManPrintStats( pAig ); + + // rs -K 12 -l + pAig = Dar_ManResub( pTemp = pAig, 12, 1, fUpdateLevel, 0, 0 ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Resub12: " ), Aig_ManPrintStats( pAig ); + + // rfz -l + Dar_ManRefactor( pAig, pParsRef ); + pAig = Aig_ManDupDfs( pTemp = pAig ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "RefactorZ: " ), Aig_ManPrintStats( pAig ); + + // rs -K 12 -N 2 -l + pAig = Dar_ManResub( pTemp = pAig, 12, 2, fUpdateLevel, 0, 0 ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Resub12x2: " ), Aig_ManPrintStats( pAig ); + + // rwz -l + Dar_ManRewrite( pAig, pParsRwr ); + pAig = Aig_ManDupDfs( pTemp = pAig ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "RewriteZ: " ), Aig_ManPrintStats( pAig ); + + // b -l + pAig = Dar_ManBalance( pTemp = pAig, fUpdateLevel ); + Aig_ManStop( pTemp ); + if ( fVerbose ) printf( "Balance: " ), Aig_ManPrintStats( pAig ); + + pGiaRes = Gia_ManFromAig( pAig ); + Aig_ManStop( pAig ); + Gia_ManTransferTiming( pGiaRes, pGia ); + return pGiaRes; +} + //////////////////////////////////////////////////////////////////////// /// END OF FILE /// @@ -914,4 +1152,3 @@ pPars->timeSynth = Abc_Clock() - clk; ABC_NAMESPACE_IMPL_END - From 91e806ffb61ad056c26166b8600c93e2c9ecbd1d Mon Sep 17 00:00:00 2001 From: Alan Mishchenko Date: Fri, 26 Dec 2025 11:04:01 -0800 Subject: [PATCH 36/36] Compiler fix. --- src/misc/util/utilAigSim.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/misc/util/utilAigSim.c b/src/misc/util/utilAigSim.c index a4dcdd1e0..a12d71d81 100644 --- a/src/misc/util/utilAigSim.c +++ b/src/misc/util/utilAigSim.c @@ -31,9 +31,9 @@ #ifdef AIGSIM_LIBRARY_ONLY #include "misc/util/abc_namespaces.h" +ABC_NAMESPACE_IMPL_START #endif -ABC_NAMESPACE_IMPL_START //////////////////////////////////////////////////////////////////////// /// DECLARATIONS /// @@ -656,6 +656,8 @@ int main(int argc, char **argv) { /// END OF FILE /// //////////////////////////////////////////////////////////////////////// - +#ifdef AIGSIM_LIBRARY_ONLY ABC_NAMESPACE_IMPL_END +#endif +