From f544165a88885b5142c316401c34d4d1308204c1 Mon Sep 17 00:00:00 2001 From: MyskYko Date: Wed, 5 Mar 2025 21:25:31 -0800 Subject: [PATCH 1/2] cadical original --- src/sat/cadical/LICENSE | 28 + src/sat/cadical/VERSION | 1 + src/sat/cadical/analyze.cpp | 1279 ++++ src/sat/cadical/arena.cpp | 30 + src/sat/cadical/arena.hpp | 105 + src/sat/cadical/assume.cpp | 607 ++ src/sat/cadical/averages.cpp | 34 + src/sat/cadical/averages.hpp | 37 + src/sat/cadical/backtrack.cpp | 173 + src/sat/cadical/backward.cpp | 231 + src/sat/cadical/bins.cpp | 22 + src/sat/cadical/bins.hpp | 22 + src/sat/cadical/block.cpp | 824 +++ src/sat/cadical/block.hpp | 37 + src/sat/cadical/cadical.hpp | 1342 +++++ src/sat/cadical/ccadical.cpp | 196 + src/sat/cadical/ccadical.h | 71 + src/sat/cadical/checker.cpp | 648 ++ src/sat/cadical/checker.hpp | 172 + src/sat/cadical/clause.cpp | 643 ++ src/sat/cadical/clause.hpp | 188 + src/sat/cadical/collect.cpp | 545 ++ src/sat/cadical/compact.cpp | 551 ++ src/sat/cadical/condition.cpp | 940 +++ src/sat/cadical/config.cpp | 101 + src/sat/cadical/config.hpp | 20 + src/sat/cadical/congruence.cpp | 7555 ++++++++++++++++++++++++ src/sat/cadical/congruence.hpp | 714 +++ src/sat/cadical/constrain.cpp | 64 + src/sat/cadical/contract.cpp | 27 + src/sat/cadical/contract.hpp | 136 + src/sat/cadical/cover.cpp | 704 +++ src/sat/cadical/cover.hpp | 34 + src/sat/cadical/decide.cpp | 252 + src/sat/cadical/decompose.cpp | 733 +++ src/sat/cadical/decompose.hpp | 23 + src/sat/cadical/deduplicate.cpp | 170 + src/sat/cadical/definition.cpp | 283 + src/sat/cadical/delay.hpp | 38 + src/sat/cadical/drattracer.cpp | 153 + src/sat/cadical/drattracer.hpp | 53 + src/sat/cadical/elim.cpp | 1172 ++++ src/sat/cadical/elim.hpp | 53 + src/sat/cadical/elimfast.cpp | 570 ++ src/sat/cadical/ema.cpp | 95 + src/sat/cadical/ema.hpp | 68 + src/sat/cadical/extend.cpp | 281 + src/sat/cadical/external.cpp | 1022 ++++ src/sat/cadical/external.hpp | 461 ++ src/sat/cadical/external_propagate.cpp | 1226 ++++ src/sat/cadical/factor.cpp | 921 +++ src/sat/cadical/factor.hpp | 54 + src/sat/cadical/file.cpp | 506 ++ src/sat/cadical/file.hpp | 210 + src/sat/cadical/flags.cpp | 135 + src/sat/cadical/flags.hpp | 85 + src/sat/cadical/flip.cpp | 269 + src/sat/cadical/format.cpp | 89 + src/sat/cadical/format.hpp | 36 + src/sat/cadical/frattracer.cpp | 277 + src/sat/cadical/frattracer.hpp | 62 + src/sat/cadical/gates.cpp | 766 +++ src/sat/cadical/heap.hpp | 212 + src/sat/cadical/idruptracer.cpp | 566 ++ src/sat/cadical/idruptracer.hpp | 110 + src/sat/cadical/instantiate.cpp | 365 ++ src/sat/cadical/instantiate.hpp | 45 + src/sat/cadical/internal.cpp | 1190 ++++ src/sat/cadical/internal.hpp | 1858 ++++++ src/sat/cadical/inttypes.hpp | 28 + src/sat/cadical/ipasir.cpp | 43 + src/sat/cadical/ipasir.h | 37 + src/sat/cadical/kitten.c | 2593 ++++++++ src/sat/cadical/kitten.h | 97 + src/sat/cadical/level.hpp | 33 + src/sat/cadical/lidruptracer.cpp | 656 ++ src/sat/cadical/lidruptracer.hpp | 117 + src/sat/cadical/limit.cpp | 129 + src/sat/cadical/limit.hpp | 156 + src/sat/cadical/logging.cpp | 215 + src/sat/cadical/logging.hpp | 98 + src/sat/cadical/lookahead.cpp | 520 ++ src/sat/cadical/lratchecker.cpp | 829 +++ src/sat/cadical/lratchecker.hpp | 164 + src/sat/cadical/lrattracer.cpp | 200 + src/sat/cadical/lrattracer.hpp | 57 + src/sat/cadical/lucky.cpp | 434 ++ src/sat/cadical/message.cpp | 212 + src/sat/cadical/message.hpp | 65 + src/sat/cadical/minimize.cpp | 224 + src/sat/cadical/occs.cpp | 52 + src/sat/cadical/occs.hpp | 36 + src/sat/cadical/options.cpp | 359 ++ src/sat/cadical/options.hpp | 416 ++ src/sat/cadical/parse.cpp | 436 ++ src/sat/cadical/parse.hpp | 72 + src/sat/cadical/phases.cpp | 44 + src/sat/cadical/phases.hpp | 18 + src/sat/cadical/probe.cpp | 987 ++++ src/sat/cadical/profile.cpp | 107 + src/sat/cadical/profile.hpp | 274 + src/sat/cadical/proof.cpp | 657 +++ src/sat/cadical/proof.hpp | 114 + src/sat/cadical/propagate.cpp | 578 ++ src/sat/cadical/queue.cpp | 90 + src/sat/cadical/queue.hpp | 64 + src/sat/cadical/radix.hpp | 180 + src/sat/cadical/random.cpp | 207 + src/sat/cadical/random.h | 44 + src/sat/cadical/random.hpp | 98 + src/sat/cadical/range.hpp | 99 + src/sat/cadical/reap.cpp | 127 + src/sat/cadical/reap.hpp | 28 + src/sat/cadical/reduce.cpp | 278 + src/sat/cadical/reluctant.hpp | 82 + src/sat/cadical/rephase.cpp | 336 ++ src/sat/cadical/report.cpp | 296 + src/sat/cadical/resources.cpp | 160 + src/sat/cadical/resources.hpp | 16 + src/sat/cadical/restart.cpp | 159 + src/sat/cadical/restore.cpp | 267 + src/sat/cadical/score.cpp | 51 + src/sat/cadical/score.hpp | 16 + src/sat/cadical/shrink.cpp | 507 ++ src/sat/cadical/signal.cpp | 136 + src/sat/cadical/signal.hpp | 33 + src/sat/cadical/solution.cpp | 50 + src/sat/cadical/solver.cpp | 1758 ++++++ src/sat/cadical/stable.cpp | 31 + src/sat/cadical/stack.h | 110 + src/sat/cadical/stats.cpp | 820 +++ src/sat/cadical/stats.hpp | 370 ++ src/sat/cadical/subsume.cpp | 646 ++ src/sat/cadical/sweep.cpp | 1953 ++++++ src/sat/cadical/sweep.hpp | 61 + src/sat/cadical/terminal.cpp | 38 + src/sat/cadical/terminal.hpp | 98 + src/sat/cadical/ternary.cpp | 450 ++ src/sat/cadical/testing.hpp | 24 + src/sat/cadical/tier.cpp | 53 + src/sat/cadical/tracer.hpp | 177 + src/sat/cadical/transred.cpp | 253 + src/sat/cadical/unstable.cpp | 29 + src/sat/cadical/util.cpp | 129 + src/sat/cadical/util.hpp | 167 + src/sat/cadical/var.cpp | 39 + src/sat/cadical/var.hpp | 22 + src/sat/cadical/veripbtracer.cpp | 394 ++ src/sat/cadical/veripbtracer.hpp | 102 + src/sat/cadical/version.cpp | 107 + src/sat/cadical/version.hpp | 13 + src/sat/cadical/vivify.cpp | 1948 ++++++ src/sat/cadical/vivify.hpp | 62 + src/sat/cadical/walk.cpp | 704 +++ src/sat/cadical/watch.cpp | 126 + src/sat/cadical/watch.hpp | 72 + 156 files changed, 57627 insertions(+) create mode 100644 src/sat/cadical/LICENSE create mode 100644 src/sat/cadical/VERSION create mode 100644 src/sat/cadical/analyze.cpp create mode 100644 src/sat/cadical/arena.cpp create mode 100644 src/sat/cadical/arena.hpp create mode 100644 src/sat/cadical/assume.cpp create mode 100644 src/sat/cadical/averages.cpp create mode 100644 src/sat/cadical/averages.hpp create mode 100644 src/sat/cadical/backtrack.cpp create mode 100644 src/sat/cadical/backward.cpp create mode 100644 src/sat/cadical/bins.cpp create mode 100644 src/sat/cadical/bins.hpp create mode 100644 src/sat/cadical/block.cpp create mode 100644 src/sat/cadical/block.hpp create mode 100644 src/sat/cadical/cadical.hpp create mode 100644 src/sat/cadical/ccadical.cpp create mode 100644 src/sat/cadical/ccadical.h create mode 100644 src/sat/cadical/checker.cpp create mode 100644 src/sat/cadical/checker.hpp create mode 100644 src/sat/cadical/clause.cpp create mode 100644 src/sat/cadical/clause.hpp create mode 100644 src/sat/cadical/collect.cpp create mode 100644 src/sat/cadical/compact.cpp create mode 100644 src/sat/cadical/condition.cpp create mode 100644 src/sat/cadical/config.cpp create mode 100644 src/sat/cadical/config.hpp create mode 100644 src/sat/cadical/congruence.cpp create mode 100644 src/sat/cadical/congruence.hpp create mode 100644 src/sat/cadical/constrain.cpp create mode 100644 src/sat/cadical/contract.cpp create mode 100644 src/sat/cadical/contract.hpp create mode 100644 src/sat/cadical/cover.cpp create mode 100644 src/sat/cadical/cover.hpp create mode 100644 src/sat/cadical/decide.cpp create mode 100644 src/sat/cadical/decompose.cpp create mode 100644 src/sat/cadical/decompose.hpp create mode 100644 src/sat/cadical/deduplicate.cpp create mode 100644 src/sat/cadical/definition.cpp create mode 100644 src/sat/cadical/delay.hpp create mode 100644 src/sat/cadical/drattracer.cpp create mode 100644 src/sat/cadical/drattracer.hpp create mode 100644 src/sat/cadical/elim.cpp create mode 100644 src/sat/cadical/elim.hpp create mode 100644 src/sat/cadical/elimfast.cpp create mode 100644 src/sat/cadical/ema.cpp create mode 100644 src/sat/cadical/ema.hpp create mode 100644 src/sat/cadical/extend.cpp create mode 100644 src/sat/cadical/external.cpp create mode 100644 src/sat/cadical/external.hpp create mode 100644 src/sat/cadical/external_propagate.cpp create mode 100644 src/sat/cadical/factor.cpp create mode 100644 src/sat/cadical/factor.hpp create mode 100644 src/sat/cadical/file.cpp create mode 100644 src/sat/cadical/file.hpp create mode 100644 src/sat/cadical/flags.cpp create mode 100644 src/sat/cadical/flags.hpp create mode 100644 src/sat/cadical/flip.cpp create mode 100644 src/sat/cadical/format.cpp create mode 100644 src/sat/cadical/format.hpp create mode 100644 src/sat/cadical/frattracer.cpp create mode 100644 src/sat/cadical/frattracer.hpp create mode 100644 src/sat/cadical/gates.cpp create mode 100644 src/sat/cadical/heap.hpp create mode 100644 src/sat/cadical/idruptracer.cpp create mode 100644 src/sat/cadical/idruptracer.hpp create mode 100644 src/sat/cadical/instantiate.cpp create mode 100644 src/sat/cadical/instantiate.hpp create mode 100644 src/sat/cadical/internal.cpp create mode 100644 src/sat/cadical/internal.hpp create mode 100644 src/sat/cadical/inttypes.hpp create mode 100644 src/sat/cadical/ipasir.cpp create mode 100644 src/sat/cadical/ipasir.h create mode 100644 src/sat/cadical/kitten.c create mode 100644 src/sat/cadical/kitten.h create mode 100644 src/sat/cadical/level.hpp create mode 100644 src/sat/cadical/lidruptracer.cpp create mode 100644 src/sat/cadical/lidruptracer.hpp create mode 100644 src/sat/cadical/limit.cpp create mode 100644 src/sat/cadical/limit.hpp create mode 100644 src/sat/cadical/logging.cpp create mode 100644 src/sat/cadical/logging.hpp create mode 100644 src/sat/cadical/lookahead.cpp create mode 100644 src/sat/cadical/lratchecker.cpp create mode 100644 src/sat/cadical/lratchecker.hpp create mode 100644 src/sat/cadical/lrattracer.cpp create mode 100644 src/sat/cadical/lrattracer.hpp create mode 100644 src/sat/cadical/lucky.cpp create mode 100644 src/sat/cadical/message.cpp create mode 100644 src/sat/cadical/message.hpp create mode 100644 src/sat/cadical/minimize.cpp create mode 100644 src/sat/cadical/occs.cpp create mode 100644 src/sat/cadical/occs.hpp create mode 100644 src/sat/cadical/options.cpp create mode 100644 src/sat/cadical/options.hpp create mode 100644 src/sat/cadical/parse.cpp create mode 100644 src/sat/cadical/parse.hpp create mode 100644 src/sat/cadical/phases.cpp create mode 100644 src/sat/cadical/phases.hpp create mode 100644 src/sat/cadical/probe.cpp create mode 100644 src/sat/cadical/profile.cpp create mode 100644 src/sat/cadical/profile.hpp create mode 100644 src/sat/cadical/proof.cpp create mode 100644 src/sat/cadical/proof.hpp create mode 100644 src/sat/cadical/propagate.cpp create mode 100644 src/sat/cadical/queue.cpp create mode 100644 src/sat/cadical/queue.hpp create mode 100644 src/sat/cadical/radix.hpp create mode 100644 src/sat/cadical/random.cpp create mode 100644 src/sat/cadical/random.h create mode 100644 src/sat/cadical/random.hpp create mode 100644 src/sat/cadical/range.hpp create mode 100644 src/sat/cadical/reap.cpp create mode 100644 src/sat/cadical/reap.hpp create mode 100644 src/sat/cadical/reduce.cpp create mode 100644 src/sat/cadical/reluctant.hpp create mode 100644 src/sat/cadical/rephase.cpp create mode 100644 src/sat/cadical/report.cpp create mode 100644 src/sat/cadical/resources.cpp create mode 100644 src/sat/cadical/resources.hpp create mode 100644 src/sat/cadical/restart.cpp create mode 100644 src/sat/cadical/restore.cpp create mode 100644 src/sat/cadical/score.cpp create mode 100644 src/sat/cadical/score.hpp create mode 100644 src/sat/cadical/shrink.cpp create mode 100644 src/sat/cadical/signal.cpp create mode 100644 src/sat/cadical/signal.hpp create mode 100644 src/sat/cadical/solution.cpp create mode 100644 src/sat/cadical/solver.cpp create mode 100644 src/sat/cadical/stable.cpp create mode 100644 src/sat/cadical/stack.h create mode 100644 src/sat/cadical/stats.cpp create mode 100644 src/sat/cadical/stats.hpp create mode 100644 src/sat/cadical/subsume.cpp create mode 100644 src/sat/cadical/sweep.cpp create mode 100644 src/sat/cadical/sweep.hpp create mode 100644 src/sat/cadical/terminal.cpp create mode 100644 src/sat/cadical/terminal.hpp create mode 100644 src/sat/cadical/ternary.cpp create mode 100644 src/sat/cadical/testing.hpp create mode 100644 src/sat/cadical/tier.cpp create mode 100644 src/sat/cadical/tracer.hpp create mode 100644 src/sat/cadical/transred.cpp create mode 100644 src/sat/cadical/unstable.cpp create mode 100644 src/sat/cadical/util.cpp create mode 100644 src/sat/cadical/util.hpp create mode 100644 src/sat/cadical/var.cpp create mode 100644 src/sat/cadical/var.hpp create mode 100644 src/sat/cadical/veripbtracer.cpp create mode 100644 src/sat/cadical/veripbtracer.hpp create mode 100644 src/sat/cadical/version.cpp create mode 100644 src/sat/cadical/version.hpp create mode 100644 src/sat/cadical/vivify.cpp create mode 100644 src/sat/cadical/vivify.hpp create mode 100644 src/sat/cadical/walk.cpp create mode 100644 src/sat/cadical/watch.cpp create mode 100644 src/sat/cadical/watch.hpp diff --git a/src/sat/cadical/LICENSE b/src/sat/cadical/LICENSE new file mode 100644 index 000000000..ead29d7a3 --- /dev/null +++ b/src/sat/cadical/LICENSE @@ -0,0 +1,28 @@ +MIT License + +Copyright (c) 2016-2021 Armin Biere, Johannes Kepler University Linz, Austria +Copyright (c) 2020-2021 Mathias Fleury, Johannes Kepler University Linz, Austria +Copyright (c) 2020-2021 Nils Froleyks, Johannes Kepler University Linz, Austria +Copyright (c) 2022-2024 Katalin Fazekas, Vienna University of Technology, Austria +Copyright (c) 2021-2024 Armin Biere, University of Freiburg, Germany +Copyright (c) 2021-2024 Mathias Fleury, University of Freiburg, Germany +Copyright (c) 2023-2024 Florian Pollitt, University of Freiburg, Germany +Copyright (c) 2024-2024 Tobias Faller, University of Freiburg, Germany + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/sat/cadical/VERSION b/src/sat/cadical/VERSION new file mode 100644 index 000000000..a1bba8921 --- /dev/null +++ b/src/sat/cadical/VERSION @@ -0,0 +1 @@ +2.2.0-rc1 diff --git a/src/sat/cadical/analyze.cpp b/src/sat/cadical/analyze.cpp new file mode 100644 index 000000000..667d59633 --- /dev/null +++ b/src/sat/cadical/analyze.cpp @@ -0,0 +1,1279 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Code for conflict analysis, i.e., to generate the first UIP clause. The +// main function is 'analyze' below. It further uses 'minimize' to minimize +// the first UIP clause, which is in 'minimize.cpp'. An important side +// effect of conflict analysis is to update the decision queue by bumping +// variables. Similarly analyzed clauses are bumped to mark them as active. + +/*------------------------------------------------------------------------*/ + +void Internal::learn_empty_clause () { + assert (!unsat); + build_chain_for_empty (); + LOG ("learned empty clause"); + external->check_learned_empty_clause (); + int64_t id = ++clause_id; + if (proof) { + proof->add_derived_empty_clause (id, lrat_chain); + } + unsat = true; + conflict_id = id; + marked_failed = true; + conclusion.push_back (id); + lrat_chain.clear (); +} + +void Internal::learn_unit_clause (int lit) { + assert (!unsat); + LOG ("learned unit clause %d, stored at position %d", lit, vlit (lit)); + external->check_learned_unit_clause (lit); + int64_t id = ++clause_id; + if (lrat || frat) { + const unsigned uidx = vlit (lit); + unit_clauses (uidx) = id; + } + if (proof) { + proof->add_derived_unit_clause (id, lit, lrat_chain); + } + mark_fixed (lit); +} + +/*------------------------------------------------------------------------*/ + +// Move bumped variables to the front of the (VMTF) decision queue. The +// 'bumped' time stamp is updated accordingly. It is used to determine +// whether the 'queue.assigned' pointer has to be moved in 'unassign'. + +void Internal::bump_queue (int lit) { + assert (opts.bump); + const int idx = vidx (lit); + if (!links[idx].next) + return; + queue.dequeue (links, idx); + queue.enqueue (links, idx); + assert (stats.bumped != INT64_MAX); + btab[idx] = ++stats.bumped; + LOG ("moved to front variable %d and bumped to %" PRId64 "", idx, + btab[idx]); + if (!vals[idx]) + update_queue_unassigned (idx); +} + +/*------------------------------------------------------------------------*/ + +// It would be better to use 'isinf' but there are some historical issues +// with this function. On some platforms it is a macro and even for C++ it +// changed the scope (in pre 5.0 gcc) from '::isinf' to 'std::isinf'. I do +// not want to worry about these strange incompatibilities and thus use the +// same trick as in older solvers (since the MiniSAT team invented EVSIDS) +// and simply put a hard limit here. It is less elegant but easy to port. + +static inline bool evsids_limit_hit (double score) { + assert (sizeof (score) == 8); // assume IEEE 754 64-bit double + return score > 1e150; // MAX_DOUBLE is around 1.8e308 +} + +/*------------------------------------------------------------------------*/ + +// Classical exponential VSIDS as pioneered by MiniSAT. + +void Internal::rescale_variable_scores () { + stats.rescored++; + double divider = score_inc; + for (auto idx : vars) { + const double tmp = stab[idx]; + if (tmp > divider) + divider = tmp; + } + PHASE ("rescore", stats.rescored, "rescoring %d variable scores by 1/%g", + max_var, divider); + assert (divider > 0); + double factor = 1.0 / divider; + for (auto idx : vars) + stab[idx] *= factor; + score_inc *= factor; + PHASE ("rescore", stats.rescored, + "new score increment %g after %" PRId64 " conflicts", score_inc, + stats.conflicts); +} + +void Internal::bump_variable_score (int lit) { + assert (opts.bump); + int idx = vidx (lit); + double old_score = score (idx); + assert (!evsids_limit_hit (old_score)); + double new_score = old_score + score_inc; + if (evsids_limit_hit (new_score)) { + LOG ("bumping %g score of %d hits EVSIDS score limit", old_score, idx); + rescale_variable_scores (); + old_score = score (idx); + assert (!evsids_limit_hit (old_score)); + new_score = old_score + score_inc; + } + assert (!evsids_limit_hit (new_score)); + LOG ("new %g score of %d", new_score, idx); + score (idx) = new_score; + if (scores.contains (idx)) + scores.update (idx); +} + +// Important variables recently used in conflict analysis are 'bumped', + +void Internal::bump_variable (int lit) { + if (use_scores ()) + bump_variable_score (lit); + else + bump_queue (lit); +} + +// After every conflict the variable score increment is increased by a +// factor (if we are currently using scores). + +void Internal::bump_variable_score_inc () { + assert (use_scores ()); + assert (!evsids_limit_hit (score_inc)); + double f = 1e3 / opts.scorefactor; + double new_score_inc = score_inc * f; + if (evsids_limit_hit (new_score_inc)) { + LOG ("bumping %g increment by %g hits EVSIDS score limit", score_inc, + f); + rescale_variable_scores (); + new_score_inc = score_inc * f; + } + assert (!evsids_limit_hit (new_score_inc)); + LOG ("bumped score increment from %g to %g with factor %g", score_inc, + new_score_inc, f); + score_inc = new_score_inc; +} + +/*------------------------------------------------------------------------*/ + +struct analyze_bumped_rank { + Internal *internal; + analyze_bumped_rank (Internal *i) : internal (i) {} + typedef uint64_t Type; + Type operator() (const int &a) const { return internal->bumped (a); } +}; + +struct analyze_bumped_smaller { + Internal *internal; + analyze_bumped_smaller (Internal *i) : internal (i) {} + bool operator() (const int &a, const int &b) const { + const auto s = analyze_bumped_rank (internal) (a); + const auto t = analyze_bumped_rank (internal) (b); + return s < t; + } +}; + +/*------------------------------------------------------------------------*/ + +void Internal::bump_variables () { + + assert (opts.bump); + + START (bump); + + if (!use_scores ()) { + + // Variables are bumped in the order they are in the current decision + // queue. This maintains relative order between bumped variables in + // the queue and seems to work best. We also experimented with + // focusing on variables of the last decision level, but results were + // mixed. + + MSORT (opts.radixsortlim, analyzed.begin (), analyzed.end (), + analyze_bumped_rank (this), analyze_bumped_smaller (this)); + } + + for (const auto &lit : analyzed) + bump_variable (lit); + + if (use_scores ()) + bump_variable_score_inc (); + + STOP (bump); +} + +/*------------------------------------------------------------------------*/ + +// We use the glue time stamp table 'gtab' for fast glue computation. + +int Internal::recompute_glue (Clause *c) { + int res = 0; + const int64_t stamp = ++stats.recomputed; + for (const auto &lit : *c) { + int level = var (lit).level; + assert (gtab[level] <= stamp); + if (gtab[level] == stamp) + continue; + gtab[level] = stamp; + res++; + } + return res; +} + +// Clauses resolved since the last reduction are marked as 'used', their +// glue is recomputed and they are promoted if the glue shrinks. Note that +// promotion from 'tier3' to 'tier2' will set 'used' to '2'. + +inline void Internal::bump_clause (Clause *c) { + LOG (c, "bumping"); + c->used = max_used; + if (c->hyper) + return; + if (!c->redundant) + return; + int new_glue = recompute_glue (c); + if (new_glue < c->glue) + promote_clause (c, new_glue); + + const size_t glue = + std::min ((size_t) c->glue, stats.used[stable].size () - 1); + ++stats.used[stable][glue]; + ++stats.bump_used[stable]; +} + +void Internal::bump_clause2 (Clause *c) { bump_clause (c); } +/*------------------------------------------------------------------------*/ + +// During conflict analysis literals not seen yet either become part of the +// first unique implication point (UIP) clause (if on lower decision level), +// are dropped (if fixed), or are resolved away (if on the current decision +// level and different from the first UIP). At the same time we update the +// number of seen literals on a decision level. This helps conflict clause +// minimization. The number of seen levels is the glucose level (also +// called 'glue', or 'LBD'). + +inline void Internal::analyze_literal (int lit, int &open, + int &resolvent_size, + int &antecedent_size) { + assert (lit); + Var &v = var (lit); + Flags &f = flags (lit); + + if (!v.level) { + if (f.seen || !lrat) + return; + f.seen = true; + unit_analyzed.push_back (lit); + assert (val (lit) < 0); + int64_t id = unit_id (-lit); + unit_chain.push_back (id); + return; + } + ++antecedent_size; + if (f.seen) + return; + + // before marking as seen, get reason and check for missed unit + + assert (val (lit) < 0); + assert (v.level <= level); + if (v.reason == external_reason) { + assert (!opts.exteagerreasons); + v.reason = learn_external_reason_clause (-lit, 0, true); + if (!v.reason) { // actually a unit + --antecedent_size; + LOG ("%d unit after explanation", -lit); + if (f.seen || !lrat) + return; + f.seen = true; + unit_analyzed.push_back (lit); + assert (val (lit) < 0); + const unsigned uidx = vlit (-lit); + int64_t id = unit_clauses (uidx); + assert (id); + unit_chain.push_back (id); + return; + } + } + + f.seen = true; + analyzed.push_back (lit); + + assert (v.reason != external_reason); + if (v.level < level) + clause.push_back (lit); + Level &l = control[v.level]; + if (!l.seen.count++) { + LOG ("found new level %d contributing to conflict", v.level); + levels.push_back (v.level); + } + if (v.trail < l.seen.trail) + l.seen.trail = v.trail; + ++resolvent_size; + LOG ("analyzed literal %d assigned at level %d", lit, v.level); + if (v.level == level) + open++; +} + +inline void Internal::analyze_reason (int lit, Clause *reason, int &open, + int &resolvent_size, + int &antecedent_size) { + assert (reason); + assert (reason != external_reason); + bump_clause (reason); + if (lrat) + lrat_chain.push_back (reason->id); + for (const auto &other : *reason) + if (other != lit) + analyze_literal (other, open, resolvent_size, antecedent_size); +} + +/*------------------------------------------------------------------------*/ + +// This is an idea which was implicit in MapleCOMSPS 2016 for 'limit = 1'. +// See also the paragraph on 'bumping reason side literals' in their SAT'16 +// paper [LiangGaneshPoupartCzarnecki-SAT'16]. Reason side bumping was +// performed exactly when 'LRB' based decision heuristics was used, which in +// the original version was enabled after 10000 conflicts until a time limit +// of 2500 seconds was reached (half of the competition time limit). The +// Maple / Glucose / MiniSAT evolution winning the SAT race in 2019 made +// the schedule of reason side bumping deterministic, i.e., avoiding a time +// limit, by switching between 'LRB' and 'VSIDS' in an interval of initially +// 30 million propagations, which then is increased geometrically by 10%. + +inline bool Internal::bump_also_reason_literal (int lit) { + assert (lit); + assert (val (lit) < 0); + Flags &f = flags (lit); + if (f.seen) + return false; + const Var &v = var (lit); + if (!v.level) + return false; + f.seen = true; + analyzed.push_back (lit); + LOG ("bumping also reason literal %d assigned at level %d", lit, v.level); + return true; +} + +// We experimented with deeper reason bumping without much success though. + +inline void Internal::bump_also_reason_literals (int lit, int depth_limit, + size_t analyzed_limit) { + assert (lit); + assert (depth_limit > 0); + const Var &v = var (lit); + assert (val (lit)); + if (!v.level) + return; + Clause *reason = v.reason; + if (!reason || reason == external_reason) + return; + stats.ticks.search[stable]++; + for (const auto &other : *reason) { + if (other == lit) + continue; + if (!bump_also_reason_literal (other)) + continue; + if (depth_limit < 2) + continue; + bump_also_reason_literals (-other, depth_limit - 1, analyzed_limit); + if (analyzed.size () > analyzed_limit) + break; + } +} + +inline void Internal::bump_also_all_reason_literals () { + assert (opts.bump); + if (!opts.bumpreason) + return; + if (averages.current.decisions > opts.bumpreasonrate) { + LOG ("decisions per conflict rate %g > limit %d", + (double) averages.current.decisions, opts.bumpreasonrate); + return; + } + if (delay[stable].bumpreasons.limit) { + LOG ("delaying reason bumping %" PRId64 " more times", + delay[stable].bumpreasons.limit); + delay[stable].bumpreasons.limit--; + return; + } + assert (opts.bumpreasondepth > 0); + const int depth_limit = opts.bumpreasondepth + stable; + size_t saved_analyzed = analyzed.size (); + size_t analyzed_limit = saved_analyzed * opts.bumpreasonlimit; + for (const auto &lit : clause) + if (analyzed.size () <= analyzed_limit) + bump_also_reason_literals (-lit, depth_limit, analyzed_limit); + else + break; + if (analyzed.size () > analyzed_limit) { + LOG ("not bumping reason side literals as limit exhausted"); + for (size_t i = saved_analyzed; i != analyzed.size (); i++) { + const int lit = analyzed[i]; + Flags &f = flags (lit); + assert (f.seen); + f.seen = false; + } + delay[stable].bumpreasons.interval++; + analyzed.resize (saved_analyzed); + } else { + LOG ("bumping reasons up to depth %d", opts.bumpreasondepth); + delay[stable].bumpreasons.interval /= 2; + } + LOG ("delay internal %" PRId64, delay[stable].bumpreasons.interval); + delay[stable].bumpreasons.limit = delay[stable].bumpreasons.interval; +} + +/*------------------------------------------------------------------------*/ + +void Internal::clear_unit_analyzed_literals () { + LOG ("clearing %zd unit analyzed literals", unit_analyzed.size ()); + for (const auto &lit : unit_analyzed) { + Flags &f = flags (lit); + assert (f.seen); + assert (!var (lit).level); + f.seen = false; + assert (!f.keep); + assert (!f.poison); + assert (!f.removable); + } + unit_analyzed.clear (); +} + +void Internal::clear_analyzed_literals () { + LOG ("clearing %zd analyzed literals", analyzed.size ()); + for (const auto &lit : analyzed) { + Flags &f = flags (lit); + assert (f.seen); + f.seen = false; + assert (!f.keep); + assert (!f.poison); + assert (!f.removable); + } + analyzed.clear (); +#if 0 // to expensive, even for debugging mode + if (unit_analyzed.size ()) + return; + for (auto idx : vars) { + Flags &f = flags (idx); + assert (!f.seen); + } +#endif +} + +void Internal::clear_analyzed_levels () { + LOG ("clearing %zd analyzed levels", levels.size ()); + for (const auto &l : levels) + if (l < (int) control.size ()) + control[l].reset (); + levels.clear (); +} + +/*------------------------------------------------------------------------*/ + +// Smaller level and trail. Comparing literals on their level is necessary +// for chronological backtracking, since trail order might in this case not +// respect level order. + +struct analyze_trail_negative_rank { + Internal *internal; + analyze_trail_negative_rank (Internal *s) : internal (s) {} + typedef uint64_t Type; + Type operator() (int a) { + Var &v = internal->var (a); + uint64_t res = v.level; + res <<= 32; + res |= v.trail; + return ~res; + } +}; + +struct analyze_trail_larger { + Internal *internal; + analyze_trail_larger (Internal *s) : internal (s) {} + bool operator() (const int &a, const int &b) const { + return analyze_trail_negative_rank (internal) (a) < + analyze_trail_negative_rank (internal) (b); + } +}; + +/*------------------------------------------------------------------------*/ + +// Generate new driving clause and compute jump level. + +Clause *Internal::new_driving_clause (const int glue, int &jump) { + + const size_t size = clause.size (); + Clause *res; + + if (!size) { + + jump = 0; + res = 0; + + } else if (size == 1) { + + iterating = true; + jump = 0; + res = 0; + + } else { + + assert (clause.size () > 1); + + // We have to get the last assigned literals into the watch position. + // Sorting all literals with respect to reverse assignment order is + // overkill but seems to get slightly faster run-time. For 'minimize' + // we sort the literals too heuristically along the trail order (so in + // the opposite order) with the hope to hit the recursion limit less + // frequently. Thus sorting effort is doubled here. + // + MSORT (opts.radixsortlim, clause.begin (), clause.end (), + analyze_trail_negative_rank (this), analyze_trail_larger (this)); + + jump = var (clause[1]).level; + res = new_learned_redundant_clause (glue); + res->used = 1 + (glue <= opts.reducetier2glue); + } + + LOG ("jump level %d", jump); + + return res; +} + +/*------------------------------------------------------------------------*/ + +// determine the OTFS level for OTFS. Unlike the find_conflict_level, we do +// not have to fix the clause + +inline int Internal::otfs_find_backtrack_level (int &forced) { + assert (opts.otfs); + int res = 0; + + for (const auto &lit : *conflict) { + const int tmp = var (lit).level; + if (tmp == level) { + forced = lit; + } else if (tmp > res) { + res = tmp; + LOG ("bt level is now %d due to %d", res, lit); + } + } + return res; +} + +/*------------------------------------------------------------------------*/ + +// If chronological backtracking is enabled we need to find the actual +// conflict level and then potentially can also reuse the conflict clause +// as driving clause instead of deriving a redundant new driving clause +// (forcing 'forced') if the number 'count' of literals in conflict assigned +// at the conflict level is exactly one. + +inline int Internal::find_conflict_level (int &forced) { + + assert (conflict); + assert (opts.chrono || opts.otfs || external_prop); + + int res = 0, count = 0; + + forced = 0; + + for (const auto &lit : *conflict) { + const int tmp = var (lit).level; + if (tmp > res) { + res = tmp; + forced = lit; + count = 1; + } else if (tmp == res) { + count++; + if (res == level && count > 1) + break; + } + } + + LOG ("%d literals on actual conflict level %d", count, res); + + const int size = conflict->size; + int *lits = conflict->literals; + + // Move the two highest level literals to the front. + // + for (int i = 0; i < 2; i++) { + + const int lit = lits[i]; + + int highest_position = i; + int highest_literal = lit; + int highest_level = var (highest_literal).level; + + for (int j = i + 1; j < size; j++) { + const int other = lits[j]; + const int tmp = var (other).level; + if (highest_level >= tmp) + continue; + highest_literal = other; + highest_position = j; + highest_level = tmp; + if (highest_level == res) + break; + } + + // No unwatched higher assignment level literal. + // + if (highest_position == i) + continue; + + if (highest_position > 1) { + LOG (conflict, "unwatch %d in", lit); + remove_watch (watches (lit), conflict); + } + + lits[highest_position] = lit; + lits[i] = highest_literal; + + if (highest_position > 1) + watch_literal (highest_literal, lits[!i], conflict); + } + + // Only if the number of highest level literals in the conflict is one + // then we can reuse the conflict clause as driving clause for 'forced'. + // + if (count != 1) + forced = 0; + + return res; +} + +/*------------------------------------------------------------------------*/ + +inline int Internal::determine_actual_backtrack_level (int jump) { + + int res; + + assert (level > jump); + + if (!opts.chrono) { + res = jump; + LOG ("chronological backtracking disabled using jump level %d", res); + } else if (opts.chronoalways) { + stats.chrono++; + res = level - 1; + LOG ("forced chronological backtracking to level %d", res); + } else if (jump >= level - 1) { + res = jump; + LOG ("jump level identical to chronological backtrack level %d", res); + } else if ((size_t) jump < assumptions.size ()) { + res = jump; + LOG ("using jump level %d since it is lower than assumption level %zd", + res, assumptions.size ()); + } else if (level - jump > opts.chronolevelim) { + stats.chrono++; + res = level - 1; + LOG ("back-jumping over %d > %d levels prohibited" + "thus backtracking chronologically to level %d", + level - jump, opts.chronolevelim, res); + } else if (opts.chronoreusetrail) { + int best_idx = 0, best_pos = 0; + + if (use_scores ()) { + for (size_t i = control[jump + 1].trail; i < trail.size (); i++) { + const int idx = abs (trail[i]); + if (best_idx && !score_smaller (this) (best_idx, idx)) + continue; + best_idx = idx; + best_pos = i; + } + LOG ("best variable score %g", score (best_idx)); + } else { + for (size_t i = control[jump + 1].trail; i < trail.size (); i++) { + const int idx = abs (trail[i]); + if (best_idx && bumped (best_idx) >= bumped (idx)) + continue; + best_idx = idx; + best_pos = i; + } + LOG ("best variable bumped %" PRId64 "", bumped (best_idx)); + } + assert (best_idx); + LOG ("best variable %d at trail position %d", best_idx, best_pos); + + // Now find the frame and decision level in the control stack of that + // best variable index. Note that, as in 'reuse_trail', the frame + // 'control[i]' for decision level 'i' contains the trail before that + // decision level, i.e., the decision 'control[i].decision' sits at + // 'control[i].trail' in the trail and we thus have to check the level + // of the control frame one higher than at the result level. + // + res = jump; + while (res < level - 1 && control[res + 1].trail <= best_pos) + res++; + + if (res == jump) + LOG ("default non-chronological back-jumping to level %d", res); + else { + stats.chrono++; + LOG ("chronological backtracking to level %d to reuse trail", res); + } + + } else { + res = jump; + LOG ("non-chronological back-jumping to level %d", res); + } + + return res; +} + +/*------------------------------------------------------------------------*/ + +void Internal::eagerly_subsume_recently_learned_clauses (Clause *c) { + assert (opts.eagersubsume); + LOG (c, "trying eager subsumption with"); + mark (c); + int64_t lim = stats.eagertried + opts.eagersubsumelim; + const auto begin = clauses.begin (); + auto it = clauses.end (); +#ifdef LOGGING + int64_t before = stats.eagersub; +#endif + while (it != begin && stats.eagertried++ <= lim) { + Clause *d = *--it; + if (c == d) + continue; + if (d->garbage) + continue; + if (!d->redundant) + continue; + int needed = c->size; + for (auto &lit : *d) { + if (marked (lit) <= 0) + continue; + if (!--needed) + break; + } + if (needed) + continue; + LOG (d, "eager subsumed"); + stats.eagersub++; + stats.subsumed++; + mark_garbage (d); + } + unmark (c); +#ifdef LOGGING + uint64_t subsumed = stats.eagersub - before; + if (subsumed) + LOG ("eagerly subsumed %" PRIu64 " clauses", subsumed); +#endif +} + +/*------------------------------------------------------------------------*/ + +Clause *Internal::on_the_fly_strengthen (Clause *new_conflict, int uip) { + assert (new_conflict); + assert (new_conflict->size > 2); + LOG (new_conflict, "applying OTFS on lit %d", uip); + auto sorted = std::vector (); + sorted.reserve (new_conflict->size); + assert (sorted.empty ()); + ++stats.otfs.strengthened; + + int *lits = new_conflict->literals; + + assert (lits[0] == uip || lits[1] == uip); + const int other_init = lits[0] ^ lits[1] ^ uip; + + assert (mini_chain.empty ()); + + const int old_size = new_conflict->size; + int new_size = 0; + for (int i = 0; i < old_size; ++i) { + const int other = lits[i]; + sorted.push_back (other); + if (var (other).level) + lits[new_size++] = other; + } + + LOG (new_conflict, "removing all units in"); + + assert (lits[0] == uip || lits[1] == uip); + const int other = lits[0] ^ lits[1] ^ uip; + lits[0] = other; + lits[1] = lits[--new_size]; + LOG (new_conflict, "putting uip at pos 1"); + + if (other_init != other) + remove_watch (watches (other_init), new_conflict); + remove_watch (watches (uip), new_conflict); + + assert (!lrat || lrat_chain.back () == new_conflict->id); + if (lrat) { + assert (!lrat_chain.empty ()); + for (const auto &id : unit_chain) { + mini_chain.push_back (id); + } + const auto end = lrat_chain.rend (); + const auto begin = lrat_chain.rbegin (); + for (auto i = begin; i != end; i++) { + const auto id = *i; + mini_chain.push_back (id); + } + lrat_chain.clear (); + clear_unit_analyzed_literals (); + unit_chain.clear (); + } + assert (unit_analyzed.empty ()); + // sort the clause + { + int highest_pos = 0; + int highest_level = 0; + for (int i = 1; i < new_size; i++) { + const unsigned other = lits[i]; + assert (val (other) < 0); + const int level = var (other).level; + assert (level); + LOG ("checking %d", other); + if (level <= highest_level) + continue; + highest_pos = i; + highest_level = level; + } + LOG ("highest lit is %d", lits[highest_pos]); + if (highest_pos != 1) + swap (lits[1], lits[highest_pos]); + LOG ("removing %d literals", new_conflict->size - new_size); + + if (new_size == 1) { + LOG (new_conflict, "new size = 1, so interrupting"); + assert (!opts.exteagerreasons); + return 0; + } else { + otfs_strengthen_clause (new_conflict, uip, new_size, sorted); + assert (new_size == new_conflict->size); + } + } + + if (other_init != other) + watch_literal (other, lits[1], new_conflict); + else { + update_watch_size (watches (other), lits[1], new_conflict); + } + watch_literal (lits[1], other, new_conflict); + + LOG (new_conflict, "strengthened clause by OTFS"); + sorted.clear (); + + return new_conflict; +} + +/*------------------------------------------------------------------------*/ +inline void Internal::otfs_subsume_clause (Clause *subsuming, + Clause *subsumed) { + stats.subsumed++; + assert (subsuming->size <= subsumed->size); + LOG (subsumed, "subsumed"); + if (subsumed->redundant) + stats.subred++; + else + stats.subirr++; + if (subsumed->redundant || !subsuming->redundant) { + mark_garbage (subsumed); + return; + } + LOG ("turning redundant subsuming clause into irredundant clause"); + subsuming->redundant = false; + if (proof) + proof->strengthen (subsuming->id); + mark_garbage (subsumed); + stats.current.irredundant++; + stats.added.irredundant++; + stats.irrlits += subsuming->size; + assert (stats.current.redundant > 0); + stats.current.redundant--; + assert (stats.added.redundant > 0); + stats.added.redundant--; + // ... and keep 'stats.added.total'. +} + +/*------------------------------------------------------------------------*/ + +// Candidate clause 'c' is strengthened by removing 'lit' and units. +// +void Internal::otfs_strengthen_clause (Clause *c, int lit, int new_size, + const std::vector &old) { + stats.strengthened++; + assert (c->size > 2); + (void) shrink_clause (c, new_size); + if (proof) { + proof->otfs_strengthen_clause (c, old, mini_chain); + } + if (!c->redundant) { + mark_removed (lit); + } + mini_chain.clear (); + c->used = true; + LOG (c, "strengthened"); + external->check_shrunken_clause (c); +} + +/*------------------------------------------------------------------------*/ + +// If the average number of decisions per conflict (analysis actually so not +// taking OTFS conflicts into account) is high we do not bump reasons. This +// is the function which updates the exponential moving decision rate +// average. + +void Internal::update_decision_rate_average () { + int64_t current = stats.decisions; + int64_t decisions = current - saved_decisions; + UPDATE_AVERAGE (averages.current.decisions, decisions); + saved_decisions = current; +} + +/*------------------------------------------------------------------------*/ + +// This is the main conflict analysis routine. It assumes that a conflict +// was found. Then we derive the 1st UIP clause, optionally minimize it, +// add it as learned clause, and then uses the clause for conflict directed +// back-jumping and flipping the 1st UIP literal. In combination with +// chronological backtracking (see discussion above) the algorithm becomes +// slightly more involved. + +void Internal::analyze () { + + START (analyze); + + assert (conflict); + assert (lrat_chain.empty ()); + assert (unit_chain.empty ()); + assert (unit_analyzed.empty ()); + assert (clause.empty ()); + + // First update moving averages of trail height at conflict. + // + UPDATE_AVERAGE (averages.current.trail.fast, num_assigned); + UPDATE_AVERAGE (averages.current.trail.slow, num_assigned); + update_decision_rate_average (); + + /*----------------------------------------------------------------------*/ + + if (external_prop && !external_prop_is_lazy && opts.exteagerreasons) { + explain_external_propagations (); + } + + if (opts.chrono || external_prop) { + + int forced; + + const int conflict_level = find_conflict_level (forced); + + // In principle we can perform conflict analysis as in non-chronological + // backtracking except if there is only one literal with the maximum + // assignment level in the clause. Then standard conflict analysis is + // unnecessary and we can use the conflict as a driving clause. In the + // pseudo code of the SAT'18 paper on chronological backtracking this + // corresponds to the situation handled in line 4-6 in Alg. 1, except + // that the pseudo code in the paper only backtracks while we eagerly + // assign the single literal on the highest decision level. + + if (forced) { + + assert (forced); + assert (conflict_level > 0); + LOG ("single highest level literal %d", forced); + + // The pseudo code in the SAT'18 paper actually backtracks to the + // 'second highest decision' level, while their code backtracks + // to 'conflict_level-1', which is more in the spirit of chronological + // backtracking anyhow and thus we also do the latter. + // + backtrack (conflict_level - 1); + + // if we are on decision level 0 search assign will learn unit + // so we need a valid chain here (of course if we are not on decision + // level 0 this will not result in a valid chain). + // we can just use build_chain_for_units in propagate + // + build_chain_for_units (forced, conflict, 0); + + LOG ("forcing %d", forced); + search_assign_driving (forced, conflict); + + conflict = 0; + STOP (analyze); + return; + } + + // Backtracking to the conflict level is in the pseudo code in the + // SAT'18 chronological backtracking paper, but not in their actual + // implementation. In principle we do not need to backtrack here. + // However, as a side effect of backtracking to the conflict level we + // set 'level' to the conflict level which then allows us to reuse the + // old 'analyze' code as is. The alternative (which we also tried but + // then abandoned) is to use 'conflict_level' instead of 'level' in the + // analysis, which however requires to pass it to the 'analyze_reason' + // and 'analyze_literal' functions. + // + backtrack (conflict_level); + } + + // Actual conflict on root level, thus formula unsatisfiable. + // + if (!level) { + learn_empty_clause (); + if (external->learner) + external->export_learned_empty_clause (); + STOP (analyze); + return; + } + + /*----------------------------------------------------------------------*/ + + // First derive the 1st UIP clause by going over literals assigned on the + // current decision level. Literals in the conflict are marked as 'seen' + // as well as all literals in reason clauses of already 'seen' literals on + // the current decision level. Thus the outer loop starts with the + // conflict clause as 'reason' and then uses the 'reason' of the next + // seen literal on the trail assigned on the current decision level. + // During this process maintain the number 'open' of seen literals on the + // current decision level with not yet processed 'reason'. As soon 'open' + // drops to one, we have found the first unique implication point. This + // is sound because the topological order in which literals are processed + // follows the assignment order and a more complex algorithm to find + // articulation points is not necessary. + // + Clause *reason = conflict; + LOG (reason, "analyzing conflict"); + + assert (clause.empty ()); + assert (lrat_chain.empty ()); + + const auto &t = &trail; + int i = t->size (); // Start at end-of-trail. + int open = 0; // Seen but not processed on this level. + int uip = 0; // The first UIP literal. + int resolvent_size = 0; // without the uip + int antecedent_size = 1; // with the uip and without unit literals + int conflict_size = 0; // without the uip and without unit literals + int resolved = 0; // number of resolution (0 = clause in CNF) + const bool otfs = opts.otfs; + + for (;;) { + antecedent_size = 1; // for uip + analyze_reason (uip, reason, open, resolvent_size, antecedent_size); + if (resolved == 0) + conflict_size = antecedent_size - 1; + assert (resolvent_size == open + (int) clause.size ()); + + if (otfs && resolved > 0 && antecedent_size > 2 && + resolvent_size < antecedent_size) { + assert (reason != conflict); + LOG (analyzed, "found candidate for OTFS conflict"); + LOG (clause, "found candidate for OTFS conflict"); + LOG (reason, "found candidate (size %d) for OTFS resolvent", + antecedent_size); + const int other = reason->literals[0] ^ reason->literals[1] ^ uip; + assert (other != uip); + reason = on_the_fly_strengthen (reason, uip); + if (opts.bump) + bump_variables (); + + assert (conflict_size); + if (!reason) { + uip = -other; + assert (open == 1); + LOG ("clause is actually unit %d, stopping", -uip); + reverse (begin (mini_chain), end (mini_chain)); + for (auto id : mini_chain) + lrat_chain.push_back (id); + mini_chain.clear (); + clear_analyzed_levels (); + assert (!opts.exteagerreasons); + clause.clear (); + break; + } + assert (conflict_size >= 2); + + if (resolved == 1 && resolvent_size < conflict_size) { + // here both clauses are part of the CNF, so one subsumes the other + otfs_subsume_clause (reason, conflict); + LOG (reason, "changing conflict to"); + --conflict_size; + assert (conflict_size == reason->size); + ++stats.otfs.subsumed; + ++stats.subsumed; + } + + LOG (reason, "changing conflict to"); + conflict = reason; + if (open == 1) { + int forced = 0; + const int conflict_level = otfs_find_backtrack_level (forced); + int new_level = determine_actual_backtrack_level (conflict_level); + UPDATE_AVERAGE (averages.current.level, new_level); + backtrack (new_level); + + LOG ("forcing %d", forced); + search_assign_driving (forced, conflict); + + // Clean up. + // + conflict = 0; + clear_analyzed_literals (); + clear_analyzed_levels (); + clause.clear (); + STOP (analyze); + return; + } + + stats.conflicts++; + + clear_analyzed_literals (); + clear_analyzed_levels (); + clause.clear (); + resolvent_size = 0; + antecedent_size = 1; + resolved = 0; + open = 0; + analyze_reason (0, reason, open, resolvent_size, antecedent_size); + conflict_size = antecedent_size - 1; + assert (open > 1); + } + + ++resolved; + + uip = 0; + while (!uip) { + assert (i > 0); + const int lit = (*t)[--i]; + if (!flags (lit).seen) + continue; + if (var (lit).level == level) + uip = lit; + } + if (!--open) + break; + reason = var (uip).reason; + if (reason == external_reason) { + assert (!opts.exteagerreasons); + reason = learn_external_reason_clause (-uip, 0, true); + var (uip).reason = reason; + } + assert (reason != external_reason); + LOG (reason, "analyzing %d reason", uip); + assert (resolvent_size); + --resolvent_size; + } + LOG ("first UIP %d", uip); + clause.push_back (-uip); + + // Update glue and learned (1st UIP literals) statistics. + // + int size = (int) clause.size (); + const int glue = (int) levels.size () - 1; + LOG (clause, "1st UIP size %d and glue %d clause", size, glue); + UPDATE_AVERAGE (averages.current.glue.fast, glue); + UPDATE_AVERAGE (averages.current.glue.slow, glue); + stats.learned.literals += size; + stats.learned.clauses++; + assert (glue < size); + + // up to this point lrat_chain contains the proof for current clause in + // reversed order. in minimize and shrink the clause is changed and + // therefore lrat_chain has to be extended. Unfortunately we cannot create + // the chain directly during minimization (or shrinking) but afterwards we + // can calculate it pretty easily and even better the same algorithm works + // for both shrinking and minimization. + + // Minimize the 1st UIP clause as pioneered by Niklas Soerensson in + // MiniSAT and described in our joint SAT'09 paper. + // + if (size > 1) { + if (opts.shrink) + shrink_and_minimize_clause (); + else if (opts.minimize) + minimize_clause (); + + size = (int) clause.size (); + + // Update decision heuristics. + // + if (opts.bump) { + bump_also_all_reason_literals (); + bump_variables (); + } + + if (external->learner) + external->export_learned_large_clause (clause); + } else if (external->learner) + external->export_learned_unit_clause (-uip); + + // Update actual size statistics. + // + stats.units += (size == 1); + stats.binaries += (size == 2); + UPDATE_AVERAGE (averages.current.size, size); + + // reverse lrat_chain. We could probably work with reversed iterators + // (views) to be more efficient but we would have to distinguish in proof + // + if (lrat) { + LOG (unit_chain, "unit chain: "); + for (auto id : unit_chain) + lrat_chain.push_back (id); + unit_chain.clear (); + reverse (lrat_chain.begin (), lrat_chain.end ()); + } + + // Determine back-jump level, learn driving clause, backtrack and assign + // flipped 1st UIP literal. + // + int jump; + Clause *driving_clause = new_driving_clause (glue, jump); + UPDATE_AVERAGE (averages.current.jump, jump); + + int new_level = determine_actual_backtrack_level (jump); + UPDATE_AVERAGE (averages.current.level, new_level); + backtrack (new_level); + + // It should hold that (!level <=> size == 1) + // and (!uip <=> size == 0) + // this means either we have already learned a clause => size >= 2 + // in this case we will not learn empty clause or unit here + // or we haven't actually learned a clause in new_driving_clause + // then lrat_chain is still valid and we will learn a unit or empty clause + // + if (uip) { + search_assign_driving (-uip, driving_clause); + } else + learn_empty_clause (); + + if (stable) + reluctant.tick (); // Reluctant has its own 'conflict' counter. + + // Clean up. + // + clear_analyzed_literals (); + clear_unit_analyzed_literals (); + clear_analyzed_levels (); + clause.clear (); + conflict = 0; + + lrat_chain.clear (); + STOP (analyze); + + if (driving_clause && opts.eagersubsume) + eagerly_subsume_recently_learned_clauses (driving_clause); + + if (lim.recompute_tier <= stats.conflicts) + recompute_tier (); +} + +// We wait reporting a learned unit until propagation of that unit is +// completed. Otherwise the 'i' report gives the number of remaining +// variables before propagating the unit (and hides the actual remaining +// variables after propagating it). + +void Internal::iterate () { + iterating = false; + report ('i'); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/arena.cpp b/src/sat/cadical/arena.cpp new file mode 100644 index 000000000..e8e2eea64 --- /dev/null +++ b/src/sat/cadical/arena.cpp @@ -0,0 +1,30 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +Arena::Arena (Internal *i) { + memset (this, 0, sizeof *this); + internal = i; +} + +Arena::~Arena () { + delete[] from.start; + delete[] to.start; +} + +void Arena::prepare (size_t bytes) { + LOG ("preparing 'to' space of arena with %zd bytes", bytes); + assert (!to.start); + to.top = to.start = new char[bytes]; + to.end = to.start + bytes; +} + +void Arena::swap () { + delete[] from.start; + LOG ("delete 'from' space of arena with %zd bytes", + (size_t) (from.end - from.start)); + from = to; + to.start = to.top = to.end = 0; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/arena.hpp b/src/sat/cadical/arena.hpp new file mode 100644 index 000000000..6dbd51121 --- /dev/null +++ b/src/sat/cadical/arena.hpp @@ -0,0 +1,105 @@ +#ifndef _arena_hpp_INCLUDED +#define _arena_hpp_INCLUDED + +namespace CaDiCaL { + +// This memory allocation arena provides fixed size pre-allocated memory for +// the moving garbage collector 'copy_non_garbage_clauses' in 'collect.cpp' +// to hold clauses which should survive garbage collection. + +// The advantage of using a pre-allocated arena is that the allocation order +// of the clauses can be adapted in such a way that clauses watched by the +// same literal are allocated consecutively. This improves locality during +// propagation and thus is more cache friendly. A similar technique is +// implemented in MiniSAT and Glucose and gives substantial speed-up in +// propagations per second even though it might even almost double peek +// memory usage. Note that in MiniSAT this arena is actually required for +// MiniSAT to be able to use 32 bit clauses references instead of 64 bit +// pointers. This would restrict the maximum number of clauses and thus is +// a restriction we do not want to use anymore. + +// New learned clauses are allocated in CaDiCaL outside of this arena and +// moved to the arena during garbage collection. The additional 'to' space +// required for such a moving garbage collector is only allocated for those +// clauses surviving garbage collection, which usually needs much less +// memory than all clauses. The net effect is that in our implementation +// the moving garbage collector using this arena only needs roughly 50% more +// memory than allocating the clauses directly. Both implementations can be +// compared by varying the 'opts.arenatype' option (which also controls the +// allocation order of clauses during moving them). + +// The standard sequence of using the arena is as follows: +// +// Arena arena; +// ... +// arena.prepare (bytes); +// q1 = arena.copy (p1, bytes1); +// ... +// qn = arena.copy (pn, bytesn); +// assert (bytes1 + ... + bytesn <= bytes); +// arena.swap (); +// ... +// if (!arena.contains (q)) delete q; +// ... +// arena.prepare (bytes); +// q1 = arena.copy (p1, bytes1); +// ... +// qn = arena.copy (pn, bytesn); +// assert (bytes1 + ... + bytesn <= bytes); +// arena.swap (); +// ... +// +// One has to be really careful with 'qi' references to arena memory. + +struct Internal; + +class Arena { + + Internal *internal; + + struct { + char *start, *top, *end; + } from, to; + +public: + Arena (Internal *); + ~Arena (); + + // Prepare 'to' space to hold that amount of memory. Precondition is that + // the 'to' space is empty. The following sequence of 'copy' operations + // can use as much memory in sum as pre-allocated here. + // + void prepare (size_t bytes); + + // Does the memory pointed to by 'p' belong to this arena? More precisely + // to the 'from' space, since that is the only one remaining after 'swap'. + // + bool contains (void *p) const { + char *c = (char *) p; + return (from.start <= c && c < from.top) || + (to.start <= c && c < to.top); + } + + // Allocate that amount of memory in 'to' space. This assumes the 'to' + // space has been prepared to hold enough memory with 'prepare'. Then + // copy the memory pointed to by 'p' of size 'bytes'. Note that it does + // not matter whether 'p' is in 'from' or allocated outside of the arena. + // + char *copy (const char *p, size_t bytes) { + char *res = to.top; + to.top += bytes; + assert (to.top <= to.end); + memcpy (res, p, bytes); + return res; + } + + // Completely delete 'from' space and then replace 'from' by 'to' (by + // pointer swapping). Everything previously allocated (in 'from') and not + // explicitly copied to 'to' with 'copy' becomes invalid. + // + void swap (); +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/assume.cpp b/src/sat/cadical/assume.cpp new file mode 100644 index 000000000..a49d36af0 --- /dev/null +++ b/src/sat/cadical/assume.cpp @@ -0,0 +1,607 @@ +#include "internal.hpp" +#include "options.hpp" + +namespace CaDiCaL { + +// Failed literal handling as pioneered by MiniSAT. This first function +// adds an assumption literal onto the assumption stack. + +void Internal::assume (int lit) { + if (level && !opts.ilbassumptions) + backtrack (); + else if (val (lit) < 0) + backtrack (max (0, var (lit).level - 1)); + Flags &f = flags (lit); + const unsigned char bit = bign (lit); + if (f.assumed & bit) { + LOG ("ignoring already assumed %d", lit); + return; + } + LOG ("assume %d", lit); + f.assumed |= bit; + assumptions.push_back (lit); + freeze (lit); +} + +// for LRAT we actually need to implement recursive DFS +// for non-lrat use BFS. TODO: maybe derecursify to avoid stack overflow +// +void Internal::assume_analyze_literal (int lit) { + assert (lit); + Flags &f = flags (lit); + if (f.seen) + return; + f.seen = true; + analyzed.push_back (lit); + Var &v = var (lit); + assert (val (lit) < 0); + if (v.reason == external_reason) { + v.reason = wrapped_learn_external_reason_clause (-lit); + assert (v.reason || !v.level); + } + assert (v.reason != external_reason); + if (!v.level) { + int64_t id = unit_id (-lit); + lrat_chain.push_back (id); + return; + } + if (v.reason) { + assert (v.level); + LOG (v.reason, "analyze reason"); + for (const auto &other : *v.reason) { + assume_analyze_literal (other); + } + lrat_chain.push_back (v.reason->id); + return; + } + assert (assumed (-lit)); + LOG ("failed assumption %d", -lit); + clause.push_back (lit); +} + +void Internal::assume_analyze_reason (int lit, Clause *reason) { + assert (reason); + assert (lrat_chain.empty ()); + assert (reason != external_reason); + assert (lrat); + for (const auto &other : *reason) + if (other != lit) + assume_analyze_literal (other); + lrat_chain.push_back (reason->id); +} + +// Find all failing assumptions starting from the one on the assumption +// stack with the lowest decision level. This goes back to MiniSAT and is +// called 'analyze_final' there. + +void Internal::failing () { + + START (analyze); + + LOG ("analyzing failing assumptions"); + + assert (analyzed.empty ()); + assert (clause.empty ()); + assert (lrat_chain.empty ()); + assert (!marked_failed); + assert (!conflict_id); + + if (!unsat_constraint) { + // Search for failing assumptions in the (internal) assumption stack. + + // There are in essence three cases: (1) An assumption is falsified on + // the root-level and then 'failed_unit' is set to that assumption, (2) + // two clashing assumptions are assumed and then 'failed_clashing' is + // set to the second assumed one, or otherwise (3) there is a failing + // assumption 'first_failed' with minimum (non-zero) decision level + // 'failed_level'. + + int failed_unit = 0; + int failed_clashing = 0; + int first_failed = 0; + int failed_level = INT_MAX; + int efailed = 0; + + for (auto &elit : external->assumptions) { + int lit = external->e2i[abs (elit)]; + if (elit < 0) + lit = -lit; + if (val (lit) >= 0) + continue; + const Var &v = var (lit); + if (!v.level) { + failed_unit = lit; + efailed = elit; + break; + } + if (failed_clashing) + continue; + if (v.reason == external_reason) { + Var &ev = var (lit); + ev.reason = learn_external_reason_clause (-lit); + if (!ev.reason) { + ev.level = 0; + failed_unit = lit; + efailed = elit; + break; + } + ev.level = 0; + // Recalculate assignment level + for (const auto &other : *ev.reason) { + if (other == -lit) + continue; + assert (val (other)); + int tmp = var (other).level; + if (tmp > ev.level) + ev.level = tmp; + } + if (!ev.level) { + failed_unit = lit; + efailed = elit; + break; + } + } + assert (v.reason != external_reason); + if (!v.reason) { + failed_clashing = lit; + efailed = elit; + } else if (!first_failed || v.level < failed_level) { + first_failed = lit; + efailed = elit; + failed_level = v.level; + } + } + + assert (clause.empty ()); + + // Get the 'failed' assumption from one of the three cases. + int failed; + if (failed_unit) + failed = failed_unit; + else if (failed_clashing) + failed = failed_clashing; + else + failed = first_failed; + assert (failed); + assert (efailed); + + // In any case mark literal 'failed' as failed assumption. + { + Flags &f = flags (failed); + const unsigned bit = bign (failed); + assert (!(f.failed & bit)); + f.failed |= bit; + } + + // First case (1). + if (failed_unit) { + assert (failed == failed_unit); + LOG ("root-level falsified assumption %d", failed); + if (proof) { + if (lrat) { + unsigned eidx = (efailed > 0) + 2u * (unsigned) abs (efailed); + assert ((size_t) eidx < external->ext_units.size ()); + const int64_t id = external->ext_units[eidx]; + if (id) { + lrat_chain.push_back (id); + } else { + int64_t id = unit_id (-failed_unit); + lrat_chain.push_back (id); + } + } + proof->add_assumption_clause (++clause_id, -efailed, lrat_chain); + conclusion.push_back (clause_id); + lrat_chain.clear (); + } + goto DONE; + } + + // Second case (2). + if (failed_clashing) { + assert (failed == failed_clashing); + LOG ("clashing assumptions %d and %d", failed, -failed); + Flags &f = flags (-failed); + const unsigned bit = bign (-failed); + assert (!(f.failed & bit)); + f.failed |= bit; + if (proof) { + vector clash = {externalize (failed), externalize (-failed)}; + proof->add_assumption_clause (++clause_id, clash, lrat_chain); + conclusion.push_back (clause_id); + } + goto DONE; + } + + // Fall through to third case (3). + LOG ("starting with assumption %d falsified on minimum decision level " + "%d", + first_failed, failed_level); + + assert (first_failed); + assert (failed_level > 0); + + // The 'analyzed' stack serves as working stack for a BFS through the + // implication graph until decisions, which are all assumptions, or + // units are reached. This is simpler than corresponding code in + // 'analyze'. + { + LOG ("failed assumption %d", first_failed); + Flags &f = flags (first_failed); + assert (!f.seen); + f.seen = true; + assert (f.failed & bign (first_failed)); + analyzed.push_back (-first_failed); + clause.push_back (-first_failed); + } + } else { + // unsat_constraint + // The assumptions necessary to fail each literal in the constraint are + // collected. + for (auto lit : constraint) { + lit *= -1; + assert (lit != INT_MIN); + flags (lit).seen = true; + analyzed.push_back (lit); + } + } + + { + // used for unsat_constraint lrat + vector> constraint_chains; + vector> constraint_clauses; + vector sum_constraints; + vector econstraints; + for (auto &elit : external->constraint) { + int lit = external->e2i[abs (elit)]; + if (elit < 0) + lit = -lit; + if (!lit) + continue; + Flags &f = flags (lit); + if (f.seen) + continue; + if (std::find (econstraints.begin (), econstraints.end (), elit) != + econstraints.end ()) + continue; + econstraints.push_back (elit); + } + + // no LRAT do bfs as it was before + if (!lrat) { + size_t next = 0; + while (next < analyzed.size ()) { + const int lit = analyzed[next++]; + assert (val (lit) > 0); + Var &v = var (lit); + if (!v.level) + continue; + if (v.reason == external_reason) { + v.reason = wrapped_learn_external_reason_clause (lit); + if (!v.reason) { + v.level = 0; + continue; + } + } + assert (v.reason != external_reason); + if (v.reason) { + assert (v.level); + LOG (v.reason, "analyze reason"); + for (const auto &other : *v.reason) { + Flags &f = flags (other); + if (f.seen) + continue; + f.seen = true; + assert (val (other) < 0); + analyzed.push_back (-other); + } + } else { + assert (assumed (lit)); + LOG ("failed assumption %d", lit); + clause.push_back (-lit); + Flags &f = flags (lit); + const unsigned bit = bign (lit); + assert (!(f.failed & bit)); + f.failed |= bit; + } + } + clear_analyzed_literals (); + } else if (!unsat_constraint) { // LRAT for case (3) + assert (clause.size () == 1); + const int lit = clause[0]; + Var &v = var (lit); + assert (v.reason); + if (v.reason == external_reason) { // does this even happen? + v.reason = wrapped_learn_external_reason_clause (lit); + } + assert (v.reason != external_reason); + if (v.reason) + assume_analyze_reason (lit, v.reason); + else { + int64_t id = unit_id (lit); + lrat_chain.push_back (id); + } + for (auto &lit : clause) { + Flags &f = flags (lit); + const unsigned bit = bign (-lit); + if (!(f.failed & bit)) + f.failed |= bit; + } + clear_analyzed_literals (); + } else { // LRAT for unsat_constraint + assert (clause.empty ()); + clear_analyzed_literals (); + for (auto lit : constraint) { + // make sure nothing gets marked failed twice + // also might shortcut the case where + // lrat_chain is empty because clause is tautological + assert (lit != INT_MIN); + assume_analyze_literal (lit); + vector empty; + vector empty2; + constraint_chains.push_back (empty); + constraint_clauses.push_back (empty2); + for (auto ign : clause) { + constraint_clauses.back ().push_back (ign); + Flags &f = flags (ign); + const unsigned bit = bign (-ign); + if (!(f.failed & bit)) { + sum_constraints.push_back (ign); + assert (!(f.failed & bit)); + f.failed |= bit; + } + } + clause.clear (); + clear_analyzed_literals (); + for (auto p : lrat_chain) { + constraint_chains.back ().push_back (p); + } + lrat_chain.clear (); + } + for (auto &lit : sum_constraints) + clause.push_back (lit); + } + clear_analyzed_literals (); + + // Doing clause minimization here does not do anything because + // the clause already contains only one literal of each level + // and minimization can never reduce the number of levels + + VERBOSE (1, "found %zd failed assumptions %.0f%%", clause.size (), + percent (clause.size (), assumptions.size ())); + + // We do not actually need to learn this clause, since the conflict is + // forced already by some other clauses. There is also no bumping + // of variables nor clauses necessary. But we still want to check + // correctness of the claim that the determined subset of failing + // assumptions are a high-level core or equivalently their negations + // form a unit-implied clause. + // + if (!unsat_constraint) { + external->check_learned_clause (); + if (proof) { + vector eclause; + for (auto &lit : clause) + eclause.push_back (externalize (lit)); + proof->add_assumption_clause (++clause_id, eclause, lrat_chain); + conclusion.push_back (clause_id); + } + } else { + assert (!lrat || (constraint.size () == constraint_clauses.size () && + constraint.size () == constraint_chains.size ())); + for (auto p = constraint.rbegin (); p != constraint.rend (); p++) { + const auto &lit = *p; + if (lrat) { + clause.clear (); + for (auto &ign : constraint_clauses.back ()) + clause.push_back (ign); + constraint_clauses.pop_back (); + } + clause.push_back (-lit); + external->check_learned_clause (); + if (proof) { + if (lrat) { + for (auto p : constraint_chains.back ()) { + lrat_chain.push_back (p); + } + constraint_chains.pop_back (); + LOG (lrat_chain, "assume proof chain with constraints"); + } + vector eclause; + for (auto &lit : clause) + eclause.push_back (externalize (lit)); + proof->add_assumption_clause (++clause_id, eclause, lrat_chain); + conclusion.push_back (clause_id); + lrat_chain.clear (); + } + clause.pop_back (); + } + if (proof) { + for (auto &elit : econstraints) { + if (lrat) { + unsigned eidx = (elit > 0) + 2u * (unsigned) abs (elit); + assert ((size_t) eidx < external->ext_units.size ()); + const int64_t id = external->ext_units[eidx]; + if (id) { + lrat_chain.push_back (id); + } else { + int lit = external->e2i[abs (elit)]; + if (elit < 0) + lit = -lit; + int64_t id = unit_id (-lit); + lrat_chain.push_back (id); + } + } + proof->add_assumption_clause (++clause_id, -elit, lrat_chain); + conclusion.push_back (clause_id); + lrat_chain.clear (); + } + } + } + lrat_chain.clear (); + clause.clear (); + } + +DONE: + + STOP (analyze); +} + +bool Internal::failed (int lit) { + if (!marked_failed) { + if (!conflict_id) + failing (); + marked_failed = true; + } + conclude_unsat (); + Flags &f = flags (lit); + const unsigned bit = bign (lit); + return (f.failed & bit) != 0; +} + +void Internal::conclude_unsat () { + if (!proof || concluded) + return; + concluded = true; + if (!marked_failed) { + assert (conclusion.empty ()); + if (!conflict_id) + failing (); + marked_failed = true; + } + ConclusionType con; + if (conflict_id) + con = CONFLICT; + else if (unsat_constraint) + con = CONSTRAINT; + else + con = ASSUMPTIONS; + proof->conclude_unsat (con, conclusion); +} + +void Internal::reset_concluded () { + if (proof) + proof->reset_assumptions (); + if (concluded) { + LOG ("reset concluded"); + concluded = false; + } + if (conflict_id) { + assert (conclusion.size () == 1); + return; + } + conclusion.clear (); +} + +// Add the start of each incremental phase (leaving the state +// 'UNSATISFIABLE' actually) we reset all assumptions. + +void Internal::reset_assumptions () { + for (const auto &lit : assumptions) { + Flags &f = flags (lit); + const unsigned char bit = bign (lit); + f.assumed &= ~bit; + f.failed &= ~bit; + melt (lit); + } + LOG ("cleared %zd assumptions", assumptions.size ()); + assumptions.clear (); + marked_failed = true; +} + +struct sort_assumptions_positive_rank { + Internal *internal; + + // Decision level could be 'INT_MAX' and thus 'level + 1' could overflow. + // Therefore we carefully have to use 'unsigned' for levels below. + + const unsigned max_level; + + sort_assumptions_positive_rank (Internal *s) + : internal (s), max_level (s->level + 1u) {} + + typedef uint64_t Type; + + // Set assumptions first, then sorted by position on the trail + // unset literals are sorted by literal value. + + Type operator() (const int &a) const { + const int val = internal->val (a); + const bool assigned = (val != 0); + const Var &v = internal->var (a); + uint64_t res = (assigned ? (unsigned) v.level : max_level); + res <<= 32; + res |= (assigned ? v.trail : abs (a)); + return res; + } +}; + +struct sort_assumptions_smaller { + Internal *internal; + sort_assumptions_smaller (Internal *s) : internal (s) {} + bool operator() (const int &a, const int &b) const { + return sort_assumptions_positive_rank (internal) (a) < + sort_assumptions_positive_rank (internal) (b); + } +}; + +// Sort the assumptions by the current position on the trail and backtrack +// to the first place where the assumptions and the current trail differ. + +void Internal::sort_and_reuse_assumptions () { + assert (opts.ilbassumptions); + if (assumptions.empty ()) + return; + MSORT (opts.radixsortlim, assumptions.begin (), assumptions.end (), + sort_assumptions_positive_rank (this), + sort_assumptions_smaller (this)); + + unsigned max_level = 0; + // assumptions are sorted by level, with unset at the end + for (auto lit : assumptions) { + if (val (lit)) + max_level = var (lit).level; + else + break; + } + + const unsigned size = min (level + 1u, max_level + 1); + assert ((size_t) level == control.size () - 1); + LOG (assumptions, "sorted assumptions"); + int target = 0; + for (unsigned i = 1, j = 0; i < size;) { + const Level &l = control[i]; + const int lit = l.decision; + const int alit = assumptions[j]; + const int lev = i; + target = lev; + if (val (alit) > 0 && + var (alit).level < lev) { // we can ignore propagated assumptions + LOG ("ILB skipping propagation %d", alit); + ++j; + continue; + } + if (!lit) { // skip fake decisions + target = lev - 1; + break; + } + ++i, ++j; + assert (var (lit).level == lev); + if (l.decision == alit) { + continue; + } + target = lev - 1; + LOG ("first different literal %d on the trail and %d from the " + "assumptions", + lit, alit); + break; + } + if (target < level) + backtrack (target); + LOG ("assumptions allow for reuse of trail up to level %d", level); + // COVER (target > 1); + if ((size_t) level > assumptions.size ()) + stats.assumptionsreused += assumptions.size (); + else + stats.assumptionsreused += level; +} +} // namespace CaDiCaL diff --git a/src/sat/cadical/averages.cpp b/src/sat/cadical/averages.cpp new file mode 100644 index 000000000..971598bd8 --- /dev/null +++ b/src/sat/cadical/averages.cpp @@ -0,0 +1,34 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +void Internal::init_averages () { + + LOG ("initializing averages"); + + INIT_EMA (averages.current.jump, opts.emajump); + INIT_EMA (averages.current.level, opts.emalevel); + INIT_EMA (averages.current.size, opts.emasize); + + INIT_EMA (averages.current.glue.fast, opts.emagluefast); + INIT_EMA (averages.current.glue.slow, opts.emaglueslow); + + INIT_EMA (averages.current.decisions, opts.emadecisions); + + INIT_EMA (averages.current.trail.fast, opts.ematrailfast); + INIT_EMA (averages.current.trail.slow, opts.ematrailslow); + + assert (!averages.swapped); +} + +void Internal::swap_averages () { + LOG ("saving current averages"); + swap (averages.current, averages.saved); + if (!averages.swapped) + init_averages (); + else + LOG ("swapping in previously saved averages"); + averages.swapped++; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/averages.hpp b/src/sat/cadical/averages.hpp new file mode 100644 index 000000000..9f259509d --- /dev/null +++ b/src/sat/cadical/averages.hpp @@ -0,0 +1,37 @@ +#ifndef _averages_hpp_INCLUDED +#define _averages_hpp_INCLUDED + +#include "ema.hpp" // alphabetically after 'averages.hpp' + +namespace CaDiCaL { + +struct Averages { + + int64_t swapped; + + struct { + + struct { + EMA fast; // average fast (small window) moving glucose level + EMA slow; // average slow (large window) moving glucose level + } glue; + + struct { + EMA fast; // average fast (small window) moving trail level + EMA slow; // average slow (large window) moving trail level + } trail; + + EMA decisions; + + EMA size; // average learned clause size + EMA jump; // average (potential non-chronological) back-jump level + EMA level; // average back track level after conflict + + } current, saved; + + Averages () : swapped (0) {} +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/backtrack.cpp b/src/sat/cadical/backtrack.cpp new file mode 100644 index 000000000..3a70b2775 --- /dev/null +++ b/src/sat/cadical/backtrack.cpp @@ -0,0 +1,173 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +// The global assignment stack can only be (partially) reset through +// 'backtrack' which is the only function using 'unassign' (inlined and thus +// local to this file). It turns out that 'unassign' does not need a +// specialization for 'probe' nor 'vivify' and thus it is shared. + +inline void Internal::unassign (int lit) { + assert (val (lit) > 0); + set_val (lit, 0); + + int idx = vidx (lit); + LOG ("unassign %d @ %d", lit, var (idx).level); + num_assigned--; + + // In the standard EVSIDS variable decision heuristic of MiniSAT, we need + // to push variables which become unassigned back to the heap. + // + if (!scores.contains (idx)) + scores.push_back (idx); + + // For VMTF we need to update the 'queue.unassigned' pointer in case this + // variable sits after the variable to which 'queue.unassigned' currently + // points. See our SAT'15 paper for more details on this aspect. + // + if (queue.bumped < btab[idx]) + update_queue_unassigned (idx); +} + +/*------------------------------------------------------------------------*/ + +// Update the current target maximum assignment and also the very best +// assignment. Whether a trail produces a conflict is determined during +// propagation. Thus that all functions in the 'search' loop after +// propagation can assume that 'no_conflict_until' is valid. If a conflict +// is found then the trail before the last decision is used (see the end of +// 'propagate'). During backtracking we can then save this largest +// propagation conflict free assignment. It is saved as both 'target' +// assignment for picking decisions in 'stable' mode and if it is the +// largest ever such assignment also as 'best' assignment. This 'best' +// assignment can then be used in future stable decisions after the next +// 'rephase_best' overwrites saved phases with it. + +void Internal::update_target_and_best () { + + bool reset = (rephased && stats.conflicts > last.rephase.conflicts); + + if (reset) { + target_assigned = 0; + if (rephased == 'B') + best_assigned = 0; // update it again + } + + if (no_conflict_until > target_assigned) { + copy_phases (phases.target); + target_assigned = no_conflict_until; + LOG ("new target trail level %zu", target_assigned); + } + + if (no_conflict_until > best_assigned) { + copy_phases (phases.best); + best_assigned = no_conflict_until; + LOG ("new best trail level %zu", best_assigned); + } + + if (reset) { + report (rephased); + rephased = 0; + } +} + +/*------------------------------------------------------------------------*/ + +void Internal::backtrack (int new_level) { + assert (new_level <= level); + if (new_level == level) + return; + + update_target_and_best (); + backtrack_without_updating_phases (new_level); +} + +void Internal::backtrack_without_updating_phases (int new_level) { + + assert (new_level <= level); + if (new_level == level) + return; + + stats.backtracks++; + + assert (num_assigned == trail.size ()); + + const size_t assigned = control[new_level + 1].trail; + + LOG ("backtracking to decision level %d with decision %d and trail %zd", + new_level, control[new_level].decision, assigned); + + const size_t end_of_trail = trail.size (); + size_t i = assigned, j = i; + +#ifdef LOGGING + int unassigned = 0; +#endif + int reassigned = 0; + + notify_backtrack (new_level); + if (external_prop && !external_prop_is_lazy && !private_steps && + notified > assigned) { + LOG ("external propagator is notified about some unassignments (trail: " + "%zd, notified: %zd).", + trail.size (), notified); + notified = assigned; + } + + while (i < end_of_trail) { + int lit = trail[i++]; + Var &v = var (lit); + if (v.level > new_level) { + unassign (lit); +#ifdef LOGGING + unassigned++; +#endif + } else { + // This is the essence of the SAT'18 paper on chronological + // backtracking. It is possible to just keep out-of-order assigned + // literals on the trail without breaking the solver (after some + // modifications to 'analyze' - see 'opts.chrono' guarded code there). + assert (opts.chrono || external_prop || did_external_prop); +#ifdef LOGGING + if (!v.level) + LOG ("reassign %d @ 0 unit clause %d", lit, lit); + else + LOG (v.reason, "reassign %d @ %d", lit, v.level); +#endif + trail[j] = lit; + v.trail = j++; + reassigned++; + } + } + trail.resize (j); + LOG ("unassigned %d literals %.0f%%", unassigned, + percent (unassigned, unassigned + reassigned)); + LOG ("reassigned %d literals %.0f%%", reassigned, + percent (reassigned, unassigned + reassigned)); + + if (propagated > assigned) + propagated = assigned; + if (propagated2 > assigned) + propagated2 = assigned; + if (no_conflict_until > assigned) + no_conflict_until = assigned; + + propergated = 0; // Always go back to root-level. + + assert (notified <= assigned + reassigned); + if (reassigned) { + notify_assignments (); + } + + control.resize (new_level + 1); + level = new_level; + if (tainted_literal) { + assert (opts.ilb); + if (!val (tainted_literal)) { + tainted_literal = 0; + } + } + assert (num_assigned == trail.size ()); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/backward.cpp b/src/sat/cadical/backward.cpp new file mode 100644 index 000000000..e7228c521 --- /dev/null +++ b/src/sat/cadical/backward.cpp @@ -0,0 +1,231 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Provide eager backward subsumption for resolved clauses. + +// The eliminator maintains a queue of clauses that are new and have to be +// checked to subsume or strengthen other (longer or same size) clauses. + +void Eliminator::enqueue (Clause *c) { + if (!internal->opts.elimbackward) + return; + if (c->enqueued) + return; + LOG (c, "backward enqueue"); + backward.push (c); + c->enqueued = true; +} + +Clause *Eliminator::dequeue () { + if (backward.empty ()) + return 0; + Clause *res = backward.front (); + backward.pop (); + assert (res->enqueued); + res->enqueued = false; + LOG (res, "backward dequeue"); + return res; +} + +Eliminator::~Eliminator () { + while (dequeue ()) + ; +} + +/*------------------------------------------------------------------------*/ + +void Internal::elim_backward_clause (Eliminator &eliminator, Clause *c) { + assert (opts.elimbackward); + assert (!c->redundant); + if (c->garbage) + return; + LOG (c, "attempting backward subsumption and strengthening with"); + size_t len = UINT_MAX; + unsigned size = 0; + int best = 0; + bool satisfied = false; + assert (mini_chain.empty ()); + for (const auto &lit : *c) { + const signed char tmp = val (lit); + if (tmp > 0) { + satisfied = true; + break; + } + if (tmp < 0) + continue; + size_t l = occs (lit).size (); + LOG ("literal %d occurs %zd times", lit, l); + if (l < len) + best = lit, len = l; + mark (lit); + size++; + } + if (satisfied) { + LOG ("clause actually already satisfied"); + elim_update_removed_clause (eliminator, c); + mark_garbage (c); + } else if (len > (size_t) opts.elimocclim) { + LOG ("skipping backward subsumption due to too many occurrences"); + } else { + assert (len); + LOG ("literal %d has smallest number of occurrences %zd", best, len); + LOG ("marked %d literals in clause of size %d", size, c->size); + for (auto &d : occs (best)) { + if (d == c) + continue; + if (d->garbage) + continue; + if ((unsigned) d->size < size) + continue; + int negated = 0; + unsigned found = 0; + satisfied = false; + for (const auto &lit : *d) { + signed char tmp = val (lit); + if (tmp > 0) { + satisfied = true; + break; + } + if (tmp < 0) + continue; + tmp = marked (lit); + if (!tmp) + continue; + if (tmp < 0) { + if (negated) { + size = UINT_MAX; + break; + } else + negated = lit; + } + if (++found == size) + break; + } + if (satisfied) { + LOG (d, "found satisfied clause"); + elim_update_removed_clause (eliminator, d); + mark_garbage (d); + } else if (found == size) { + if (!negated) { + LOG (d, "found subsumed clause"); + elim_update_removed_clause (eliminator, d); + mark_garbage (d); + stats.subsumed++; + stats.elimbwsub++; + } else { + int unit = 0; + assert (minimize_chain.empty ()); + assert (analyzed.empty ()); + assert (lrat_chain.empty ()); + // figure out wether we strengthen c or get a new unit + for (const auto &lit : *d) { + const signed char tmp = val (lit); + if (tmp < 0) { + if (!lrat) + continue; + Flags &f = flags (lit); + assert (!f.seen); + if (f.seen) + continue; + f.seen = true; + analyzed.push_back (lit); + continue; + } + if (tmp > 0) { + satisfied = true; + break; + } + if (lit == negated) + continue; + if (unit) { + unit = INT_MIN; + continue; // needed to guarantee d is not satsified + } else + unit = lit; + } + if (lrat && !satisfied) { + // if we found a unit we need to add all unit ids from + // {c\d}U{d\c} otherwise just the unit ids from {c\d} + for (const auto &lit : *c) { + const signed char tmp = val (lit); + assert (tmp <= 0); + if (tmp >= 0) + continue; + Flags &f = flags (lit); + if (f.seen && unit && unit == INT_MIN) { + f.seen = false; + continue; + } else if (!f.seen) { + f.seen = true; + analyzed.push_back (lit); + } + } + if (unit == INT_MIN) { // we do not need units from {d\c} + for (const auto &lit : *d) { + flags (lit).seen = false; + } + } + for (const auto &lit : analyzed) { + Flags &f = flags (lit); + if (!f.seen) { + f.seen = true; + continue; + } + int64_t id = unit_id (-lit); + lrat_chain.push_back (id); + } + clear_analyzed_literals (); + lrat_chain.push_back (d->id); + lrat_chain.push_back (c->id); + } else if (lrat) + clear_analyzed_literals (); + if (satisfied) { + assert (lrat_chain.empty ()); + mark_garbage (d); + elim_update_removed_clause (eliminator, d); + } else if (unit && unit != INT_MIN) { + assert (unit); + LOG (d, "unit %d through hyper unary resolution with", unit); + assign_unit (unit); + elim_propagate (eliminator, unit); + lrat_chain.clear (); + break; + } else if (occs (negated).size () <= (size_t) opts.elimocclim) { + strengthen_clause (d, negated); + remove_occs (occs (negated), d); + elim_update_removed_lit (eliminator, negated); + stats.elimbwstr++; + assert (negated != best); + eliminator.enqueue (d); + } + lrat_chain.clear (); + } + } + } + } + mini_chain.clear (); + unmark (c); +} + +/*------------------------------------------------------------------------*/ + +void Internal::elim_backward_clauses (Eliminator &eliminator) { + if (!opts.elimbackward) { + assert (eliminator.backward.empty ()); + return; + } + START (backward); + LOG ("attempting backward subsumption and strengthening with %zd clauses", + eliminator.backward.size ()); + Clause *c; + while (!unsat && (c = eliminator.dequeue ())) + elim_backward_clause (eliminator, c); + STOP (backward); +} + +/*------------------------------------------------------------------------*/ + +} // namespace CaDiCaL diff --git a/src/sat/cadical/bins.cpp b/src/sat/cadical/bins.cpp new file mode 100644 index 000000000..406074eff --- /dev/null +++ b/src/sat/cadical/bins.cpp @@ -0,0 +1,22 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Binary implication graph lists. + +void Internal::init_bins () { + assert (big.empty ()); + if (big.size () < 2 * vsize) + big.resize (2 * vsize, Bins ()); + LOG ("initialized binary implication graph"); +} + +void Internal::reset_bins () { + assert (!big.empty ()); + erase_vector (big); + LOG ("reset binary implication graph"); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/bins.hpp b/src/sat/cadical/bins.hpp new file mode 100644 index 000000000..ec0f0f1dc --- /dev/null +++ b/src/sat/cadical/bins.hpp @@ -0,0 +1,22 @@ +#ifndef _bins_hpp_INCLUDED +#define _bins_hpp_INCLUDED + +#include "util.hpp" // Alphabetically after 'bins'. + +namespace CaDiCaL { + +using namespace std; + +struct Bin { + int lit; + int64_t id; +}; + +typedef vector Bins; + +inline void shrink_bins (Bins &bs) { shrink_vector (bs); } +inline void erase_bins (Bins &bs) { erase_vector (bs); } + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/block.cpp b/src/sat/cadical/block.cpp new file mode 100644 index 000000000..11721d8bd --- /dev/null +++ b/src/sat/cadical/block.cpp @@ -0,0 +1,824 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// This implements an inprocessing version of blocked clause elimination and +// is assumed to be triggered just before bounded variable elimination. It +// has a separate 'block' flag while variable elimination uses 'elim'. +// Thus it only tries to block clauses on a literal which was removed in an +// irredundant clause in negated form before and has not been tried to use +// as blocking literal since then. + +/*------------------------------------------------------------------------*/ + +inline bool block_more_occs_size::operator() (unsigned a, unsigned b) { + size_t s = internal->noccs (-internal->u2i (a)); + size_t t = internal->noccs (-internal->u2i (b)); + if (s > t) + return true; + if (s < t) + return false; + s = internal->noccs (internal->u2i (a)); + t = internal->noccs (internal->u2i (b)); + if (s > t) + return true; + if (s < t) + return false; + return a > b; +} + +/*------------------------------------------------------------------------*/ + +// Determine whether 'c' is blocked on 'lit', by first marking all its +// literals and then checking all resolvents with negative clauses (with +// '-lit') are tautological. We use a move-to-front scheme for both the +// occurrence list of negative clauses (with '-lit') and then for literals +// within each such clause. The clause move-to-front scheme has the goal to +// find non-tautological clauses faster in the future, while the literal +// move-to-front scheme has the goal to faster find the matching literal, +// which makes the resolvent tautological (again in the future). + +bool Internal::is_blocked_clause (Clause *c, int lit) { + + LOG (c, "trying to block on %d", lit); + + assert (c->size >= opts.blockminclslim); + assert (c->size <= opts.blockmaxclslim); + assert (active (lit)); + assert (!val (lit)); + assert (!c->garbage); + assert (!c->redundant); + assert (!level); + + mark (c); // First mark all literals in 'c'. + + Occs &os = occs (-lit); + LOG ("resolving against at most %zd clauses with %d", os.size (), -lit); + + bool res = true; // Result is true if all resolvents tautological. + + // Can not use 'auto' here since we update 'os' during traversal. + // + const auto end_of_os = os.end (); + auto i = os.begin (); + + Clause *prev_d = 0; // Previous non-tautological clause. + + for (; i != end_of_os; i++) { + // Move the first clause with non-tautological resolvent to the front of + // the occurrence list to improve finding it faster later. + // + Clause *d = *i; + + assert (!d->garbage); + assert (!d->redundant); + assert (d->size <= opts.blockmaxclslim); + + *i = prev_d; // Move previous non-tautological clause + prev_d = d; // backwards but remember clause at this position. + + LOG (d, "resolving on %d against", lit); + stats.blockres++; + + int prev_other = 0; // Previous non-tautological literal. + + // No 'auto' since we update literals of 'd' during traversal. + // + const const_literal_iterator end_of_d = d->end (); + literal_iterator l; + + for (l = d->begin (); l != end_of_d; l++) { + // Same move-to-front mechanism for literals within a clause. It + // moves the first negatively marked literal to the front to find it + // faster in the future. + // + const int other = *l; + *l = prev_other; + prev_other = other; + if (other == -lit) + continue; + assert (other != lit); + assert (active (other)); + assert (!val (other)); + if (marked (other) < 0) { + LOG ("found tautological literal %d", other); + d->literals[0] = other; // Move to front of 'd'. + break; + } + } + + if (l == end_of_d) { + LOG ("no tautological literal found"); + // + // Since we did not find a tautological literal we restore the old + // order of literals in the clause. + // + const const_literal_iterator begin_of_d = d->begin (); + while (l-- != begin_of_d) { + const int other = *l; + *l = prev_other; + prev_other = other; + } + res = false; // Now 'd' is a witness that 'c' is not blocked. + os[0] = d; // Move it to the front of the occurrence list. + break; + } + } + unmark (c); // ... all literals of the candidate clause. + + // If all resolvents are tautological and thus the clause is blocked we + // restore the old order of clauses in the occurrence list of '-lit'. + // + if (res) { + assert (i == end_of_os); + const auto boc = os.begin (); + while (i != boc) { + Clause *d = *--i; + *i = prev_d; + prev_d = d; + } + } + + return res; +} + +/*------------------------------------------------------------------------*/ + +void Internal::block_schedule (Blocker &blocker) { + // Set skip flags for all literals in too large clauses. + // + for (const auto &c : clauses) { + + if (c->garbage) + continue; + if (c->redundant) + continue; + if (c->size <= opts.blockmaxclslim) + continue; + + for (const auto &lit : *c) + mark_skip (-lit); + } + + // Connect all literal occurrences in irredundant clauses. + // + for (const auto &c : clauses) { + + if (c->garbage) + continue; + if (c->redundant) + continue; + + for (const auto &lit : *c) { + assert (active (lit)); + assert (!val (lit)); + occs (lit).push_back (c); + } + } + + // We establish the invariant that 'noccs' gives the number of actual + // occurrences of 'lit' in non-garbage clauses, while 'occs' might still + // refer to garbage clauses, thus 'noccs (lit) <= occs (lit).size ()'. It + // is expensive to remove references to garbage clauses from 'occs' during + // blocked clause elimination, but decrementing 'noccs' is cheap. + + for (auto lit : lits) { + if (!active (lit)) + continue; + assert (!val (lit)); + Occs &os = occs (lit); + noccs (lit) = os.size (); + } + + // Now we fill the schedule (priority queue) of candidate literals to be + // tried as blocking literals. It is probably slightly faster to do this + // in one go after all occurrences have been determined, instead of + // filling the priority queue during pushing occurrences. Filling the + // schedule can not be fused with the previous loop (easily) since we + // first have to initialize 'noccs' for both 'lit' and '-lit'. + +#ifndef QUIET + int skipped = 0; +#endif + + for (auto idx : vars) { + if (!active (idx)) + continue; + if (frozen (idx)) { +#ifndef QUIET + skipped += 2; +#endif + continue; + } + assert (!val (idx)); + for (int sign = -1; sign <= 1; sign += 2) { + const int lit = sign * idx; + if (marked_skip (lit)) { +#ifndef QUIET + skipped++; +#endif + continue; + } + if (!marked_block (lit)) + continue; + unmark_block (lit); + LOG ("scheduling %d with %" PRId64 " positive and %" PRId64 + " negative occurrences", + lit, noccs (lit), noccs (-lit)); + blocker.schedule.push_back (vlit (lit)); + } + } + + PHASE ("block", stats.blockings, + "scheduled %zd candidate literals %.2f%% (%d skipped %.2f%%)", + blocker.schedule.size (), + percent (blocker.schedule.size (), 2.0 * active ()), skipped, + percent (skipped, 2.0 * active ())); +} + +/*------------------------------------------------------------------------*/ + +// A literal is pure if it only occurs positive. Then all clauses in which +// it occurs are blocked on it. This special case can be implemented faster +// than trying to block literals with at least one negative occurrence and +// is thus handled separately. It also allows to avoid pushing blocked +// clauses onto the extension stack. + +void Internal::block_pure_literal (Blocker &blocker, int lit) { + if (frozen (lit)) + return; + assert (active (lit)); + + Occs &pos = occs (lit); + Occs &nos = occs (-lit); + + assert (!noccs (-lit)); +#ifndef NDEBUG + for (const auto &c : nos) + assert (c->garbage); +#endif + stats.blockpurelits++; + LOG ("found pure literal %d", lit); +#ifdef LOGGING + int64_t pured = 0; +#endif + for (const auto &c : pos) { + if (c->garbage) + continue; + assert (!c->redundant); + LOG (c, "pure literal %d in", lit); + blocker.reschedule.push_back (c); + if (proof) { + proof->weaken_minus (c); + } + external->push_clause_on_extension_stack (c, lit); + stats.blockpured++; + mark_garbage (c); +#ifdef LOGGING + pured++; +#endif + } + + erase_vector (pos); + erase_vector (nos); + + mark_pure (lit); + stats.blockpured++; + LOG ("blocking %" PRId64 " clauses on pure literal %d", pured, lit); +} + +/*------------------------------------------------------------------------*/ + +// If there is only one negative clause with '-lit' it is faster to mark it +// instead of marking all the positive clauses with 'lit' one after the +// other and then resolving against the negative clause. + +void Internal::block_literal_with_one_negative_occ (Blocker &blocker, + int lit) { + assert (active (lit)); + assert (!frozen (lit)); + assert (noccs (lit) > 0); + assert (noccs (-lit) == 1); + + Occs &nos = occs (-lit); + assert (nos.size () >= 1); + + Clause *d = 0; + for (const auto &c : nos) { + if (c->garbage) + continue; + assert (!d); + d = c; +#ifndef NDEBUG + break; +#endif + } + assert (d); + nos.resize (1); + nos[0] = d; + + if (d && d->size > opts.blockmaxclslim) { + LOG (d, "skipped common antecedent"); + return; + } + + assert (!d->garbage); + assert (!d->redundant); + assert (d->size <= opts.blockmaxclslim); + + LOG (d, "common %d antecedent", lit); + mark (d); + int64_t blocked = 0; +#ifdef LOGGING + int64_t skipped = 0; +#endif + Occs &pos = occs (lit); + + // Again no 'auto' since 'pos' is update during traversal. + // + const auto eop = pos.end (); + auto j = pos.begin (), i = j; + + for (; i != eop; i++) { + Clause *c = *j++ = *i; + + if (c->garbage) { + j--; + continue; + } + if (c->size > opts.blockmaxclslim) { +#ifdef LOGGING + skipped++; +#endif + continue; + } + if (c->size < opts.blockminclslim) { +#ifdef LOGGING + skipped++; +#endif + continue; + } + + LOG (c, "trying to block on %d", lit); + + // We use the same literal move-to-front strategy as in + // 'is_blocked_clause'. See there for more explanations. + + int prev_other = 0; // Previous non-tautological literal. + + // No 'auto' since literals of 'c' are updated during traversal. + // + const const_literal_iterator end_of_c = c->end (); + literal_iterator l; + + for (l = c->begin (); l != end_of_c; l++) { + const int other = *l; + *l = prev_other; + prev_other = other; + if (other == lit) + continue; + assert (other != -lit); + assert (active (other)); + assert (!val (other)); + if (marked (other) < 0) { + LOG ("found tautological literal %d", other); + c->literals[0] = other; // Move to front of 'c'. + break; + } + } + + if (l == end_of_c) { + LOG ("no tautological literal found"); + + // Restore old literal order in the clause because. + + const const_literal_iterator begin_of_c = c->begin (); + while (l-- != begin_of_c) { + const int other = *l; + *l = prev_other; + prev_other = other; + } + + continue; // ... with next candidate 'c' in 'pos'. + } + + blocked++; + LOG (c, "blocked"); + if (proof) { + proof->weaken_minus (c); + } + external->push_clause_on_extension_stack (c, lit); + blocker.reschedule.push_back (c); + mark_garbage (c); + j--; + } + if (j == pos.begin ()) + erase_vector (pos); + else + pos.resize (j - pos.begin ()); + + stats.blocked += blocked; + LOG ("blocked %" PRId64 " clauses on %d (skipped %" PRId64 ")", blocked, + lit, skipped); + + unmark (d); +} + +/*------------------------------------------------------------------------*/ + +// Determine the set of candidate clauses with 'lit', which are checked to +// be blocked by 'lit'. Filter out too large and small clauses and which do +// not have any negated other literal in any of the clauses with '-lit'. + +size_t Internal::block_candidates (Blocker &blocker, int lit) { + + assert (blocker.candidates.empty ()); + + Occs &pos = occs (lit); // Positive occurrences of 'lit'. + Occs &nos = occs (-lit); + + assert ((size_t) noccs (lit) <= pos.size ()); + assert ((size_t) noccs (-lit) == nos.size ()); // Already flushed. + + // Mark all literals in clauses with '-lit'. Note that 'mark2' uses + // separate bits for 'lit' and '-lit'. + // + for (const auto &c : nos) + mark2 (c); + + const auto eop = pos.end (); + auto j = pos.begin (), i = j; + + for (; i != eop; i++) { + Clause *c = *j++ = *i; + if (c->garbage) { + j--; + continue; + } + assert (!c->redundant); + if (c->size > opts.blockmaxclslim) + continue; + if (c->size < opts.blockminclslim) + continue; + const const_literal_iterator eoc = c->end (); + const_literal_iterator l; + for (l = c->begin (); l != eoc; l++) { + const int other = *l; + if (other == lit) + continue; + assert (other != -lit); + assert (active (other)); + assert (!val (other)); + if (marked2 (-other)) + break; + } + if (l != eoc) + blocker.candidates.push_back (c); + } + if (j == pos.begin ()) + erase_vector (pos); + else + pos.resize (j - pos.begin ()); + + assert (pos.size () == (size_t) noccs (lit)); // Now also flushed. + + for (const auto &c : nos) + unmark (c); + + return blocker.candidates.size (); +} + +/*------------------------------------------------------------------------*/ + +// Try to find a clause with '-lit' which does not have any literal in +// clauses with 'lit'. If such a clause exists no candidate clause can be +// blocked on 'lit' since all candidates would produce a non-tautological +// resolvent with that clause. + +Clause *Internal::block_impossible (Blocker &blocker, int lit) { + assert (noccs (-lit) > 1); + assert (blocker.candidates.size () > 1); + + for (const auto &c : blocker.candidates) + mark2 (c); + + Occs &nos = occs (-lit); + Clause *res = 0; + + for (const auto &c : nos) { + assert (!c->garbage); + assert (!c->redundant); + assert (c->size <= opts.blockmaxclslim); + const const_literal_iterator eoc = c->end (); + const_literal_iterator l; + for (l = c->begin (); l != eoc; l++) { + const int other = *l; + if (other == -lit) + continue; + assert (other != lit); + assert (active (other)); + assert (!val (other)); + if (marked2 (-other)) + break; + } + if (l == eoc) + res = c; + } + + for (const auto &c : blocker.candidates) + unmark (c); + + if (res) { + LOG (res, "common non-tautological resolvent producing"); + blocker.candidates.clear (); + } + + return res; +} + +/*------------------------------------------------------------------------*/ + +// In the general case we have at least two negative occurrences. + +void Internal::block_literal_with_at_least_two_negative_occs ( + Blocker &blocker, int lit) { + assert (active (lit)); + assert (!frozen (lit)); + assert (noccs (lit) > 0); + assert (noccs (-lit) > 1); + + Occs &nos = occs (-lit); + assert ((size_t) noccs (-lit) <= nos.size ()); + + int max_size = 0; + + // Flush all garbage clauses in occurrence list 'nos' of '-lit' and + // determine the maximum size of negative clauses (with '-lit'). + // + const auto eon = nos.end (); + auto j = nos.begin (), i = j; + for (; i != eon; i++) { + Clause *c = *j++ = *i; + if (c->garbage) + j--; + else if (c->size > max_size) + max_size = c->size; + } + if (j == nos.begin ()) + erase_vector (nos); + else + nos.resize (j - nos.begin ()); + + assert (nos.size () == (size_t) noccs (-lit)); + assert (nos.size () > 1); + + // If the maximum size of a negative clause (with '-lit') exceeds the + // maximum clause size limit ignore this candidate literal. + // + if (max_size > opts.blockmaxclslim) { + LOG ("maximum size %d of clauses with %d exceeds clause size limit %d", + max_size, -lit, opts.blockmaxclslim); + return; + } + + LOG ("maximum size %d of clauses with %d", max_size, -lit); + + // We filter candidate clauses with positive occurrence of 'lit' in + // 'blocker.candidates' and return if no candidate clause remains. + // Candidates should be small enough and should have at least one literal + // which occurs negated in one of the clauses with '-lit'. + // + size_t candidates = block_candidates (blocker, lit); + if (!candidates) { + LOG ("no candidate clauses found"); + return; + } + + LOG ("found %zd candidate clauses", candidates); + + // We further search for a clause with '-lit' that has no literal + // negated in any of the candidate clauses (except 'lit'). If such a + // clause exists, we know that none of the candidates is blocked. + // + if (candidates > 1 && block_impossible (blocker, lit)) { + LOG ("impossible to block any candidate clause on %d", lit); + assert (blocker.candidates.empty ()); + return; + } + + LOG ("trying to block %zd clauses out of %" PRId64 " with literal %d", + candidates, noccs (lit), lit); + + int64_t blocked = 0; + + // Go over all remaining candidates and try to block them on 'lit'. + // + for (const auto &c : blocker.candidates) { + assert (!c->garbage); + assert (!c->redundant); + if (!is_blocked_clause (c, lit)) + continue; + blocked++; + LOG (c, "blocked"); + if (proof) { + proof->weaken_minus (c); + } + external->push_clause_on_extension_stack (c, lit); + blocker.reschedule.push_back (c); + mark_garbage (c); + } + + LOG ("blocked %" PRId64 + " clauses on %d out of %zd candidates in %zd occurrences", + blocked, lit, blocker.candidates.size (), occs (lit).size ()); + + blocker.candidates.clear (); + stats.blocked += blocked; + if (blocked) + flush_occs (lit); +} + +/*------------------------------------------------------------------------*/ + +// Reschedule literals in a clause (except 'lit') which was blocked. + +void Internal::block_reschedule_clause (Blocker &blocker, int lit, + Clause *c) { +#ifdef NDEBUG + (void) lit; +#endif + assert (c->garbage); + + for (const auto &other : *c) { + + int64_t &n = noccs (other); + assert (n > 0); + n--; + + LOG ("updating %d with %" PRId64 " positive and %" PRId64 + " negative occurrences", + other, noccs (other), noccs (-other)); + + if (blocker.schedule.contains (vlit (-other))) + blocker.schedule.update (vlit (-other)); + else if (active (other) && !frozen (other) && !marked_skip (-other)) { + LOG ("rescheduling to block clauses on %d", -other); + blocker.schedule.push_back (vlit (-other)); + } + + if (blocker.schedule.contains (vlit (other))) { + assert (other != lit); + blocker.schedule.update (vlit (other)); + } + } +} + +// Reschedule all literals in clauses blocked by 'lit' (except 'lit'). + +void Internal::block_reschedule (Blocker &blocker, int lit) { + while (!blocker.reschedule.empty ()) { + Clause *c = blocker.reschedule.back (); + blocker.reschedule.pop_back (); + block_reschedule_clause (blocker, lit, c); + } +} + +/*------------------------------------------------------------------------*/ + +void Internal::block_literal (Blocker &blocker, int lit) { + assert (!marked_skip (lit)); + + if (!active (lit)) + return; // Pure literal '-lit'. + if (frozen (lit)) + return; + + assert (!val (lit)); + + // If the maximum number of a negative clauses (with '-lit') exceeds the + // occurrence limit ignore this candidate literal. + // + if (noccs (-lit) > opts.blockocclim) + return; + + LOG ("blocking literal candidate %d " + "with %" PRId64 " positive and %" PRId64 " negative occurrences", + lit, noccs (lit), noccs (-lit)); + + stats.blockcands++; + + assert (blocker.reschedule.empty ()); + assert (blocker.candidates.empty ()); + + if (!noccs (-lit)) + block_pure_literal (blocker, lit); + else if (!noccs (lit)) { + // Rare situation, where the clause length limit was hit for 'lit' and + // '-lit' is skipped and then it becomes pure. Can be ignored. We also + // so it once happening for a 'elimboundmin=-1' and zero positive and + // one negative occurrence. + } else if (noccs (-lit) == 1) + block_literal_with_one_negative_occ (blocker, lit); + else + block_literal_with_at_least_two_negative_occs (blocker, lit); + + // Done with blocked clause elimination on this literal and we do not + // have to try blocked clause elimination on it again until irredundant + // clauses with its negation are removed. + // + assert (!frozen (lit)); // just to be sure ... + unmark_block (lit); +} + +/*------------------------------------------------------------------------*/ + +bool Internal::block () { + + if (!opts.block) + return false; + if (unsat) + return false; + if (!stats.current.irredundant) + return false; + if (terminated_asynchronously ()) + return false; + + if (propagated < trail.size ()) { + LOG ("need to propagate %zd units first", trail.size () - propagated); + init_watches (); + connect_watches (); + if (!propagate ()) { + LOG ("propagating units results in empty clause"); + learn_empty_clause (); + assert (unsat); + } + clear_watches (); + reset_watches (); + if (unsat) + return false; + } + + START_SIMPLIFIER (block, BLOCK); + + stats.blockings++; + + LOG ("block-%" PRId64 "", stats.blockings); + + assert (!level); + assert (!watching ()); + assert (!occurring ()); + + mark_satisfied_clauses_as_garbage (); + + init_occs (); // Occurrence lists for all literals. + init_noccs (); // Number of occurrences to avoid flushing garbage clauses. + + Blocker blocker (this); + block_schedule (blocker); + + int64_t blocked = stats.blocked; + int64_t resolutions = stats.blockres; + int64_t purelits = stats.blockpurelits; + int64_t pured = stats.blockpured; + + while (!terminated_asynchronously () && !blocker.schedule.empty ()) { + int lit = u2i (blocker.schedule.front ()); + blocker.schedule.pop_front (); + block_literal (blocker, lit); + block_reschedule (blocker, lit); + } + + blocker.erase (); + reset_noccs (); + reset_occs (); + + resolutions = stats.blockres - resolutions; + blocked = stats.blocked - blocked; + + PHASE ("block", stats.blockings, + "blocked %" PRId64 " clauses in %" PRId64 " resolutions", blocked, + resolutions); + + pured = stats.blockpured - pured; + purelits = stats.blockpurelits - purelits; + + if (pured) + mark_redundant_clauses_with_eliminated_variables_as_garbage (); + + if (purelits) + PHASE ("block", stats.blockings, + "found %" PRId64 " pure literals in %" PRId64 " clauses", + purelits, pured); + else + PHASE ("block", stats.blockings, "no pure literals found"); + + report ('b', !opts.reportall && !blocked); + + STOP_SIMPLIFIER (block, BLOCK); + + return blocked; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/block.hpp b/src/sat/cadical/block.hpp new file mode 100644 index 000000000..c56707563 --- /dev/null +++ b/src/sat/cadical/block.hpp @@ -0,0 +1,37 @@ +#ifndef _block_hpp_INCLUDED +#define _block_hpp_INCLUDED + +#include "heap.hpp" // Alphabetically after 'block.hpp'. + +namespace CaDiCaL { + +struct Internal; + +struct block_more_occs_size { + Internal *internal; + block_more_occs_size (Internal *i) : internal (i) {} + bool operator() (unsigned a, unsigned b); +}; + +typedef heap BlockSchedule; + +class Blocker { + + friend struct Internal; + + vector candidates; + vector reschedule; + BlockSchedule schedule; + + Blocker (Internal *i) : schedule (block_more_occs_size (i)) {} + + void erase () { + erase_vector (candidates); + erase_vector (reschedule); + schedule.erase (); + } +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/cadical.hpp b/src/sat/cadical/cadical.hpp new file mode 100644 index 000000000..8dacaf3dc --- /dev/null +++ b/src/sat/cadical/cadical.hpp @@ -0,0 +1,1342 @@ +#ifndef _cadical_hpp_INCLUDED +#define _cadical_hpp_INCLUDED + +#include +#include +#include + +namespace CaDiCaL { + +/*========================================================================*/ + +// This provides the actual API of the CaDiCaL solver, which is implemented +// in the class 'Solver' below. Beside its constructor and destructor most +// important is the IPASIR part which you can find between 'BEGIN IPASIR' +// and 'END IPASIR' comments below. The following '[Example]' below might +// also be a good starting point to understand the API. + +/*========================================================================*/ + +// The SAT competition standardized the exit code of SAT solvers to the +// following which then is also used return code for 'solve' functions. +// In the following example we use those constants for brevity though. + +enum Status { + SATISFIABLE = 10, + UNSATISFIABLE = 20, + UNKNOWN = 0, +}; + +/*========================================================================*/ + +// [Example] +// +// The internal solver state follows the IPASIR API model used in the +// incremental track of the SAT competition. State transitions are +// triggered by member function calls, declared and described below. +// +// Consider the following code (from 'test/api/example.cpp') of API usage: +// +// CaDiCaL::Solver * solver = new CaDiCaL::Solver; +// +// // ------------------------------------------------------------------ +// // Encode Problem and check without assumptions. +// +// enum { TIE = 1, SHIRT = 2 }; +// +// solver->add (-TIE), solver->add (SHIRT), solver->add (0); +// solver->add (TIE), solver->add (SHIRT), solver->add (0); +// solver->add (-TIE), solver->add (-SHIRT), solver->add (0); +// +// int res = solver->solve (); // Solve instance. +// assert (res == 10); // Check it is 'SATISFIABLE'. +// +// res = solver->val (TIE); // Obtain assignment of 'TIE'. +// assert (res < 0); // Check 'TIE' assigned to 'false'. +// +// res = solver->val (SHIRT); // Obtain assignment of 'SHIRT'. +// assert (res > 0); // Check 'SHIRT' assigned to 'true'. +// +// // ------------------------------------------------------------------ +// // Incrementally solve again under one assumption. +// +// solver->assume (TIE); // Now force 'TIE' to true. +// +// res = solver->solve (); // Solve again incrementally. +// assert (res == 20); // Check it is 'UNSATISFIABLE'. +// +// res = solver->failed (TIE); // Check 'TIE' responsible. +// assert (res); // Yes, 'TIE' in core. +// +// res = solver->failed (SHIRT); // Check 'SHIRT' responsible. +// assert (!res); // No, 'SHIRT' not in core. +// +// // ------------------------------------------------------------------ +// // Incrementally solve once more under another assumption. +// +// solver->assume (-SHIRT); // Now force 'SHIRT' to false. +// +// res = solver->solve (); // Solve again incrementally. +// assert (res == 20); // Check it is 'UNSATISFIABLE'. +// +// res = solver->failed (TIE); // Check 'TIE' responsible. +// assert (!res); // No, 'TIE' not in core. +// +// res = solver->failed (-SHIRT); // Check '!SHIRT' responsible. +// assert (res); // Yes, '!SHIRT' in core. +// +// // ------------------------------------------------------------------ +// +// delete solver; + +/*========================================================================*/ + +// [States and Transitions] +// +// Compared to IPASIR we also use an 'ADDING' state in which the solver +// stays while adding non-zero literals until the clause is completed +// through adding a zero literal. The additional 'INITIALIZING', +// 'CONFIGURING' and 'DELETING' states are also not part of IPASIR but also +// useful for testing and debugging. +// +// We have the following transitions which are all synchronous except for +// the reentrant 'terminate' call: +// +// new +// INITIALIZING --------------------------> CONFIGURING +// +// set / trace +// CONFIGURING --------------------------> CONFIGURING +// +// add (non zero literal) +// VALID --------------------------> ADDING +// +// add (zero literal) +// VALID --------------------------> STEADY +// +// assume (non zero literal) +// READY --------------------------> STEADY +// +// solve +// READY --------------------------> SOLVING +// +// (internal) +// SOLVING --------------------------> SOLVED +// +// val (non zero literal) +// SATISFIED --------------------------> SATISFIED +// +// failed (non zero literal) +// UNSATISFIED --------------------------> UNSATISFIED +// +// implied (non zero literal) +// INCONCLUSIVE --------------------------> INCONCLUSIVE +// +// delete +// VALID --------------------------> DELETING +// +// where +// +// SOLVED = SATISFIED | UNSATISFIED | INCONCLUSIVE +// READY = CONFIGURING | STEADY | SOLVED +// VALID = READY | ADDING +// INVALID = INITIALIZING | DELETING +// +// The 'SOLVING' state is only visible in different contexts, i.e., from +// another thread or from a signal handler. It is used to implement +// 'terminate'. Here is the only asynchronous transition: +// +// terminate (asynchronously) +// SOLVING -------------------------> STEADY +// +// The important behaviour to remember is that adding, assuming or +// constraining a literal (immediately) destroys the satisfying assignment +// in the 'SATISFIED' state and vice versa resets all assumptions in the +// 'UNSATISFIED' state. This is exactly the behaviour required by the IPASIR +// interface. +// +// Furthermore, the model can only be queried through 'val' in the +// 'SATISFIED' state, while extracting failed assumptions with 'failed' only +// in the 'UNSATISFIED' state. Solving can only be started in the 'STEADY ' +// or 'CONFIGURING' state or after the previous call to 'solve' yielded an +// 'INCONCLUSIVE , 'SATISFIED' or 'UNSATISFIED' state. +// +// All literals have to be valid literals too, i.e., 32-bit integers +// different from 'INT_MIN'. If any of these requirements is violated the +// solver aborts with an 'API contract violation' message. +// +// HINT: If you do not understand why a contract is violated you can run +// 'mobical' on the failing API call trace. Point the environment variable +// 'CADICAL_API_TRACE' to the file where you want to save the trace during +// execution of your program linking against the library. You probably need +// for 'mobical' to use the option '--do-not-enforce-contracts' though to +// force running into the same contract violation. +// +// Additional API calls (like 'freeze' and 'melt') do not change the state +// of the solver and are all described below. + +/*========================================================================*/ + +// States are represented by a bit-set in order to combine them. + +enum State { + INITIALIZING = 1, // during initialization (invalid) + CONFIGURING = 2, // configure options (with 'set') + STEADY = 4, // ready to call 'solve' + ADDING = 8, // adding clause literals (zero missing) + SOLVING = 16, // while solving (within 'solve') + SATISFIED = 32, // satisfiable allows 'val' + UNSATISFIED = 64, // unsatisfiable allows 'failed' + DELETING = 128, // during and after deletion (invalid) + INCONCLUSIVE = 256, // unknown allows 'implied' + + // These combined states are used to check contracts. + + READY = CONFIGURING | STEADY | SATISFIED | UNSATISFIED | INCONCLUSIVE, + VALID = READY | ADDING, + INVALID = INITIALIZING | DELETING +}; + +/*------------------------------------------------------------------------*/ + +// Opaque classes needed in the API and declared in the same namespace. + +class File; +class Testing; +struct Internal; +struct External; + +/*------------------------------------------------------------------------*/ + +// Forward declaration of call-back classes. See bottom of this file. + +class Learner; +class FixedAssignmentListener; +class Terminator; +class ClauseIterator; +class WitnessIterator; +class ExternalPropagator; +class Tracer; +struct InternalTracer; +class FileTracer; +class StatTracer; + +/*------------------------------------------------------------------------*/ + +class Solver { + +public: + // ====== BEGIN IPASIR =================================================== + + // This section implements the corresponding IPASIR functionality. + + Solver (); + ~Solver (); + + static const char *signature (); // name of this library + + // Core functionality as in the IPASIR incremental SAT solver interface. + // (recall 'READY = CONFIGURING | STEADY | SATISFIED | UNSATISFIED'). + // Further note that 'lit' is required to be different from 'INT_MIN' and + // different from '0' except for 'add'. + + // Add valid literal to clause or zero to terminate clause. + // + // require (VALID) // recall 'VALID = READY | ADDING' + // if (lit) ensure (ADDING) // and thus VALID but not READY + // if (!lit) ensure (STEADY ) // and thus READY + // + void add (int lit); + + // Here are functions simplifying clause addition. The given literals + // should all be valid (different from 'INT_MIN' and different from '0'). + // + // require (VALID) + // ensure (STEADY ) + // + void clause (int); // Add unit clause. + void clause (int, int); // Add binary clause. + void clause (int, int, int); // Add ternary clause. + void clause (int, int, int, int); // Add quaternary clause. + void clause (int, int, int, int, int); // Add quinternary clause. + void clause (const std::vector &); // Add literal vector as clause. + void clause (const int *, size_t); // Add literal array as clause. + + // This function can be used to check if the formula is already + // inconsistent (contains the empty clause or was proven to be + // root-level unsatisfiable). + + bool inconsistent (); + + // Assume valid non zero literal for next call to 'solve'. These + // assumptions are reset after the call to 'solve' as well as after + // returning from 'simplify' and 'lookahead. + // + // require (READY) + // ensure (STEADY ) + // + void assume (int lit); + + // Try to solve the current formula. Returns + // + // 0 = UNKNOWN (limit reached or interrupted through 'terminate') + // 10 = SATISFIABLE + // 20 = UNSATISFIABLE + // + // require (READY) + // ensure (INCONCLUSIVE | SATISFIED | UNSATISFIED) + // + // Note, that while in this call the solver actually transitions to state + // 'SOLVING', which however is only visible from a different context, + // i.e., from a different thread or from a signal handler. Only right + // before returning from this call it goes into a 'READY' state. + // + int solve (); + + // Get value (-lit=false, lit=true) of valid non-zero literal. + // + // require (SATISFIED) + // ensure (SATISFIED) + // + int val (int lit); + + // Try to flip the value of the given literal without falsifying the + // formula. Returns 'true' if this was successful. Otherwise the model is + // not changed and 'false' is returned. If a literal was eliminated or + // substituted flipping will fail on that literal and in particular the + // solver will not taint it nor restore any clauses. + // + // The 'flip' function can only flip the value of a variables not acting + // as witness on the reconstruction stack. + // + // As a side effect of calling this function first all assigned variables + // are propagated again without using blocking literal. Thus the first + // call to this function after obtaining a model adds a substantial + // overhead. Subsequent calls will not need to properly propagate again. + // + // Furthermore if the reconstruction stack is non-empty and has been + // traversed to reconstruct a full extended model for eliminated + // variables (and to satisfy removed blocked clauses), the values of these + // witness variables obtained via 'val' before become invalid. The user + // thus will need to call 'val' again after calling 'flip' which will + // trigger then a traversal of the reconstruction stack. + // + // So try to avoid mixing 'flip' and 'val' (for efficiency only). + // Further, this functionality is currently not supported in the presence + // of an external propagator. + // + // require (SATISFIED) + // ensure (SATISFIED) + // + bool flip (int lit); + + // Same as 'flip' without actually flipping it. This functionality is + // currently not supported in the presence of an external propagator. + // + // require (SATISFIED) + // ensure (SATISFIED) + // + bool flippable (int lit); + + // Determine whether the valid non-zero literal is in the core. + // Returns 'true' if the literal is in the core and 'false' otherwise. + // Note that the core does not have to be minimal. + // + // require (UNSATISFIED) + // ensure (UNSATISFIED) + // + bool failed (int lit); + + // Add call-back which is checked regularly for termination. There can + // only be one terminator connected. If a second (non-zero) one is added + // the first one is implicitly disconnected. + // + // require (VALID) + // ensure (VALID) + // + void connect_terminator (Terminator *terminator); + void disconnect_terminator (); + + // Add call-back which allows to export learned clauses. + // + // require (VALID) + // ensure (VALID) + // + void connect_learner (Learner *learner); + void disconnect_learner (); + + // ====== END IPASIR ===================================================== + + // Add call-back which allows to observe when a variable is fixed. + // + // require (VALID) + // ensure (VALID) + // + void connect_fixed_listener (FixedAssignmentListener *fixed_listener); + void disconnect_fixed_listener (); + + // ====== BEGIN IPASIR-UP ================================================ + + // Add call-back which allows to learn, propagate and backtrack based on + // external constraints. Only one external propagator can be connected + // and after connection every related variables must be 'observed' (use + // 'add_observed_var' function). + // Disconnection of the external propagator resets all the observed + // variables. + // + // require (VALID) + // ensure (VALID) + // + void connect_external_propagator (ExternalPropagator *propagator); + void disconnect_external_propagator (); + + // Mark as 'observed' those variables that are relevant to the external + // propagator. External propagation, clause addition during search and + // notifications are all over these observed variables. + // A variable can not be observed without having an external propagator + // connected. Observed variables are "frozen" internally, and so + // inprocessing will not consider them as candidates for elimination. + // An observed variable is allowed to be a fresh variable and it can be + // added also during solving. + // + // require (VALID_OR_SOLVING) + // ensure (VALID_OR_SOLVING) + // + void add_observed_var (int var); + + // Removes the 'observed' flag from the given variable. A variable can be + // set unobserved only between solve calls, not during it (to guarantee + // that no yet unexplained external propagation involves it). + // + // require (VALID) + // ensure (VALID) + // + void remove_observed_var (int var); + + // Removes all the 'observed' flags from the variables. Disconnecting the + // propagator invokes this step as well. + // + // require (VALID) + // ensure (VALID) + // + void reset_observed_vars (); + + // Get reason of valid observed literal (true = it is an observed variable + // and it got assigned by a decision during the CDCL loop. Otherwise: + // false. + // + // require (VALID_OR_SOLVING) + // ensure (VALID_OR_SOLVING) + // + bool is_decision (int lit); + + // Force solve to backtrack to certain decision level. Can be called only + // during 'cb_decide' of a connected External Propagator. + // Invoking in any other time will not have an effect. + // If the call had an effect, the External Propagator will be notified + // about the backtrack via 'notify_backtrack'. + // + // require (SOLVING) + // ensure (SOLVING) + // + void force_backtrack (size_t new_level); + + // ====== END IPASIR-UP ================================================== + + //------------------------------------------------------------------------ + // Adds a literal to the constraint clause. Same functionality as 'add' + // but the clause only exists for the next call to solve (same lifetime as + // assumptions). Only one constraint may exists at a time. A new + // constraint replaces the old. The main application of this functionality + // is the model checking algorithm IC3. See our FMCAD'21 paper + // [FroleyksBiere-FMCAD'19] for more details. + // + // Add valid literal to the constraint clause or zero to terminate it. + // + // require (VALID) // recall 'VALID = READY | + // ADDING' if (lit) ensure (ADDING) // and thus VALID but not + // READY if (!lit) && !adding_clause ensure (STEADY ) // and thus READY + // + void constrain (int lit); + + // Determine whether the constraint was used to proof the + // unsatisfiability. Note that the formula might still be unsatisfiable + // without the constraint. + // + // require (UNSATISFIED) + // ensure (UNSATISFIED) + // + bool constraint_failed (); + + // Collects a subset of those literals that are implied by unit + // propagation by assuming the currently defined (potentially empty) set + // of assumptions (see IPASIR assume(lit)) function. In case unit + // propgation over the defined set of assumptions (or over the clause + // database on its own) leads to conflict, the function returns 20 and the + // content of 'implicants' is undefined. In case unit propagation happens + // to satisfy all the clauses (not probable, but not impossible), the + // function returns 10 and 'implicants' is a solution of the current + // formula under the current assumptions (after solution reconstruction). + // In any other case, the function returns 0 (indicating 'UNKNOWN') and + // 'implicants' lists the non-conflicting current value of the trail. + + // Returns + // + // 0 = UNKNOWN (unit propagation did not lead to a conflict nor to a + // complete assignment, or limit reached or interrupted + // through 'terminate') + // 10 = SATISFIABLE + // 20 = UNSATISFIABLE + + // require (READY) + // ensure (INCONCLUSIVE | SATISFIED | UNSATISFIED) + int propagate (); + + // + // require (INCONCLUSIVE) + // ensure (INCONCLUSIVE) + // + void implied (std::vector &implicants); + + //------------------------------------------------------------------------ + // This function determines a good splitting literal. The result can be + // zero if the formula is proven to be satisfiable or unsatisfiable. This + // can then be checked by 'state ()'. If the formula is empty and + // the function is not able to determine satisfiability also zero is + // returned but the state remains steady. + // + // require (READY) + // ensure (INCONCLUSIVE |SATISFIED|UNSATISFIED) + // + int lookahead (void); + + struct CubesWithStatus { + int status; + std::vector> cubes; + }; + + CubesWithStatus generate_cubes (int, int min_depth = 0); + + void reset_assumptions (); + void reset_constraint (); + + // Return the current state of the solver as defined above. + // + const State &state () const { return _state; } + + // Similar to 'state ()' but using the standard competition exit codes of + // '10' for 'SATISFIABLE', '20' for 'UNSATISFIABLE' and '0' otherwise. + // + int status () const { + if (_state == SATISFIED) + return 10; + else if (_state == UNSATISFIED) + return 20; + else + return 0; + } + + /*----------------------------------------------------------------------*/ + + static const char *version (); // return version string + + /*----------------------------------------------------------------------*/ + // Copy 'this' into a fresh 'other'. The copy procedure is not a deep + // clone, but only copies irredundant clauses and units. It also makes + // sure that witness reconstruction works with the copy as with the + // original formula such that both solvers have the same models. + // Assumptions are not copied. Options however are copied as well as + // flags which remember the current state of variables in preprocessing. + // + // require (READY) // for 'this' + // ensure (READY) // for 'this' + // + // other.require (CONFIGURING) + // other.ensure (CONFIGURING | STEADY ) + // + void copy (Solver &other) const; + + /*----------------------------------------------------------------------*/ + // Variables are usually added and initialized implicitly whenever a + // literal is used as an argument except for the functions 'val', 'fixed', + // 'failed' and 'frozen'. However, the library internally keeps a maximum + // variable index, which can be queried. + // With factor (BVA) the solver might also add new variables. In that case + // the user is required to use this to check which variables are currently + // free before adding new variables of their own. + // The alternative is to reserve variables in batches with + // 'reserve_difference'. Using 'reserve' in combination with any technique + // that could add variables (currently only factor) is not advised. + // + // require (VALID | SOLVING) + // ensure (VALID | SOLVING) + // + int vars (); + + // Increase the maximum variable index explicitly. This function makes + // sure that at least 'min_max_var' variables are initialized. Since it + // might need to reallocate tables, it destroys a satisfying assignment + // and has the same state transition and conditions as 'assume' etc. + // + // require (READY) + // ensure (STEADY ) + // + void reserve (int min_max_var); + + // Increase the maximum variable index by a number of new variables. + // initializes 'number_of_vars' new variables and protects them from + // being used by the solver as extension variables (BVA). + // It returns the new maximum variable index which is the highest + // variable name of the consecutive range of newly reserved variables. + // It has the same state transition and conditions as 'reserve' above. + // + // require (READY) + // ensure (STEADY ) + // + int reserve_difference (int number_of_vars); + +#ifndef NTRACING + //------------------------------------------------------------------------ + // This function can be used to write API calls to a file. The same + // format is used which 'mobical' can read, execute and also shrink + // through delta debugging. + // + // Tracing API calls can also be achieved by using the environment + // variable 'CADICAL_API_TRACE'. That alternative is useful if you do not + // want to change the source code using the solver, e.g., if you only have + // a binary with the solver linked in. However, that method only allows + // to trace one solver instance, while with the following function API + // tracing can be enabled for different solver instances individually. + // + // The solver will flush the file after every trace API call but does not + // close it during deletion. It remains owned by the user of the library. + // + // require (VALID) + // ensure (VALID) + // + void trace_api_calls (FILE *file); +#endif + + //------------------------------------------------------------------------ + // Option handling. + + // Determine whether 'name' is a valid option name. + // + static bool is_valid_option (const char *name); + + // Determine whether 'name' enables a specific preprocessing technique. + // + static bool is_preprocessing_option (const char *name); + + // Determine whether 'arg' is a valid long option of the form '--', + // '--=' or '--no-' similar to 'set_long_option' below. + // Legal values are 'true', 'false', or '[-][e]'. + + static bool is_valid_long_option (const char *arg); + + // Get the current value of the option 'name'. If 'name' is invalid then + // zero is returned. Here '--...' arguments as invalid options. + // + int get (const char *name); + + // Set the default verbose message prefix (default "c "). + // + void prefix (const char *verbose_message_prefix); + + // Explicit version of setting an option. If the option '' exists + // and '' can be parsed then 'true' is returned. If the option value + // is out of range the actual value is computed as the closest (minimum or + // maximum) value possible, but still 'true' is returned. + // + // require (CONFIGURING) + // ensure (CONFIGURING) + // + // Thus options can only bet set right after initialization. + // + bool set (const char *name, int val); + + // This function accepts options in command line syntax: + // + // '--=', '--', or '--no-' + // + // It actually calls the previous 'set' function after parsing 'arg'. The + // same values are expected as for 'is_valid_long_option' above and as + // with 'set' any value outside of the range of legal values for a + // particular option are set to either the minimum or maximum depending on + // which side of the valid interval they lie. + // + // require (CONFIGURING) + // ensure (CONFIGURING) + // + bool set_long_option (const char *arg); + + // Determine whether 'name' is a valid configuration. + // + static bool is_valid_configuration (const char *); + + // Overwrite (some) options with the forced values of the configuration. + // The result is 'true' iff the 'name' is a valid configuration. + // + // require (CONFIGURING) + // ensure (CONFIGURING) + // + bool configure (const char *); + + // Increase preprocessing and inprocessing limits by '10^'. Values + // below '0' are ignored and values above '9' are reduced to '9'. + // + // require (READY) + // ensure (READY) + // + void optimize (int val); + + // Specify search limits, where currently 'name' can be "conflicts", + // "decisions", "preprocessing", or "localsearch". The first two limits + // are unbounded by default. Thus using a negative limit for conflicts or + // decisions switches back to the default of unlimited search (for that + // particular limit). The preprocessing limit determines the number of + // preprocessing rounds, which is zero by default. Similarly, the local + // search limit determines the number of local search rounds (also zero by + // default). As with 'set', the return value denotes whether the limit + // 'name' is valid. These limits are only valid for the next 'solve' or + // 'simplify' call and reset to their default after 'solve' returns (as + // well as overwritten and reset during calls to 'simplify' and + // 'lookahead'). We actually also have an internal "terminate" limit + // which however should only be used for testing and debugging. + // + // require (READY) + // ensure (READY) + // + bool limit (const char *arg, int val); + bool is_valid_limit (const char *arg); + + // The number of currently active variables and clauses can be queried by + // these functions. Variables become active if a clause is added with it. + // They become inactive if they are eliminated or fixed at the root level + // Clauses become inactive if they are satisfied, subsumed, eliminated. + // Redundant clauses are reduced regularly and thus the 'redundant' + // function is less useful. + // + // require (VALID) + // ensure (VALID) + // + int active () const; // Number of active variables. + int64_t redundant () const; // Number of active redundant clauses. + int64_t irredundant () const; // Number of active irredundant clauses. + + //------------------------------------------------------------------------ + // This function executes the given number of preprocessing rounds. It is + // similar to 'solve' with 'limits ("preprocessing", rounds)' except that + // no CDCL nor local search, nor lucky phases are executed. The result + // values are also the same: 0=UNKNOWN, 10=SATISFIABLE, 20=UNSATISFIABLE. + // As 'solve' it resets current assumptions and limits before returning. + // The numbers of rounds should not be negative. If the number of rounds + // is zero only clauses are restored (if necessary) and top level unit + // propagation is performed, which both take some time. + // + // require (READY) + // ensure (INCONCLUSIVE | SATISFIED | UNSATISFIED) + // + int simplify (int rounds = 3); + + //------------------------------------------------------------------------ + // Force termination of 'solve' asynchronously. + // + // require (SOLVING | READY) + // ensure (INCONCLUSIVE ) // actually not immediately (synchronously) + // + void terminate (); + + //------------------------------------------------------------------------ + + // We have the following common reference counting functions, which avoid + // to restore clauses but require substantial user guidance. This was the + // only way to use inprocessing in incremental SAT solving in Lingeling + // (and before in MiniSAT's 'freeze' / 'thaw') and which did not use + // automatic clause restoring. In general this is slower than + // restoring clauses and should not be used. + // + // In essence the user freezes variables which potentially are still + // needed in clauses added or assumptions used after the next 'solve' + // call. As in Lingeling you can freeze a variable multiple times, but + // then have to melt it the same number of times again in order to enable + // variable eliminating on it etc. The arguments can be literals + // (negative indices) but conceptually variables are frozen. + // + // In the old way of doing things without restore you should not use a + // variable incrementally (in 'add' or 'assume'), which was used before + // and potentially could have been eliminated in a previous 'solve' call. + // This can lead to spurious satisfying assignment. In order to check + // this API contract one can use the 'checkfrozen' option. This has the + // drawback that restoring clauses implicitly would fail with a fatal + // error message even if in principle the solver could just restore + // clauses. Thus this option is disabled by default. + // + // See our SAT'19 paper [FazekasBiereScholl-SAT'19] for more details. + // + // require (VALID) + // ensure (VALID) + // + bool frozen (int lit) const; + void freeze (int lit); + void melt (int lit); // Also needs 'require (frozen (lit))'. + + //------------------------------------------------------------------------ + + // Root level assigned variables can be queried with this function. + // It returns '1' if the literal is implied by the formula, '-1' if its + // negation is implied, or '0' if this is unclear at this point. + // + // require (VALID) + // ensure (VALID) + // + int fixed (int lit) const; + + //------------------------------------------------------------------------ + // Force the default decision phase of a variable to a certain value. + // + void phase (int lit); + void unphase (int lit); + + //------------------------------------------------------------------------ + + // Enables clausal proof tracing in DRAT format and returns 'true' if + // successfully opened for writing. Writing proofs has to be enabled + // before calling 'solve', 'add' and 'dimacs', that is in state + // 'CONFIGURING'. Otherwise only partial proofs would be written. + // + // require (CONFIGURING) + // ensure (CONFIGURING) + // + bool trace_proof (FILE *file, const char *name); // Write DRAT proof. + bool trace_proof (const char *path); // Open & write proof. + + // Flushing the proof trace file eventually calls 'fflush' on the actual + // file or pipe and thus if this function returns all the proof steps + // should have been written (with the same guarantees as 'fflush'). + // + // The additional optional argument forces to print the number of addition + // and deletion steps in the proof even if the verbosity level is zero but + // not if quiet is set as well. The default for the stand-alone solver is + // to print this information (in the 'closing proof' section) but for API + // usage of the library we want to stay silent unless explicitly requested + // or verbosity is non-zero (and as explained quiet is not set). + // + // This function can be called multiple times. + // + // require (VALID) + // ensure (VALID) + // + void flush_proof_trace (bool print = false); + + // Close proof trace early. Similar to 'flush' we allow the user to + // control with 'print' in a more fine-grained way whether statistics + // about the size of the written proof file and if compressed on-the-fly + // the number of actual bytes written (including deflation percentage) are + // printed. Before actually closing (or detaching in case of writing to + // '') we check whether 'flush_proof_trace' was called since the + // last time a proof step (addition or deletion) was traced. If this is + // not the case we would call 'flush_proof_trace' with the same 'print' + // argument. + // + // require (VALID) + // ensure (VALID) + // + void close_proof_trace (bool print = false); + + // Enables clausal proof tracing with or without antecedents using + // the Tracer interface defined in 'tracer.hpp' + // + // InternalTracer, StatTracer and FileTracer for internal use + // + // require (CONFIGURING) + // ensure (CONFIGURING) + // + void connect_proof_tracer (Tracer *tracer, bool antecedents, + bool finalize_clauses = false); + void connect_proof_tracer (InternalTracer *tracer, bool antecedents, + bool finalize_clauses = false); + void connect_proof_tracer (StatTracer *tracer, bool antecedents, + bool finalize_clauses = false); + void connect_proof_tracer (FileTracer *tracer, bool antecedents, + bool finalize_clauses = false); + + // Triggers the conclusion of incremental proofs. + // if the solver is SATISFIED it will trigger extend () + // and give the model to the proof tracer through conclude_sat () + // if the solver is UNSATISFIED it will trigger failing () + // which will learn new clauses as explained below: + // In case of failed assumptions will provide a core negated + // as a clause through the proof tracer interface. + // With a failing constraint these can be multiple clauses. + // Then it will trigger a conclude_unsat event with the id(s) + // of the newly learnt clauses or the id of the global conflict. + // In case the solver is in UNKNOWN, it will collect the currently + // entrailed literals and add them to the proof. + // + // require (SATISFIED || UNSATISFIED || UNKNOWN) + // ensure (SATISFIED || UNSATISFIED || UNKNOWN) + // + void conclude (); + + // Disconnect proof tracer. If this is not done before deleting + // the tracer will be deleted. Returns true if successful. + // + // require (VALID) + // ensure (VALID) + // + bool disconnect_proof_tracer (Tracer *tracer); + bool disconnect_proof_tracer (StatTracer *tracer); + bool disconnect_proof_tracer (FileTracer *tracer); + + //------------------------------------------------------------------------ + + static void usage (); // print usage information for long options + + static void configurations (); // print configuration usage options + + // require (!DELETING) + // ensure (!DELETING) + // + void statistics (); // print statistics + void resources (); // print resource usage (time and memory) + + // require (VALID) + // ensure (VALID) + // + void options (); // print current option and value list + + //------------------------------------------------------------------------ + // Traverse irredundant clauses or the extension stack in reverse order. + // + // The return value is false if traversal is aborted early due to one of + // the visitor functions returning false. See description of the + // iterators below for more details on how to use these functions. + // + // require (VALID) + // ensure (VALID) + // + bool traverse_clauses (ClauseIterator &) const; + bool traverse_witnesses_backward (WitnessIterator &) const; + bool traverse_witnesses_forward (WitnessIterator &) const; + + //------------------------------------------------------------------------ + // Files with explicit path argument support compressed input and output + // if appropriate helper functions 'gzip' etc. are available. They are + // called through opening a pipe to an external command. + // + // If the 'strict' argument is zero then the number of variables and + // clauses specified in the DIMACS headers are ignored, i.e., the header + // 'p cnf 0 0' is always legal. If the 'strict' argument is larger '1' + // strict formatting of the header is required, i.e., single spaces + // everywhere and no trailing white space. + // + // Returns zero if successful and otherwise an error message. + // + // require (VALID) + // ensure (VALID) + // + const char *read_dimacs (FILE *file, const char *name, int &vars, + int strict = 1); + + const char *read_dimacs (const char *path, int &vars, int strict = 1); + + // The following routines work the same way but parse both DIMACS and + // INCCNF files (with 'p inccnf' header and 'a ' lines). If the + // parser finds and 'p inccnf' header or cubes then '*incremental' is set + // to true and the cubes are stored in the given vector (each cube + // terminated by a zero). + + const char *read_dimacs (FILE *file, const char *name, int &vars, + int strict, bool &incremental, + std::vector &cubes); + + const char *read_dimacs (const char *path, int &vars, int strict, + bool &incremental, std::vector &cubes); + + //------------------------------------------------------------------------ + // Write current irredundant clauses and all derived unit clauses + // to a file in DIMACS format. Clauses on the extension stack are + // not included, nor any redundant clauses. + // + // The 'min_max_var' parameter gives a lower bound on the number '' + // of variables used in the DIMACS 'p cnf ...' header. + // + // Returns zero if successful and otherwise an error message. + // + // require (VALID) + // ensure (VALID) + // + const char *write_dimacs (const char *path, int min_max_var = 0); + + // The extension stack for reconstruction a solution can be written too. + // + const char *write_extension (const char *path); + + // Print build configuration to a file with prefix 'c '. If the file + // is '' or '' then terminal color codes might be used. + // + static void build (FILE *file, const char *prefix = "c "); + +private: + //==== start of state ==================================================== + + // The solver is in the state ADDING if either the current clause or the + // constraint (or both) is not yet terminated. + bool adding_clause; + bool adding_constraint; + + State _state; // API states as discussed above. + + /*----------------------------------------------------------------------*/ + + // The 'Solver' class is a 'facade' object for 'External'. It exposes the + // public API of 'External' but hides everything else (except for the some + // private functions). It is supposed to make it easier to understand the + // API and use the solver through the API. + + // This approach has the benefit of decoupling this header file from all + // internal data structures, which is particularly useful if the rest of + // the source is not available. For instance if only a CaDiCaL library is + // installed in a system, then only this header file has to be installed + // too, and still allows to compile and link against the library. + + /*----------------------------------------------------------------------*/ + + // More precisely the CaDiCaL code is split into three layers: + // + // Solver: facade object providing the actual API of the solver + // External: communication layer between 'Solver' and 'Internal' + // Internal: the actual solver code + // + // The 'External' and 'Internal' layers are declared and implemented in + // the corresponding '{external,internal}.{hpp,cpp}' files (as expected), + // while the 'Solver' facade class is defined in 'cadical.hpp' (here) but + // implemented in 'solver.cpp'. The reason for this naming mismatch is, + // that we want to use 'cadical.hpp' for the library header (this header + // file) and call the binary of the stand alone SAT also 'cadical', which + // is more naturally implemented in 'cadical.cpp'. + // + // Separating 'External' from 'Internal' also allows us to map external + // literals to internal literals, which is useful with many fixed or + // eliminated variables (during 'compact' the internal variable range is + // reduced and external variables are remapped). Such an approach is also + // necessary, if we want to use extended resolution in the future (such as + // bounded variable addition). + // + Internal *internal; // Hidden internal solver. + External *external; // Hidden API to internal solver mapping. + + friend class Testing; // Access to 'internal' for testing only! + +#ifndef NTRACING + // The API calls to the solver can be traced by setting the environment + // variable 'CADICAL_API_TRACE' to point to the path of a file to which + // API calls are written. The same format is used which 'mobical' can + // read, execute and also shrink through delta debugging. + // + // The environment variable is read in the constructor and the trace is + // opened for writing and then closed again in the destructor. + // + // Alternatively one case use 'trace_api_calls'. Both + // + bool close_trace_api_file; // Close file if owned by solver it. + FILE *trace_api_file; // Also acts as flag that we are tracing. + + static bool tracing_api_through_environment; + + //===== end of state ==================================================== + + void trace_api_call (const char *) const; + void trace_api_call (const char *, int) const; + void trace_api_call (const char *, const char *) const; + void trace_api_call (const char *, const char *, int) const; +#endif + + void transition_to_steady_state (); + + //------------------------------------------------------------------------ + // Used in the stand alone solver application 'App' and the model based + // tester 'Mobical'. So only these two classes need direct access to the + // otherwise more application specific functions listed here together with + // the internal DIMACS parser. + + friend class App; + friend class Mobical; + friend class Parser; + + // Read solution in competition format for debugging and testing. + // + // require (VALID) + // ensure (VALID) + // + const char *read_solution (const char *path); + + // Cross-compilation with 'MinGW' needs some work-around for 'printf' + // style printing of 64-bit numbers including warning messages. The + // followings lines are copies of similar code in 'inttypes.hpp' but we + // want to keep the 'cadical.hpp' header file stand-alone. + +#ifndef PRINTF_FORMAT +#ifdef __MINGW32__ +#define __USE_MINGW_ANSI_STDIO 1 +#define PRINTF_FORMAT __MINGW_PRINTF_FORMAT +#else +#define PRINTF_FORMAT printf +#endif +#endif + + // This gives warning messages for wrong 'printf' style format string + // usage. Apparently (on 'gcc 9' at least) the first argument is 'this' + // here. + // + // TODO: support for other compilers (beside 'gcc' and 'clang'). + +#define CADICAL_ATTRIBUTE_FORMAT(FORMAT_POSITION, \ + VARIADIC_ARGUMENT_POSITION) \ + __attribute__ ((format (PRINTF_FORMAT, FORMAT_POSITION, \ + VARIADIC_ARGUMENT_POSITION))) + + // Messages in a common style. + // + // require (VALID | DELETING) + // ensure (VALID | DELETING) + // + void section (const char *); // print section header + void message (const char *, ...) // ordinary message + CADICAL_ATTRIBUTE_FORMAT (2, 3); + + void message (); // empty line - only prefix + void error (const char *, ...) // produce error message + CADICAL_ATTRIBUTE_FORMAT (2, 3); + + // Explicit verbose level ('section' and 'message' use '0'). + // + // require (VALID | DELETING) + // ensure (VALID | DELETING) + // + void verbose (int level, const char *, ...) + CADICAL_ATTRIBUTE_FORMAT (3, 4); + + // Factoring out common code to both 'read_dimacs' functions above. + // + // require (VALID) + // ensure (VALID) + // + const char *read_dimacs (File *, int &, int strict, bool *incremental = 0, + std::vector * = 0); + + // Factored out common code for 'solve', 'simplify' and 'lookahead'. + // + int call_external_solve_and_check_results (bool preprocess_only); + + //------------------------------------------------------------------------ + // Print DIMACS file to '' for debugging and testing purposes, + // including derived units and assumptions. Since it will print in terms + // of internal literals it is otherwise not really useful. To write a + // DIMACS formula in terms of external variables use 'write_dimacs'. + // + // require (!INITIALIZING) + // ensure (!INITIALIZING) + // + void dump_cnf (); + friend struct DumpCall; // Mobical calls 'dump_cnf' in 'DumpCall::execute' + + /*----------------------------------------------------------------------*/ + + // Used in mobical to test external propagation internally. + // These functions should not be called for any other purposes. + // + ExternalPropagator *get_propagator (); + bool observed (int lit); + bool is_witness (int lit); + + friend struct LemmaCall; + friend struct ObserveCall; + friend struct DisconnectCall; + friend class MockPropagator; +}; + +/*========================================================================*/ + +// Connected terminators are checked for termination regularly. If the +// 'terminate' function of the terminator returns true the solver is +// terminated synchronously as soon it calls this function. + +class Terminator { +public: + virtual ~Terminator () {} + virtual bool terminate () = 0; +}; + +// Connected learners which can be used to export learned clauses. +// The 'learning' can check the size of the learn clause and only if it +// returns true then the individual literals of the learned clause are given +// to the learn through 'learn' one by one terminated by a zero literal. + +class Learner { +public: + virtual ~Learner () {} + virtual bool learning (int size) = 0; + virtual void learn (int lit) = 0; +}; + +// Connected listener gets notified whenever the truth value of a variable +// is fixed (for example during inprocessing or due to some derived unit +// clauses). + +class FixedAssignmentListener { +public: + virtual ~FixedAssignmentListener () {} + + virtual void notify_fixed_assignment (int) = 0; +}; + +/*------------------------------------------------------------------------*/ + +// Allows to connect an external propagator to propagate values to variables +// with an external clause as a reason or to learn new clauses during the +// CDCL loop (without restart). + +class ExternalPropagator { + +public: + bool is_lazy = false; // lazy propagator only checks complete assignments + bool are_reasons_forgettable = + false; // Reason external clauses can be deleted + + virtual ~ExternalPropagator () {} + + // Notify the propagator about assignments to observed variables. + // The notification is not necessarily eager. It usually happens before + // the call of propagator callbacks and when a driving clause is leading + // to an assignment. + // + // virtual void notify_assignment (int lit, bool is_fixed) = 0; + virtual void notify_assignment (const std::vector &lits) = 0; + virtual void notify_new_decision_level () = 0; + virtual void notify_backtrack (size_t new_level) = 0; + + // Check by the external propagator the found complete solution (after + // solution reconstruction). If it returns false, the propagator should + // provide an external clause during the next callback or introduce new + // observed variables during this callback. + // + virtual bool cb_check_found_model (const std::vector &model) = 0; + + // Ask the external propagator for the next decision literal. If it + // returns 0, the solver makes its own choice. + // + virtual int cb_decide () { return 0; }; + + // Ask the external propagator if there is an external propagation to make + // under the current assignment. It returns either a literal to be + // propagated or 0, indicating that there is no external propagation under + // the current assignment. + // + virtual int cb_propagate () { return 0; }; + + // Ask the external propagator for the reason clause of a previous + // external propagation step (done by cb_propagate). The clause must be + // added literal-by-literal closed with a 0. Further, the clause must + // contain the propagated literal. + // + // The clause will be learned as an Irredundant Non-Forgettable Clause + // (see below at 'cb_has_external_clause' more details about it). + // + virtual int cb_add_reason_clause_lit (int propagated_lit) { + (void) propagated_lit; + return 0; + }; + + // The following two functions are used to add external clauses to the + // solver during the CDCL loop. The external clause is added + // literal-by-literal and learned by the solver as an irredundant + // (original) input clause. The clause can be arbitrary, but if it is + // root-satisfied or tautology, the solver will ignore it without learning + // it. Root-falsified literals are eagerly removed from the clause. + // Falsified clauses trigger conflict analysis, propagating clauses + // trigger propagation. In case chrono is 0, the solver backtracks to + // propagate the new literal on the right decision level, otherwise it + // potentially will be an out-of-order assignment on the current level. + // Unit clauses always (unless root-satisfied, see above) trigger + // backtracking (independently from the value of the chrono option and + // independently from being falsified or satisfied or unassigned) to level + // 0. Empty clause (or root falsified clause, see above) makes the problem + // unsat and stops the search immediately. A literal 0 must close the + // clause. + // + // The external propagator indicates that there is a clause to add. + // The parameter of the function allows the user to indicate that how + // 'forgettable' is the external clause. Forgettable clauses are allowed + // to be removed by the SAT solver during clause database reduction. + // However, it is up to the solver to decide when actually the clause is + // deleted. For example, unit clauses, even forgettable ones, will not be + // deleted. In case the clause is not 'forgettable' (the parameter is + // false), the solver considers the clause to be irredundant. + // + // In case the solver produces incremental proofs, these external clauses + // are added to the proof during solving at real-time, i.e., the proof + // checker can ignore them until that point (so added as input clause, but + // input after the query line). + // + // Reason clauses of external propagation steps are assumed to be + // forgettable, parameter 'reason_forgettable' can be used to change it. + // + // Currently, every external clause is expected to be over observed + // (therefore frozen) variables, hence no tainting or restore steps + // are performed upon their addition. This will be changed in later + // versions probably. + // + virtual bool cb_has_external_clause (bool &is_forgettable) = 0; + + // The actual function called to add the external clause. + // + virtual int cb_add_external_clause_lit () = 0; +}; + +/*------------------------------------------------------------------------*/ + +// Allows to traverse all remaining irredundant clauses. Satisfied and +// eliminated clauses are not included, nor any derived units unless such +// a unit literal is frozen. Falsified literals are skipped. If the solver +// is inconsistent only the empty clause is traversed. +// +// If 'clause' returns false traversal aborts early. + +class ClauseIterator { +public: + virtual ~ClauseIterator () {} + virtual bool clause (const std::vector &) = 0; +}; + +/*------------------------------------------------------------------------*/ + +// Allows to traverse all clauses on the extension stack together with their +// witness cubes. If the solver is inconsistent, i.e., an empty clause is +// found and the formula is unsatisfiable, then nothing is traversed. +// +// The clauses traversed in 'traverse_clauses' together with the clauses on +// the extension stack are logically equivalent to the original clauses. +// See our SAT'19 paper for more details. +// +// The witness literals can be used to extend and fix an assignment on the +// remaining clauses to satisfy the clauses on the extension stack too. +// +// All derived units of non-frozen variables are included too. +// +// If 'witness' returns false traversal aborts early. + +class WitnessIterator { +public: + virtual ~WitnessIterator () {} + virtual bool witness (const std::vector &clause, + const std::vector &witness, + int64_t id = 0) = 0; +}; + +/*------------------------------------------------------------------------*/ + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/ccadical.cpp b/src/sat/cadical/ccadical.cpp new file mode 100644 index 000000000..91438932b --- /dev/null +++ b/src/sat/cadical/ccadical.cpp @@ -0,0 +1,196 @@ +#include "cadical.hpp" + +#include +#include + +namespace CaDiCaL { + +struct Wrapper : Learner, Terminator { + + Solver *solver; + struct { + void *state; + int (*function) (void *); + } terminator; + + struct { + void *state; + int max_length; + int *begin_clause, *end_clause, *capacity_clause; + void (*function) (void *, int *); + } learner; + + bool terminate () { + if (!terminator.function) + return false; + return terminator.function (terminator.state); + } + + bool learning (int size) { + if (!learner.function) + return false; + return size <= learner.max_length; + } + + void learn (int lit) { + if (learner.end_clause == learner.capacity_clause) { + size_t count = learner.end_clause - learner.begin_clause; + size_t size = count ? 2 * count : 1; + learner.begin_clause = + (int *) realloc (learner.begin_clause, size * sizeof (int)); + learner.end_clause = learner.begin_clause + count; + learner.capacity_clause = learner.begin_clause + size; + } + *learner.end_clause++ = lit; + if (lit) + return; + learner.function (learner.state, learner.begin_clause); + learner.end_clause = learner.begin_clause; + } + + Wrapper () : solver (new Solver ()) { + memset (&terminator, 0, sizeof terminator); + memset (&learner, 0, sizeof learner); + } + + ~Wrapper () { + terminator.function = 0; + if (learner.begin_clause) + free (learner.begin_clause); + delete solver; + } +}; + +} // namespace CaDiCaL + +using namespace CaDiCaL; + +extern "C" { + +#include "ccadical.h" + +const char *ccadical_signature (void) { return Solver::signature (); } + +CCaDiCaL *ccadical_init (void) { return (CCaDiCaL *) new Wrapper (); } + +void ccadical_release (CCaDiCaL *wrapper) { delete (Wrapper *) wrapper; } + +void ccadical_constrain (CCaDiCaL *wrapper, int lit) { + ((Wrapper *) wrapper)->solver->constrain (lit); +} + +int ccadical_constraint_failed (CCaDiCaL *wrapper) { + return ((Wrapper *) wrapper)->solver->constraint_failed (); +} + +void ccadical_set_option (CCaDiCaL *wrapper, const char *name, int val) { + ((Wrapper *) wrapper)->solver->set (name, val); +} + +void ccadical_limit (CCaDiCaL *wrapper, const char *name, int val) { + ((Wrapper *) wrapper)->solver->limit (name, val); +} + +int ccadical_get_option (CCaDiCaL *wrapper, const char *name) { + return ((Wrapper *) wrapper)->solver->get (name); +} + +void ccadical_add (CCaDiCaL *wrapper, int lit) { + ((Wrapper *) wrapper)->solver->add (lit); +} + +void ccadical_assume (CCaDiCaL *wrapper, int lit) { + ((Wrapper *) wrapper)->solver->assume (lit); +} + +int ccadical_solve (CCaDiCaL *wrapper) { + return ((Wrapper *) wrapper)->solver->solve (); +} + +int ccadical_simplify (CCaDiCaL *wrapper) { + return ((Wrapper *) wrapper)->solver->simplify (); +} + +int ccadical_val (CCaDiCaL *wrapper, int lit) { + return ((Wrapper *) wrapper)->solver->val (lit); +} + +int ccadical_failed (CCaDiCaL *wrapper, int lit) { + return ((Wrapper *) wrapper)->solver->failed (lit); +} + +void ccadical_print_statistics (CCaDiCaL *wrapper) { + ((Wrapper *) wrapper)->solver->statistics (); +} + +void ccadical_terminate (CCaDiCaL *wrapper) { + ((Wrapper *) wrapper)->solver->terminate (); +} + +int64_t ccadical_active (CCaDiCaL *wrapper) { + return ((Wrapper *) wrapper)->solver->active (); +} + +int64_t ccadical_irredundant (CCaDiCaL *wrapper) { + return ((Wrapper *) wrapper)->solver->irredundant (); +} + +int ccadical_fixed (CCaDiCaL *wrapper, int lit) { + return ((Wrapper *) wrapper)->solver->fixed (lit); +} + +void ccadical_set_terminate (CCaDiCaL *ptr, void *state, + int (*terminate) (void *)) { + Wrapper *wrapper = (Wrapper *) ptr; + wrapper->terminator.state = state; + wrapper->terminator.function = terminate; + if (terminate) + wrapper->solver->connect_terminator (wrapper); + else + wrapper->solver->disconnect_terminator (); +} + +void ccadical_set_learn (CCaDiCaL *ptr, void *state, int max_length, + void (*learn) (void *state, int *clause)) { + Wrapper *wrapper = (Wrapper *) ptr; + wrapper->learner.state = state; + wrapper->learner.max_length = max_length; + wrapper->learner.function = learn; + if (learn) + wrapper->solver->connect_learner (wrapper); + else + wrapper->solver->disconnect_learner (); +} + +void ccadical_freeze (CCaDiCaL *ptr, int lit) { + ((Wrapper *) ptr)->solver->freeze (lit); +} + +void ccadical_melt (CCaDiCaL *ptr, int lit) { + ((Wrapper *) ptr)->solver->melt (lit); +} + +int ccadical_frozen (CCaDiCaL *ptr, int lit) { + return ((Wrapper *) ptr)->solver->frozen (lit); +} + +int ccadical_trace_proof (CCaDiCaL *ptr, FILE *file, const char *path) { + return ((Wrapper *) ptr)->solver->trace_proof (file, path); +} + +void ccadical_close_proof (CCaDiCaL *ptr) { + ((Wrapper *) ptr)->solver->close_proof_trace (); +} + +void ccadical_conclude (CCaDiCaL *ptr) { + ((Wrapper *) ptr)->solver->conclude (); +} + +int ccadical_vars (CCaDiCaL *ptr) { + return ((Wrapper *) ptr)->solver->vars (); +} + +int ccadical_reserve_difference (CCaDiCaL *ptr, int number_of_vars) { + return ((Wrapper *) ptr)->solver->reserve_difference (number_of_vars); +} +} diff --git a/src/sat/cadical/ccadical.h b/src/sat/cadical/ccadical.h new file mode 100644 index 000000000..fb99e3b42 --- /dev/null +++ b/src/sat/cadical/ccadical.h @@ -0,0 +1,71 @@ +#ifndef _ccadical_h_INCLUDED +#define _ccadical_h_INCLUDED + +/*------------------------------------------------------------------------*/ +#ifdef __cplusplus +extern "C" { +#endif +/*------------------------------------------------------------------------*/ + +#include +#include + +// C wrapper for CaDiCaL's C++ API following IPASIR. + +typedef struct CCaDiCaL CCaDiCaL; + +const char *ccadical_signature (void); +CCaDiCaL *ccadical_init (void); +void ccadical_release (CCaDiCaL *); + +void ccadical_add (CCaDiCaL *, int lit); +void ccadical_assume (CCaDiCaL *, int lit); +int ccadical_solve (CCaDiCaL *); +int ccadical_val (CCaDiCaL *, int lit); +int ccadical_failed (CCaDiCaL *, int lit); + +void ccadical_set_terminate (CCaDiCaL *, void *state, + int (*terminate) (void *state)); + +void ccadical_set_learn (CCaDiCaL *, void *state, int max_length, + void (*learn) (void *state, int *clause)); + +/*------------------------------------------------------------------------*/ + +// Non-IPASIR conformant 'C' functions. + +void ccadical_constrain (CCaDiCaL *, int lit); +int ccadical_constraint_failed (CCaDiCaL *); +void ccadical_set_option (CCaDiCaL *, const char *name, int val); +void ccadical_limit (CCaDiCaL *, const char *name, int limit); +int ccadical_get_option (CCaDiCaL *, const char *name); +void ccadical_print_statistics (CCaDiCaL *); +int64_t ccadical_active (CCaDiCaL *); +int64_t ccadical_irredundant (CCaDiCaL *); +int ccadical_fixed (CCaDiCaL *, int lit); +int ccadical_trace_proof (CCaDiCaL *, FILE *, const char *); +void ccadical_close_proof (CCaDiCaL *); +void ccadical_conclude (CCaDiCaL *); +void ccadical_terminate (CCaDiCaL *); +void ccadical_freeze (CCaDiCaL *, int lit); +int ccadical_frozen (CCaDiCaL *, int lit); +void ccadical_melt (CCaDiCaL *, int lit); +int ccadical_simplify (CCaDiCaL *); +int ccadical_vars (CCaDiCaL *); +int ccadical_reserve_difference (CCaDiCaL *, int number_of_vars); + +/*------------------------------------------------------------------------*/ + +// Support legacy names used before moving to more IPASIR conforming names. + +#define ccadical_reset ccadical_release +#define ccadical_sat ccadical_solve +#define ccadical_deref ccadical_val + +/*------------------------------------------------------------------------*/ +#ifdef __cplusplus +} +#endif +/*------------------------------------------------------------------------*/ + +#endif diff --git a/src/sat/cadical/checker.cpp b/src/sat/cadical/checker.cpp new file mode 100644 index 000000000..cfb60f0f7 --- /dev/null +++ b/src/sat/cadical/checker.cpp @@ -0,0 +1,648 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +inline unsigned Checker::l2u (int lit) { + assert (lit); + assert (lit != INT_MIN); + unsigned res = 2 * (abs (lit) - 1); + if (lit < 0) + res++; + return res; +} + +inline signed char Checker::val (int lit) { + assert (lit); + assert (lit != INT_MIN); + assert (abs (lit) < size_vars); + assert (vals[lit] == -vals[-lit]); + return vals[lit]; +} + +signed char &Checker::mark (int lit) { + const unsigned u = l2u (lit); + assert (u < marks.size ()); + return marks[u]; +} + +inline CheckerWatcher &Checker::watcher (int lit) { + const unsigned u = l2u (lit); + assert (u < watchers.size ()); + return watchers[u]; +} + +/*------------------------------------------------------------------------*/ + +CheckerClause *Checker::new_clause () { + const size_t size = simplified.size (); + assert (size > 1), assert (size <= UINT_MAX); + const size_t bytes = sizeof (CheckerClause) + (size - 2) * sizeof (int); + CheckerClause *res = (CheckerClause *) new char[bytes]; + DeferDeleteArray delete_res ((char *) res); + res->next = 0; + res->hash = last_hash; + res->size = size; + int *literals = res->literals, *p = literals; + for (const auto &lit : simplified) + *p++ = lit; + num_clauses++; + + // First two literals are used as watches and should not be false. + // + for (unsigned i = 0; i < 2; i++) { + int lit = literals[i]; + if (!val (lit)) + continue; + for (unsigned j = i + 1; j < size; j++) { + int other = literals[j]; + if (val (other)) + continue; + swap (literals[i], literals[j]); + break; + } + } + assert (!val (literals[0])); + assert (!val (literals[1])); + watcher (literals[0]).push_back (CheckerWatch (literals[1], res)); + watcher (literals[1]).push_back (CheckerWatch (literals[0], res)); + + delete_res.release (); + return res; +} + +void Checker::delete_clause (CheckerClause *c) { + if (c->size) { + assert (c->size > 1); + assert (num_clauses); + num_clauses--; + } else { + assert (num_garbage); + num_garbage--; + } + delete[] (char *) c; +} + +void Checker::enlarge_clauses () { + assert (num_clauses == size_clauses); + const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1; + LOG ("CHECKER enlarging clauses of checker from %" PRIu64 " to %" PRIu64, + (uint64_t) size_clauses, (uint64_t) new_size_clauses); + CheckerClause **new_clauses; + new_clauses = new CheckerClause *[new_size_clauses]; + clear_n (new_clauses, new_size_clauses); + for (uint64_t i = 0; i < size_clauses; i++) { + for (CheckerClause *c = clauses[i], *next; c; c = next) { + next = c->next; + const uint64_t h = reduce_hash (c->hash, new_size_clauses); + c->next = new_clauses[h]; + new_clauses[h] = c; + } + } + delete[] clauses; + clauses = new_clauses; + size_clauses = new_size_clauses; +} + +bool Checker::clause_satisfied (CheckerClause *c) { + for (unsigned i = 0; i < c->size; i++) + if (val (c->literals[i]) > 0) + return true; + return false; +} + +// The main reason why we have an explicit garbage collection phase is that +// removing clauses from watcher lists eagerly might lead to an accumulated +// quadratic algorithm. Thus we delay removing garbage clauses from watcher +// lists until garbage collection (even though we remove garbage clauses on +// the fly during propagation too). We also remove satisfied clauses. +// +void Checker::collect_garbage_clauses () { + + stats.collections++; + + for (size_t i = 0; i < size_clauses; i++) { + CheckerClause **p = clauses + i, *c; + while ((c = *p)) { + if (clause_satisfied (c)) { + c->size = 0; // mark as garbage + *p = c->next; + c->next = garbage; + garbage = c; + num_garbage++; + assert (num_clauses); + num_clauses--; + } else + p = &c->next; + } + } + + LOG ("CHECKER collecting %" PRIu64 " garbage clauses %.0f%%", num_garbage, + percent (num_garbage, num_clauses)); + + for (int lit = -size_vars + 1; lit < size_vars; lit++) { + if (!lit) + continue; + CheckerWatcher &ws = watcher (lit); + const auto end = ws.end (); + auto j = ws.begin (), i = j; + for (; i != end; i++) { + CheckerWatch &w = *i; + if (w.clause->size) + *j++ = w; + } + if (j == ws.end ()) + continue; + if (j == ws.begin ()) + erase_vector (ws); + else + ws.resize (j - ws.begin ()); + } + + for (CheckerClause *c = garbage, *next; c; c = next) + next = c->next, delete_clause (c); + + assert (!num_garbage); + garbage = 0; +} + +/*------------------------------------------------------------------------*/ + +Checker::Checker (Internal *i) + : internal (i), size_vars (0), vals (0), inconsistent (false), + num_clauses (0), num_garbage (0), size_clauses (0), clauses (0), + garbage (0), next_to_propagate (0), last_hash (0) { + + // Initialize random number table for hash function. + // + Random random (42); + for (unsigned n = 0; n < num_nonces; n++) { + uint64_t nonce = random.next (); + if (!(nonce & 1)) + nonce++; + assert (nonce), assert (nonce & 1); + nonces[n] = nonce; + } + + memset (&stats, 0, sizeof (stats)); // Initialize statistics. +} + +void Checker::connect_internal (Internal *i) { + internal = i; + LOG ("CHECKER connected to internal"); +} + +Checker::~Checker () { + LOG ("CHECKER delete"); + vals -= size_vars; + delete[] vals; + for (size_t i = 0; i < size_clauses; i++) + for (CheckerClause *c = clauses[i], *next; c; c = next) + next = c->next, delete_clause (c); + for (CheckerClause *c = garbage, *next; c; c = next) + next = c->next, delete_clause (c); + delete[] clauses; +} + +/*------------------------------------------------------------------------*/ + +// The simplicity for accessing 'vals' and 'watchers' directly through a +// signed integer literal, comes with the price of slightly more complex +// code in deleting and enlarging the checker data structures. + +void Checker::enlarge_vars (int64_t idx) { + + assert (0 < idx), assert (idx <= INT_MAX); + + int64_t new_size_vars = size_vars ? 2 * size_vars : 2; + while (idx >= new_size_vars) + new_size_vars *= 2; + LOG ("CHECKER enlarging variables of checker from %" PRId64 " to %" PRId64 + "", + size_vars, new_size_vars); + + signed char *new_vals; + new_vals = new signed char[2 * new_size_vars]; + clear_n (new_vals, 2 * new_size_vars); + new_vals += new_size_vars; + if (size_vars) // To make sanitizer happy (without '-O'). + memcpy ((void *) (new_vals - size_vars), (void *) (vals - size_vars), + 2 * size_vars); + vals -= size_vars; + delete[] vals; + vals = new_vals; + size_vars = new_size_vars; + + watchers.resize (2 * new_size_vars); + marks.resize (2 * new_size_vars); + + assert (idx < new_size_vars); +} + +inline void Checker::import_literal (int lit) { + assert (lit); + assert (lit != INT_MIN); + int idx = abs (lit); + if (idx >= size_vars) + enlarge_vars (idx); + simplified.push_back (lit); + unsimplified.push_back (lit); +} + +void Checker::import_clause (const vector &c) { + for (const auto &lit : c) + import_literal (lit); +} + +struct lit_smaller { + bool operator() (int a, int b) const { + int c = abs (a), d = abs (b); + if (c < d) + return true; + if (c > d) + return false; + return a < b; + } +}; + +bool Checker::tautological () { + sort (simplified.begin (), simplified.end (), lit_smaller ()); + const auto end = simplified.end (); + auto j = simplified.begin (); + int prev = 0; + for (auto i = j; i != end; i++) { + int lit = *i; + if (lit == prev) + continue; // duplicated literal + if (lit == -prev) + return true; // tautological clause + const signed char tmp = val (lit); + if (tmp > 0) + return true; // satisfied literal and clause + *j++ = prev = lit; + } + simplified.resize (j - simplified.begin ()); + return false; +} + +/*------------------------------------------------------------------------*/ + +uint64_t Checker::reduce_hash (uint64_t hash, uint64_t size) { + assert (size > 0); + unsigned shift = 32; + uint64_t res = hash; + while ((((uint64_t) 1) << shift) > size) { + res ^= res >> shift; + shift >>= 1; + } + res &= size - 1; + assert (res < size); + return res; +} + +uint64_t Checker::compute_hash () { + unsigned j = last_id % num_nonces; + uint64_t tmp = nonces[j] * last_id; + return last_hash = tmp; +} + +CheckerClause **Checker::find () { + stats.searches++; + CheckerClause **res, *c; + const uint64_t hash = compute_hash (); + const unsigned size = simplified.size (); + const uint64_t h = reduce_hash (hash, size_clauses); + for (const auto &lit : simplified) + mark (lit) = true; + for (res = clauses + h; (c = *res); res = &c->next) { + if (c->hash == hash && c->size == size) { + bool found = true; + const int *literals = c->literals; + for (unsigned i = 0; found && i != size; i++) + found = mark (literals[i]); + if (found) + break; + } + stats.collisions++; + } + for (const auto &lit : simplified) + mark (lit) = false; + return res; +} + +void Checker::insert () { + stats.insertions++; + if (num_clauses == size_clauses) + enlarge_clauses (); + const uint64_t h = reduce_hash (compute_hash (), size_clauses); + CheckerClause *c = new_clause (); + c->next = clauses[h]; + clauses[h] = c; +} + +/*------------------------------------------------------------------------*/ + +inline void Checker::assign (int lit) { + assert (!val (lit)); + vals[lit] = 1; + vals[-lit] = -1; + trail.push_back (lit); +} + +inline void Checker::assume (int lit) { + signed char tmp = val (lit); + if (tmp > 0) + return; + assert (!tmp); + stats.assumptions++; + assign (lit); +} + +void Checker::backtrack (unsigned previously_propagated) { + + assert (previously_propagated <= trail.size ()); + + while (trail.size () > previously_propagated) { + int lit = trail.back (); + assert (val (lit) > 0); + assert (val (-lit) < 0); + vals[lit] = vals[-lit] = 0; + trail.pop_back (); + } + + trail.resize (previously_propagated); + next_to_propagate = previously_propagated; + assert (trail.size () == next_to_propagate); +} + +/*------------------------------------------------------------------------*/ + +// This is a standard propagation routine without using blocking literals +// nor without saving the last replacement position. + +bool Checker::propagate () { + bool res = true; + while (res && next_to_propagate < trail.size ()) { + int lit = trail[next_to_propagate++]; + stats.propagations++; + assert (val (lit) > 0); + assert (abs (lit) < size_vars); + CheckerWatcher &ws = watcher (-lit); + const auto end = ws.end (); + auto j = ws.begin (), i = j; + for (; res && i != end; i++) { + CheckerWatch &w = *j++ = *i; + const int blit = w.blit; + assert (blit != -lit); + const signed char blit_val = val (blit); + if (blit_val > 0) + continue; + const unsigned size = w.size; + if (size == 2) { // not precise since + if (blit_val < 0) + res = false; // clause might be garbage + else + assign (w.blit); // but still sound + } else { + assert (size > 2); + CheckerClause *c = w.clause; + if (!c->size) { + j--; + continue; + } // skip garbage clauses + assert (size == c->size); + int *lits = c->literals; + int other = lits[0] ^ lits[1] ^ (-lit); + assert (other != -lit); + signed char other_val = val (other); + if (other_val > 0) { + j[-1].blit = other; + continue; + } + lits[0] = other, lits[1] = -lit; + unsigned k; + int replacement = 0; + signed char replacement_val = -1; + for (k = 2; k < size; k++) + if ((replacement_val = val (replacement = lits[k])) >= 0) + break; + if (replacement_val >= 0) { + watcher (replacement).push_back (CheckerWatch (-lit, c)); + swap (lits[1], lits[k]); + j--; + } else if (!other_val) + assign (other); + else + res = false; + } + } + while (i != end) + *j++ = *i++; + ws.resize (j - ws.begin ()); + } + return res; +} + +bool Checker::check () { + stats.checks++; + if (inconsistent) + return true; + unsigned previously_propagated = next_to_propagate; + for (const auto &lit : simplified) + assume (-lit); + bool res = !propagate (); + backtrack (previously_propagated); + return res; +} + +bool Checker::check_blocked () { + for (const auto &lit : unsimplified) { + mark (-lit) = true; + } + vector not_blocked; + for (size_t i = 0; i < size_clauses; i++) { + for (CheckerClause *c = clauses[i], *next; c; c = next) { + next = c->next; + unsigned count = 0; + int first; + for (int *i = c->literals; i < c->literals + c->size; i++) { + const int lit = *i; + if (val (lit) > 0) { + LOG (c->literals, c->size, "satisfied clause"); + count = 2; + break; + } + if (mark (lit)) { + count++; + LOG (c->literals, c->size, "clause"); + first = lit; + } + } + if (count == 1) + not_blocked.push_back (first); + } + } + for (const auto &lit : not_blocked) { + mark (lit) = false; + } + bool blocked = false; + for (const auto &lit : unsimplified) { + if (mark (-lit)) + blocked = true; + mark (-lit) = false; + } + return blocked; +} + +/*------------------------------------------------------------------------*/ + +void Checker::add_clause (const char *type) { +#ifndef LOGGING + (void) type; +#endif + + // If there are enough garbage clauses collect them first. + if (num_garbage > 0.5 * max ((size_t) size_clauses, (size_t) size_vars)) + collect_garbage_clauses (); + + int unit = 0; + for (const auto &lit : simplified) { + const signed char tmp = val (lit); + if (tmp < 0) + continue; + assert (!tmp); + if (unit) { + unit = INT_MIN; + break; + } + unit = lit; + } + + if (simplified.empty ()) { + LOG ("CHECKER added empty %s clause", type); + inconsistent = true; + } + if (!unit) { + LOG ("CHECKER added and checked falsified %s clause", type); + inconsistent = true; + } else if (unit != INT_MIN) { + LOG ("CHECKER added and checked %s unit clause %d", type, unit); + assign (unit); + stats.units++; + if (!propagate ()) { + LOG ("CHECKER inconsistent after propagating %s unit", type); + inconsistent = true; + } + } else + insert (); +} + +void Checker::add_original_clause (int64_t id, bool, const vector &c, + bool) { + if (inconsistent) + return; + START (checking); + LOG (c, "CHECKER addition of original clause"); + stats.added++; + stats.original++; + import_clause (c); + last_id = id; + if (tautological ()) + LOG ("CHECKER ignoring satisfied original clause"); + else + add_clause ("original"); + simplified.clear (); + unsimplified.clear (); + STOP (checking); +} + +void Checker::add_derived_clause (int64_t id, bool, const vector &c, + const vector &) { + if (inconsistent) + return; + START (checking); + LOG (c, "CHECKER addition of derived clause"); + stats.added++; + stats.derived++; + import_clause (c); + last_id = id; + if (tautological ()) + LOG ("CHECKER ignoring satisfied derived clause"); + else if (!check () && !check_blocked ()) { // needed for ER proof support + fatal_message_start (); + fputs ("failed to check derived clause:\n", stderr); + for (const auto &lit : unsimplified) + fprintf (stderr, "%d ", lit); + fputc ('0', stderr); + fatal_message_end (); + } else + add_clause ("derived"); + simplified.clear (); + unsimplified.clear (); + STOP (checking); +} + +/*------------------------------------------------------------------------*/ + +void Checker::delete_clause (int64_t id, bool, const vector &c) { + if (inconsistent) + return; + START (checking); + LOG (c, "CHECKER checking deletion of clause"); + stats.deleted++; + simplified.clear (); // Can be non-empty if clause allocation fails. + unsimplified.clear (); // Can be non-empty if clause allocation fails. + import_clause (c); + last_id = id; + if (!tautological ()) { + CheckerClause **p = find (), *d = *p; + if (d) { + assert (d->size > 1); + // Remove from hash table, mark as garbage, connect to garbage list. + num_garbage++; + assert (num_clauses); + num_clauses--; + *p = d->next; + d->next = garbage; + garbage = d; + d->size = 0; + } else { + fatal_message_start (); + fputs ("deleted clause not in proof:\n", stderr); + for (const auto &lit : unsimplified) + fprintf (stderr, "%d ", lit); + fputc ('0', stderr); + fatal_message_end (); + } + } + simplified.clear (); + unsimplified.clear (); + STOP (checking); +} + +void Checker::add_assumption_clause (int64_t id, const vector &c, + const vector &chain) { + add_derived_clause (id, true, c, chain); + delete_clause (id, true, c); +} + +/*------------------------------------------------------------------------*/ + +void Checker::dump () { + int max_var = 0; + for (uint64_t i = 0; i < size_clauses; i++) + for (CheckerClause *c = clauses[i]; c; c = c->next) + for (unsigned i = 0; i < c->size; i++) + if (abs (c->literals[i]) > max_var) + max_var = abs (c->literals[i]); + printf ("p cnf %d %" PRIu64 "\n", max_var, num_clauses); + for (uint64_t i = 0; i < size_clauses; i++) + for (CheckerClause *c = clauses[i]; c; c = c->next) { + for (unsigned i = 0; i < c->size; i++) + printf ("%d ", c->literals[i]); + printf ("0\n"); + } +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/checker.hpp b/src/sat/cadical/checker.hpp new file mode 100644 index 000000000..fba087095 --- /dev/null +++ b/src/sat/cadical/checker.hpp @@ -0,0 +1,172 @@ +#ifndef _checker_hpp_INCLUDED +#define _checker_hpp_INCLUDED + +#include "tracer.hpp" // Alphabetically after 'checker'. + +#include + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// This checker implements an online forward DRUP proof checker enabled by +// 'opts.checkproof' (requires 'opts.check' also to be enabled). This is +// useful for model basted testing (and delta-debugging), where we can not +// rely on an external proof checker such as 'drat-trim'. We also do not +// have yet a flow for offline incremental proof checking, while this +// checker here can also be used in an incremental setting. +// +// In essence the checker implements is a simple propagation online SAT +// solver with an additional hash table to find clauses fast for +// 'delete_clause'. It requires its own data structure for clauses +// ('CheckerClause') and watches ('CheckerWatch'). +// +// In our experiments the checker slows down overall SAT solving time by a +// factor of 3, which we contribute to its slightly less efficient +// implementation. + +/*------------------------------------------------------------------------*/ + +struct CheckerClause { + CheckerClause *next; // collision chain link for hash table + uint64_t hash; // previously computed full 64-bit hash + unsigned size; // zero if this is a garbage clause + int literals[2]; // otherwise 'literals' of length 'size' +}; + +struct CheckerWatch { + int blit; + unsigned size; + CheckerClause *clause; + CheckerWatch () {} + CheckerWatch (int b, CheckerClause *c) + : blit (b), size (c->size), clause (c) {} +}; + +typedef vector CheckerWatcher; + +/*------------------------------------------------------------------------*/ + +class Checker : public StatTracer { + + Internal *internal; + + // Capacity of variable values. + // + int64_t size_vars; + + // For the assignment we want to have an as fast access as possible and + // thus we use an array which can also be indexed by negative literals and + // is actually valid in the range [-size_vars+1, ..., size_vars-1]. + // + signed char *vals; + + // The 'watchers' and 'marks' data structures are not that time critical + // and thus we access them by first mapping a literal to 'unsigned'. + // + static unsigned l2u (int lit); + vector watchers; // watchers of literals + vector marks; // mark bits of literals + + signed char &mark (int lit); + CheckerWatcher &watcher (int lit); + + bool inconsistent; // found or added empty clause + + uint64_t num_clauses; // number of clauses in hash table + uint64_t num_garbage; // number of garbage clauses + uint64_t size_clauses; // size of clause hash table + CheckerClause **clauses; // hash table of clauses + CheckerClause *garbage; // linked list of garbage clauses + + vector unsimplified; // original clause for reporting + vector simplified; // clause for sorting + + vector trail; // for propagation + + unsigned next_to_propagate; // next to propagate on trail + + void enlarge_vars (int64_t idx); + void import_literal (int lit); + void import_clause (const vector &); + bool tautological (); + + static const unsigned num_nonces = 4; + + uint64_t nonces[num_nonces]; // random numbers for hashing + uint64_t last_hash; // last computed hash value of clause + int64_t last_id; + uint64_t compute_hash (); // compute and save hash value of clause + + // Reduce hash value to the actual size. + // + static uint64_t reduce_hash (uint64_t hash, uint64_t size); + + void enlarge_clauses (); // enlarge hash table for clauses + void insert (); // insert clause in hash table + CheckerClause **find (); // find clause position in hash table + + void add_clause (const char *type); + + void collect_garbage_clauses (); + + CheckerClause *new_clause (); + void delete_clause (CheckerClause *); + + signed char val (int lit); // returns '-1', '0' or '1' + + bool clause_satisfied (CheckerClause *); + + void assign (int lit); // assign a literal to true + void assume (int lit); // assume a literal + bool propagate (); // propagate and check for conflicts + void backtrack (unsigned); // prepare for next clause + bool check (); // check simplified clause is implied + bool check_blocked (); // check if clause is blocked + + struct { + + int64_t added; // number of added clauses + int64_t original; // number of added original clauses + int64_t derived; // number of added derived clauses + + int64_t deleted; // number of deleted clauses + + int64_t assumptions; // number of assumed literals + int64_t propagations; // number of propagated literals + + int64_t insertions; // number of clauses added to hash table + int64_t collisions; // number of hash collisions in 'find' + int64_t searches; // number of searched clauses in 'find' + + int64_t checks; // number of implication checks + + int64_t collections; // garbage collections + int64_t units; + + } stats; + +public: + Checker (Internal *); + virtual ~Checker (); + + void connect_internal (Internal *i) override; + + void add_original_clause (int64_t, bool, const vector &, + bool = false) override; + void add_derived_clause (int64_t, bool, const vector &, + const vector &) override; + void delete_clause (int64_t, bool, const vector &) override; + + void finalize_clause (int64_t, const vector &) override {} // skip + void report_status (int, int64_t) override {} // skip + void begin_proof (int64_t) override {} // skip + void add_assumption_clause (int64_t, const vector &, + const vector &) override; + void print_stats () override; + void dump (); // for debugging purposes only +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/clause.cpp b/src/sat/cadical/clause.cpp new file mode 100644 index 000000000..aa011bedd --- /dev/null +++ b/src/sat/cadical/clause.cpp @@ -0,0 +1,643 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Signed marking or unmarking of a clause or the global 'clause'. + +void Internal::mark (Clause *c) { + for (const auto &lit : *c) + mark (lit); +} + +void Internal::mark2 (Clause *c) { + for (const auto &lit : *c) + mark2 (lit); +} + +void Internal::unmark (Clause *c) { + for (const auto &lit : *c) + unmark (lit); +} + +void Internal::mark_clause () { + for (const auto &lit : clause) + mark (lit); +} + +void Internal::unmark_clause () { + for (const auto &lit : clause) + unmark (lit); +} + +/*------------------------------------------------------------------------*/ + +// Mark the variables of an irredundant clause to 'have been removed', which +// will trigger these variables to be considered again in the next bounded +// variable elimination phase. This is called from 'mark_garbage' below. +// Note that 'mark_removed (int lit)' will also mark the blocking flag of +// '-lit' to trigger reconsidering blocking clauses on '-lit'. + +void Internal::mark_removed (Clause *c, int except) { + LOG (c, "marking removed"); + assert (!c->redundant); + for (const auto &lit : *c) + if (lit != except) + mark_removed (lit); +} + +// Mark the variables of a (redundant or irredundant) clause to 'have been +// added', which triggers clauses with such a variables, to be considered +// both as a subsumed or subsuming clause in the next subsumption phase. +// This function is called from 'new_clause' below as well as in situations +// where a clause is shrunken (and thus needs to be at least considered +// again to subsume a larger clause). We also use this to tell +// 'ternary' preprocessing reconsider clauses on an added literal as well as +// trying to block clauses on it. + +inline void Internal::mark_added (int lit, int size, bool redundant) { + mark_subsume (lit); + if (size == 3) + mark_ternary (lit); + if (!redundant) + mark_block (lit); + if (!redundant || size == 2) + mark_factor (lit); +} + +void Internal::mark_added (Clause *c) { + LOG (c, "marking added"); + assert (likely_to_be_kept_clause (c)); + for (const auto &lit : *c) + mark_added (lit, c->size, c->redundant); +} + +/*------------------------------------------------------------------------*/ + +Clause *Internal::new_clause (bool red, int glue) { + + assert (clause.size () <= (size_t) INT_MAX); + const int size = (int) clause.size (); + assert (size >= 2); + + if (glue > size) + glue = size; + + size_t bytes = Clause::bytes (size); + Clause *c = (Clause *) new char[bytes]; + DeferDeleteArray clause_delete ((char *) c); + + c->id = ++clause_id; + + c->conditioned = false; + c->covered = false; + c->enqueued = false; + c->frozen = false; + c->garbage = false; + c->gate = false; + c->hyper = false; + c->instantiated = false; + c->moved = false; + c->reason = false; + c->redundant = red; + c->transred = false; + c->subsume = false; + c->swept = false; + c->flushed = false; + c->vivified = false; + c->vivify = false; + c->used = 0; + + c->glue = glue; + c->size = size; + c->pos = 2; + + for (int i = 0; i < size; i++) + c->literals[i] = clause[i]; + + // Just checking that we did not mess up our sophisticated memory layout. + // This might be compiler dependent though. Crucial for correctness. + // + assert (c->bytes () == bytes); + + stats.current.total++; + stats.added.total++; + + if (red) { + stats.current.redundant++; + stats.added.redundant++; + } else { + stats.irrlits += size; + stats.current.irredundant++; + stats.added.irredundant++; + } + + clauses.push_back (c); + clause_delete.release (); + LOG (c, "new pointer %p", (void *) c); + + if (likely_to_be_kept_clause (c)) + mark_added (c); + + return c; +} + +/*------------------------------------------------------------------------*/ + +void Internal::promote_clause (Clause *c, int new_glue) { + assert (c->redundant); + const int tier1limit = tier1[false]; + const int tier2limit = max (tier1limit, tier2[false]); + if (!c->redundant) + return; + if (c->hyper) + return; + int old_glue = c->glue; + if (new_glue >= old_glue) + return; + if (old_glue > tier1limit && new_glue <= tier1limit) { + LOG (c, "promoting with new glue %d to tier1", new_glue); + stats.promoted1++; + c->used = max_used; + } else if (old_glue > tier2limit && new_glue <= tier2limit) { + LOG (c, "promoting with new glue %d to tier2", new_glue); + stats.promoted2++; + } else if (old_glue <= tier2limit) + LOG (c, "keeping with new glue %d in tier2", new_glue); + else + LOG (c, "keeping with new glue %d in tier3", new_glue); + stats.improvedglue++; + c->glue = new_glue; +} +/*------------------------------------------------------------------------*/ + +void Internal::promote_clause_glue_only (Clause *c, int new_glue) { + assert (c->redundant); + if (c->hyper) + return; + int old_glue = c->glue; + const int tier1limit = tier1[false]; + const int tier2limit = max (tier1limit, tier2[false]); + if (new_glue >= old_glue) + return; + if (new_glue <= tier1limit) { + LOG (c, "promoting with new glue %d to tier1", new_glue); + stats.promoted1++; + c->used = max_used; + } else if (old_glue > tier2limit && new_glue <= tier2limit) { + LOG (c, "promoting with new glue %d to tier2", new_glue); + stats.promoted2++; + } else if (old_glue <= tier2limit) + LOG (c, "keeping with new glue %d in tier2", new_glue); + else + LOG (c, "keeping with new glue %d in tier3", new_glue); + stats.improvedglue++; + c->glue = new_glue; +} + +/*------------------------------------------------------------------------*/ + +// Shrinking a clause, e.g., removing one or more literals, requires to fix +// the 'pos' field, if it exists and points after the new last literal. We +// also have adjust the global statistics counter of irredundant literals +// for irredundant clauses, and also adjust the glue value of redundant +// clauses if the size becomes smaller than the glue. Also mark the +// literals in the resulting clause as 'added'. The result is the number of +// (aligned) removed bytes, resulting from shrinking the clause. +// +size_t Internal::shrink_clause (Clause *c, int new_size) { + if (opts.check && is_external_forgettable (c->id)) + mark_garbage_external_forgettable (c->id); + assert (new_size >= 2); + int old_size = c->size; + assert (new_size < old_size); +#ifndef NDEBUG + for (int i = c->size; i < new_size; i++) + c->literals[i] = 0; +#endif + + if (c->pos >= new_size) + c->pos = 2; + + size_t old_bytes = c->bytes (); + c->size = new_size; + size_t new_bytes = c->bytes (); + size_t res = old_bytes - new_bytes; + + if (c->redundant) + promote_clause_glue_only (c, min (c->size - 1, c->glue)); + else { + int delta_size = old_size - new_size; + assert (stats.irrlits >= delta_size); + stats.irrlits -= delta_size; + } + + if (likely_to_be_kept_clause (c)) + mark_added (c); + + return res; +} + +// This is the 'raw' deallocation of a clause. If the clause is in the +// arena nothing happens. If the clause is not in the arena its memory is +// reclaimed immediately. + +void Internal::deallocate_clause (Clause *c) { + char *p = (char *) c; + if (arena.contains (p)) + return; + LOG (c, "deallocate pointer %p", (void *) c); + delete[] p; +} + +void Internal::delete_clause (Clause *c) { + LOG (c, "delete pointer %p", (void *) c); + size_t bytes = c->bytes (); + stats.collected += bytes; + if (c->garbage) { + assert (stats.garbage.bytes >= (int64_t) bytes); + stats.garbage.bytes -= bytes; + assert (stats.garbage.clauses > 0); + stats.garbage.clauses--; + assert (stats.garbage.literals >= c->size); + stats.garbage.literals -= c->size; + + // See the discussion in 'propagate' on avoiding to eagerly trace binary + // clauses as deleted (produce 'd ...' lines) as soon they are marked + // garbage. We avoid this and only trace them as deleted when they are + // actually deleted here. This allows the solver to propagate binary + // garbage clauses without producing incorrect 'd' lines. The effect + // from the proof perspective is that the deletion of these binary + // clauses occurs later in the proof file. + // + if (proof && c->size == 2 && !c->flushed) { + proof->delete_clause (c); + } + } + deallocate_clause (c); +} + +// We want to eagerly update statistics as soon clauses are marked garbage. +// Otherwise 'report' for instance gives wrong numbers after 'subsume' +// before the next 'reduce'. Thus we factored out marking and accounting +// for garbage clauses. +// +// Eagerly deleting clauses instead is problematic, since references to +// these clauses need to be flushed, which is too costly to do eagerly. +// +// We also update garbage statistics at this point. This helps to +// determine whether the garbage collector should be called during for +// instance bounded variable elimination, which usually generates lots of +// garbage clauses. +// +// In order not to miss any update to these clause statistics we call +// 'check_clause_stats' after garbage collection in debugging mode. +// +void Internal::mark_garbage (Clause *c) { + + assert (!c->garbage); + + // Delay tracing deletion of binary clauses. See the discussion above in + // 'delete_clause' and also in 'propagate'. + // + if (proof && (c->size != 2 || !watching ())) { + c->flushed = true; + proof->delete_clause (c); + } + + // Because of the internal model checking, external forgettable clauses + // must be marked as removed already upon mark_garbage, can not wait until + // actual deletion. + if (opts.check && is_external_forgettable (c->id)) + mark_garbage_external_forgettable (c->id); + + assert (stats.current.total > 0); + stats.current.total--; + + size_t bytes = c->bytes (); + if (c->redundant) { + assert (stats.current.redundant > 0); + stats.current.redundant--; + } else { + assert (stats.current.irredundant > 0); + stats.current.irredundant--; + assert (stats.irrlits >= c->size); + stats.irrlits -= c->size; + mark_removed (c); + } + stats.garbage.bytes += bytes; + stats.garbage.clauses++; + stats.garbage.literals += c->size; + c->garbage = true; + c->used = 0; + + LOG (c, "marked garbage pointer %p", (void *) c); +} + +/*------------------------------------------------------------------------*/ + +// Almost the same function as 'search_assign' except that we do not pretend +// to learn a new unit clause (which was confusing in log files). + +void Internal::assign_original_unit (int64_t id, int lit) { + assert (!level || opts.chrono); + assert (!unsat); + const int idx = vidx (lit); + assert (!vals[idx]); + assert (!flags (idx).eliminated ()); + Var &v = var (idx); + v.level = 0; + v.trail = (int) trail.size (); + v.reason = 0; + const signed char tmp = sign (lit); + set_val (idx, tmp); + trail.push_back (lit); + num_assigned++; + const unsigned uidx = vlit (lit); + if (lrat || frat) + unit_clauses (uidx) = id; + LOG ("original unit assign %d", lit); + assert (num_assigned == trail.size () || level); + mark_fixed (lit); + if (level) + return; + if (propagate ()) + return; + assert (conflict); + LOG ("propagation of original unit results in conflict"); + learn_empty_clause (); +} + +// New clause added through the API, e.g., while parsing a DIMACS file. +// Also used by external_propagate in various different modes. +// clause, original, lrat_chain and external->eclause are set. +// from_propagator and force_no_backtrack change the behaviour. +// sometimes the pointer to the new clause is needed, therefore it is +// made sure that newest_clause points to the new clause upon return. +// +// TODO: Find another name for 'tainted' in the context of ilb, tainted +// is reconstruction related already and they should not mix. +void Internal::add_new_original_clause (int64_t id) { + + if (!from_propagator && level && !opts.ilb) { + backtrack (); + } else if (tainted_literal) { + assert (val (tainted_literal)); + int new_level = var (tainted_literal).level - 1; + assert (new_level >= 0); + backtrack (new_level); + } + assert (!tainted_literal); + LOG (original, "original clause"); + assert (clause.empty ()); + bool skip = false; + unordered_set learned_levels; + size_t unassigned = 0; + newest_clause = 0; + if (unsat) { + LOG ("skipping clause since formula is already inconsistent"); + skip = true; + } else { + assert (clause.empty ()); + for (const auto &lit : original) { + int tmp = marked (lit); + if (tmp > 0) { + LOG ("removing duplicated literal %d", lit); + } else if (tmp < 0) { + LOG ("tautological since both %d and %d occur", -lit, lit); + skip = true; + } else { + mark (lit); + tmp = fixed (lit); + if (tmp < 0) { + LOG ("removing falsified literal %d", lit); + if (lrat) { + int elit = externalize (lit); + unsigned eidx = (elit > 0) + 2u * (unsigned) abs (elit); + if (!external->ext_units[eidx]) { + int64_t uid = unit_id (-lit); + lrat_chain.push_back (uid); + } + } + } else if (tmp > 0) { + LOG ("satisfied since literal %d true", lit); + skip = true; + } else { + clause.push_back (lit); + assert (flags (lit).status != Flags::UNUSED); + tmp = val (lit); + if (tmp) + learned_levels.insert (var (lit).level); + else + unassigned++; + } + } + } + for (const auto &lit : original) + unmark (lit); + } + if (skip) { + if (from_propagator) { + stats.ext_prop.elearn_conf++; + + // In case it was a skipped external forgettable, we need to mark it + // immediately as removed + + if (opts.check && is_external_forgettable (id)) + mark_garbage_external_forgettable (id); + } + if (proof) { + proof->delete_external_original_clause (id, false, external->eclause); + } + } else { + int64_t new_id = id; + const size_t size = clause.size (); + if (original.size () > size) { + new_id = ++clause_id; + if (proof) { + if (lrat) + lrat_chain.push_back (id); + proof->add_derived_clause (new_id, false, clause, lrat_chain); + proof->delete_external_original_clause (id, false, + external->eclause); + } + external->check_learned_clause (); + + if (from_propagator) { + // The original form of the added clause is immediately forgotten + // TODO: shall we save and check the simplified form? (one with + // new_id) + if (opts.check && is_external_forgettable (id)) + mark_garbage_external_forgettable (id); + } + } + external->eclause.clear (); + lrat_chain.clear (); + if (!size) { + if (from_propagator) + stats.ext_prop.elearn_conf++; + assert (!unsat); + if (!original.size ()) + VERBOSE (1, "found empty original clause"); + else + VERBOSE (1, "found falsified original clause"); + unsat = true; + conflict_id = new_id; + marked_failed = true; + conclusion.push_back (new_id); + } else if (size == 1) { + if (force_no_backtrack) { + assert (level); + const int idx = vidx (clause[0]); + assert (val (clause[0]) >= 0); + assert (!flags (idx).eliminated ()); + Var &v = var (idx); + assert (val (clause[0])); + v.level = 0; + v.reason = 0; + const unsigned uidx = vlit (clause[0]); + if (lrat || frat) + unit_clauses (uidx) = new_id; + mark_fixed (clause[0]); + } else { + const int lit = clause[0]; + assert (!val (lit) || var (lit).level); + if (val (lit) < 0) + backtrack (var (lit).level - 1); + assert (val (lit) >= 0); + handle_external_clause (0); + assign_original_unit (new_id, lit); + } + } else { + move_literals_to_watch (); +#ifndef NDEBUG + check_watched_literal_invariants (); +#endif + int glue = (int) (learned_levels.size () + unassigned); + assert (glue <= (int) clause.size ()); + bool clause_redundancy = from_propagator && ext_clause_forgettable; + Clause *c = new_clause (clause_redundancy, glue); + c->id = new_id; + clause_id--; + watch_clause (c); + clause.clear (); + original.clear (); + handle_external_clause (c); + newest_clause = c; + } + } + clause.clear (); + lrat_chain.clear (); +} + +// Add learned new clause during conflict analysis and watch it. Requires +// that the clause is at least of size 2, and the first two literals +// are assigned at the highest decision level. +// +Clause *Internal::new_learned_redundant_clause (int glue) { + assert (clause.size () > 1); +#ifndef NDEBUG + for (size_t i = 2; i < clause.size (); i++) + assert (var (clause[0]).level >= var (clause[i]).level), + assert (var (clause[1]).level >= var (clause[i]).level); +#endif + external->check_learned_clause (); + Clause *res = new_clause (true, glue); + if (proof) { + proof->add_derived_clause (res, lrat_chain); + } + assert (watching ()); + watch_clause (res); + return res; +} + +// Add hyper binary resolved clause during 'probing'. +// +Clause *Internal::new_hyper_binary_resolved_clause (bool red, int glue) { + external->check_learned_clause (); + Clause *res = new_clause (red, glue); + if (proof) { + proof->add_derived_clause (res, lrat_chain); + } + assert (watching ()); + watch_clause (res); + return res; +} + +// Add hyper ternary resolved clause during 'ternary'. +// +Clause *Internal::new_hyper_ternary_resolved_clause (bool red) { + external->check_learned_clause (); + size_t size = clause.size (); + Clause *res = new_clause (red, size); + if (proof) { + proof->add_derived_clause (res, lrat_chain); + } + assert (!watching ()); + return res; +} + +Clause *Internal::new_factor_clause () { + external->check_learned_clause (); + stats.factor_added++; + stats.literals_factored += clause.size (); + Clause *res = new_clause (false, 0); + if (proof) { + proof->add_derived_clause (res, lrat_chain); + } + assert (!watching ()); + assert (occurring ()); + for (const auto &lit : *res) { + occs (lit).push_back (res); + } + return res; +} + +// Add hyper ternary resolved clause during 'congruence' and watch it +// +Clause * +Internal::new_hyper_ternary_resolved_clause_and_watch (bool red, + bool full_watching) { + external->check_learned_clause (); + size_t size = clause.size (); + Clause *res = new_clause (red, size); + if (proof) { + proof->add_derived_clause (res, lrat_chain); + } + if (full_watching) { + assert (watching ()); + watch_clause (res); + } + return res; +} + +// Add a new clause with same glue and redundancy as 'orig' but literals are +// assumed to be in 'clause' in 'decompose' and 'vivify'. +// +Clause *Internal::new_clause_as (const Clause *orig) { + external->check_learned_clause (); + const int new_glue = orig->glue; + Clause *res = new_clause (orig->redundant, new_glue); + if (proof) { + proof->add_derived_clause (res, lrat_chain); + } + assert (watching ()); + watch_clause (res); + return res; +} + +// Add resolved clause during resolution, e.g., bounded variable +// elimination, but do not connect its occurrences here. +// +Clause *Internal::new_resolved_irredundant_clause () { + external->check_learned_clause (); + if (proof) { + proof->add_derived_clause (clause_id + 1, false, clause, lrat_chain); + } + Clause *res = new_clause (false); + assert (!watching ()); + return res; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/clause.hpp b/src/sat/cadical/clause.hpp new file mode 100644 index 000000000..eed019cde --- /dev/null +++ b/src/sat/cadical/clause.hpp @@ -0,0 +1,188 @@ +#ifndef _clause_hpp_INCLUDED +#define _clause_hpp_INCLUDED + +#include "util.hpp" +#include +#include +#include + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +typedef int *literal_iterator; +typedef const int *const_literal_iterator; + +/*------------------------------------------------------------------------*/ + +// The 'Clause' data structure is very important. There are usually many +// clauses and accessing them is a hot-spot. Thus we use common +// optimizations to reduce memory and improve cache usage, even though this +// induces some complexity in understanding the code. +// +// The most important optimization is to 'embed' the actual literals in the +// clause. This requires a variadic size structure and thus strictly is not +// 'C' conform, but supported by all compilers we used. The alternative is +// to store the actual literals somewhere else, which not only needs more +// memory but more importantly also requires another memory access and thus +// is very costly. + +struct Clause { + union { + int64_t id; // Used to create LRAT-style proofs + Clause *copy; // Only valid if 'moved', then that's where to. + // + // The 'copy' field is only valid for 'moved' clauses in the moving + // garbage collector 'copy_non_garbage_clauses' for keeping clauses + // compactly in a contiguous memory arena. Otherwise, so almost all of + // the time, 'id' is valid. See 'collect.cpp' for details. + }; + bool conditioned : 1; // Tried for globally blocked clause elimination. + bool covered : 1; // Already considered for covered clause elimination. + bool enqueued : 1; // Enqueued on backward queue. + bool frozen : 1; // Temporarily frozen (in covered clause elimination). + bool garbage : 1; // can be garbage collected unless it is a 'reason' + bool gate : 1; // Clause part of a gate (function definition). + bool hyper : 1; // redundant hyper binary or ternary resolved + bool instantiated : 1; // tried to instantiate + bool moved : 1; // moved during garbage collector ('copy' valid) + bool reason : 1; // reason / antecedent clause can not be collected + bool redundant : 1; // aka 'learned' so not 'irredundant' (original) + bool transred : 1; // already checked for transitive reduction + bool subsume : 1; // not checked in last subsumption round + bool swept : 1; // clause used to sweep equivalences + bool flushed : 1; // garbage in proof deleted binaries + unsigned used : 8; // resolved in conflict analysis since last 'reduce' + bool vivified : 1; // clause already vivified + bool vivify : 1; // clause scheduled to be vivified + + // The glucose level ('LBD' or short 'glue') is a heuristic value for the + // expected usefulness of a learned clause, where smaller glue is consider + // more useful. During learning the 'glue' is determined as the number of + // decisions in the learned clause. Thus the glue of a clause is a strict + // upper limit on the smallest number of decisions needed to make it + // propagate. For instance a binary clause will propagate if one of its + // literals is set to false. Similarly a learned clause with glue 1 can + // propagate after one decision, one with glue 2 after 2 decisions etc. + // In some sense the glue is an abstraction of the size of the clause. + // + // See the IJCAI'09 paper by Audemard & Simon for more details. We + // switched back and forth between keeping the glue stored in a clause and + // using it only initially to determine whether it is kept, that is + // survives clause reduction. The latter strategy is not bad but also + // does not allow to use glue values for instance in 'reduce'. + // + // More recently we also update the glue and promote clauses to lower + // level tiers during conflict analysis. The idea of using three tiers is + // also due to Chanseok Oh and thus used in all recent 'Maple...' solvers. + // Tier one are the always kept clauses with low glue at most + // 'opts.reducetier1glue' (default '2'). The second tier contains all + // clauses with glue larger than 'opts.reducetier1glue' but smaller or + // equal than 'opts.reducetier2glue' (default '6'). The third tier + // consists of clauses with glue larger than 'opts.reducetier2glue'. + // + // Clauses in tier one are not deleted in 'reduce'. Clauses in tier + // two require to be unused in two consecutive 'reduce' intervals before + // being collected while for clauses in tier three not being used since + // the last 'reduce' call makes them deletion candidates. Clauses derived + // by hyper binary or ternary resolution (even though small and thus with + // low glue) are always removed if they remain unused during one interval. + // See 'mark_useless_redundant_clauses_as_garbage' in 'reduce.cpp' and + // 'bump_clause' in 'analyze.cpp'. + // + int glue; + + int size; // Actual size of 'literals' (at least 2). + int pos; // Position of last watch replacement [Gent'13]. + + // This 'flexible array member' is of variadic 'size' (and actually + // shrunken if strengthened) and keeps the literals close to the header of + // the clause to avoid another pointer dereference, which would be costly. + + // In earlier versions we used 'literals[2]' to fake it (in order to + // support older Microsoft compilers even though this feature is in C99) + // and at the same time being able to overlay the first two literals with + // the 'copy' field above, as having a flexible array member inside a + // union is not allowed. Now compilers start to figure out that those + // literals can be accessed with indices larger than 1 and produce + // warnings. After having the 'id' field mandatory we now overlay that + // one with the copy field. + + // However, it turns out that even though flexible array members are in + // C99 they are not in C11++, and therefore pedantic compilation with + // '--pedantic' fails completely. Therefore we still support as + // alternative faked flexible array members, which unfortunately need + // then again more care when accessing the literals outside the faked + // virtual sizes and the compiler can somehow figure that out, because + // that would in turn produce a warning. + +#ifndef NFLEXIBLE + int literals[]; +#else + int literals[2]; +#endif + + // Supports simple range based for loops over clauses. + + literal_iterator begin () { return literals; } + literal_iterator end () { return literals + size; } + + const_literal_iterator begin () const { return literals; } + const_literal_iterator end () const { return literals + size; } + + static size_t bytes (int size) { + + // Memory sanitizer insists that clauses put into consecutive memory in + // the arena are still 8 byte aligned. We could also allocate 8 byte + // aligned memory there. However, assuming the real memory foot print + // of a clause is 8 bytes anyhow, we just allocate 8 byte aligned memory + // all the time (even if allocated outside of the arena). + // + assert (size > 1); + const size_t header_bytes = sizeof (Clause); + const size_t actual_literal_bytes = size * sizeof (int); + size_t combined_bytes = header_bytes + actual_literal_bytes; +#ifdef NFLEXIBLE + const size_t faked_literals_bytes = sizeof ((Clause *) 0)->literals; + combined_bytes -= faked_literals_bytes; +#endif + size_t aligned_bytes = align (combined_bytes, 8); + return aligned_bytes; + } + + size_t bytes () const { return bytes (size); } + + // Check whether this clause is ready to be collected and deleted. The + // 'reason' flag is only there to protect reason clauses in 'reduce', + // which does not backtrack to the root level. If garbage collection is + // triggered from a preprocessor, which backtracks to the root level, then + // 'reason' is false for sure. We want to use the same garbage collection + // code though for both situations and thus hide here this variance. + // + bool collect () const { return !reason && garbage; } +}; + +struct clause_smaller_size { + bool operator() (const Clause *a, const Clause *b) { + return a->size < b->size; + } +}; + +/*------------------------------------------------------------------------*/ + +// Place literals over the same variable close to each other. This would +// allow eager removal of identical literals and detection of tautological +// clauses but is only currently used for better logging (see also +// 'opts.logsort' in 'logging.cpp'). + +struct clause_lit_less_than { + bool operator() (int a, int b) const { + using namespace std; + int s = abs (a), t = abs (b); + return s < t || (s == t && a < b); + } +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/collect.cpp b/src/sat/cadical/collect.cpp new file mode 100644 index 000000000..2e583c9c2 --- /dev/null +++ b/src/sat/cadical/collect.cpp @@ -0,0 +1,545 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Returns the positive number '1' ( > 0) if the given clause is root level +// satisfied or the negative number '-1' ( < 0) if it is not root level +// satisfied but contains a root level falsified literal. Otherwise, if it +// contains neither a satisfied nor falsified literal, then '0' is returned. + +int Internal::clause_contains_fixed_literal (Clause *c) { + int satisfied = 0, falsified = 0; + for (const auto &lit : *c) { + const int tmp = fixed (lit); + if (tmp > 0) { + LOG (c, "root level satisfied literal %d in", lit); + satisfied++; + } + if (tmp < 0) { + LOG (c, "root level falsified literal %d in", lit); + falsified++; + } + } + if (satisfied) + return 1; + else if (falsified) + return -1; + else + return 0; +} + +// Assume that the clause is not root level satisfied but contains a literal +// set to false (root level falsified literal), so it can be shrunken. The +// clause data is not actually reallocated at this point to avoid dealing +// with issues of special policies for watching binary clauses or whether a +// clause is extended or not. Only its size field is adjusted accordingly +// after flushing out root level falsified literals. + +void Internal::remove_falsified_literals (Clause *c) { + const const_literal_iterator end = c->end (); + const_literal_iterator i; + int num_non_false = 0; + for (i = c->begin (); num_non_false < 2 && i != end; i++) + if (fixed (*i) >= 0) + num_non_false++; + if (num_non_false < 2) + return; + if (proof) { + // Flush changes the clause id, external forgettables need to be + // marked here (or the new id could be used instead of old one) + if (opts.check && is_external_forgettable (c->id)) + mark_garbage_external_forgettable (c->id); + proof->flush_clause (c); + } + literal_iterator j = c->begin (); + for (i = j; i != end; i++) { + const int lit = *j++ = *i, tmp = fixed (lit); + assert (tmp <= 0); + if (tmp >= 0) + continue; + LOG ("flushing %d", lit); + j--; + } + stats.collected += shrink_clause (c, j - c->begin ()); +} + +// If there are new units (fixed variables) since the last garbage +// collection we go over all clauses, mark satisfied ones as garbage and +// flush falsified literals. Otherwise if no new units have been generated +// since the last garbage collection just skip this step. + +void Internal::mark_satisfied_clauses_as_garbage () { + + if (last.collect.fixed >= stats.all.fixed) + return; + last.collect.fixed = stats.all.fixed; + + LOG ("marking satisfied clauses and removing falsified literals"); + + for (const auto &c : clauses) { + if (c->garbage) + continue; + const int tmp = clause_contains_fixed_literal (c); + if (tmp > 0) + mark_garbage (c); + else if (tmp < 0) + remove_falsified_literals (c); + } +} + +/*------------------------------------------------------------------------*/ + +// Reason clauses can not be collected. +// +// We protect reasons before and release protection after garbage collection +// (actually within garbage collection). +// +// For 'reduce' we still need to make sure that all clauses which should not +// be removed are marked as such and thus we need to call it before marking +// clauses to be flushed. + +void Internal::protect_reasons () { + LOG ("protecting reason clauses of all assigned variables on trail"); + assert (!protected_reasons); +#ifdef LOGGING + size_t count = 0; +#endif + for (const auto &lit : trail) { + if (!active (lit)) + continue; + assert (val (lit)); + Var &v = var (lit); + assert (v.level > 0); + Clause *reason = v.reason; + if (!reason) + continue; + if (reason == external_reason) + continue; + LOG (reason, "protecting assigned %d reason %p", lit, (void *) reason); + assert (!reason->reason); + reason->reason = true; +#ifdef LOGGING + count++; +#endif + } + LOG ("protected %zd reason clauses referenced on trail", count); + protected_reasons = true; +} + +/*------------------------------------------------------------------------*/ + +// After garbage collection we reset the 'reason' flag of the reasons +// of assigned literals on the trail. + +void Internal::unprotect_reasons () { + LOG ("unprotecting reasons clauses of all assigned variables on trail"); + assert (protected_reasons); +#ifdef LOGGING + size_t count = 0; +#endif + for (const auto &lit : trail) { + if (!active (lit)) + continue; + assert (val (lit)); + Var &v = var (lit); + assert (v.level > 0); + Clause *reason = v.reason; + if (!reason) + continue; + if (reason == external_reason) + continue; + LOG (reason, "unprotecting assigned %d reason %p", lit, + (void *) reason); + assert (reason->reason); + reason->reason = false; +#ifdef LOGGING + count++; +#endif + } + LOG ("unprotected %zd reason clauses referenced on trail", count); + protected_reasons = false; +} + +/*------------------------------------------------------------------------*/ + +// Update occurrence lists before deleting garbage clauses in the context of +// preprocessing, e.g., during bounded variable elimination 'elim'. The +// result is the number of remaining clauses, which in this context means +// the number of non-garbage clauses. + +size_t Internal::flush_occs (int lit) { + Occs &os = occs (lit); + const const_occs_iterator end = os.end (); + occs_iterator j = os.begin (); + const_occs_iterator i; + size_t res = 0; + Clause *c; + for (i = j; i != end; i++) { + c = *i; + if (c->collect ()) + continue; + *j++ = c->moved ? c->copy : c; + // assert (!c->redundant); // -> not true in sweeping + res++; + } + os.resize (j - os.begin ()); + shrink_occs (os); + return res; +} + +// Update watch lists before deleting garbage clauses in the context of +// 'reduce' where we watch and no occurrence lists. We have to protect +// reason clauses not be collected and thus we have this additional check +// hidden in 'Clause.collect', which for the root level context of +// preprocessing is actually redundant. + +inline void Internal::flush_watches (int lit, Watches &saved) { + assert (saved.empty ()); + Watches &ws = watches (lit); + const const_watch_iterator end = ws.end (); + watch_iterator j = ws.begin (); + const_watch_iterator i; + for (i = j; i != end; i++) { + Watch w = *i; + Clause *c = w.clause; + if (c->collect ()) + continue; + if (c->moved) + c = w.clause = c->copy; + w.size = c->size; + const int new_blit_pos = (c->literals[0] == lit); + LOG (c, "clause in flush_watch starting from %d", lit); + assert (c->literals[!new_blit_pos] == lit); /*FW1*/ + w.blit = c->literals[new_blit_pos]; + if (w.binary ()) + *j++ = w; + else + saved.push_back (w); + } + ws.resize (j - ws.begin ()); + for (const auto &w : saved) + ws.push_back (w); + saved.clear (); + shrink_vector (ws); +} + +void Internal::flush_all_occs_and_watches () { + if (occurring ()) + for (auto idx : vars) + flush_occs (idx), flush_occs (-idx); + + if (watching ()) { + Watches tmp; + for (auto idx : vars) + flush_watches (idx, tmp), flush_watches (-idx, tmp); + } +} + +/*------------------------------------------------------------------------*/ + +void Internal::update_reason_references () { + LOG ("update assigned reason references"); +#ifdef LOGGING + size_t count = 0; +#endif + for (auto &lit : trail) { + if (!active (lit)) + continue; + Var &v = var (lit); + Clause *c = v.reason; + if (!c) + continue; + if (c == external_reason) + continue; + LOG (c, "updating assigned %d reason", lit); + assert (c->reason); + assert (c->moved); + Clause *d = c->copy; + v.reason = d; +#ifdef LOGGING + count++; +#endif + } + LOG ("updated %zd assigned reason references", count); +} + +/*------------------------------------------------------------------------*/ + +// This is a simple garbage collector which does not move clauses. It needs +// less space than the arena based clause allocator, but is not as cache +// efficient, since the copying garbage collector can put clauses together +// which are likely accessed after each other. + +void Internal::delete_garbage_clauses () { + + flush_all_occs_and_watches (); + + LOG ("deleting garbage clauses"); +#ifndef QUIET + int64_t collected_bytes = 0, collected_clauses = 0; +#endif + const auto end = clauses.end (); + auto j = clauses.begin (), i = j; + while (i != end) { + Clause *c = *j++ = *i++; + if (!c->collect ()) + continue; +#ifndef QUIET + collected_bytes += c->bytes (); + collected_clauses++; +#endif + delete_clause (c); + j--; + } + clauses.resize (j - clauses.begin ()); + shrink_vector (clauses); + + PHASE ("collect", stats.collections, + "collected %" PRId64 " bytes of %" PRId64 " garbage clauses", + collected_bytes, collected_clauses); +} + +/*------------------------------------------------------------------------*/ + +// This is the start of the copying garbage collector using the arena. At +// the core is the following function, which copies a clause to the 'to' +// space of the arena. Be careful if this clause is a reason of an +// assignment. In that case update the reason reference. +// +void Internal::copy_clause (Clause *c) { + LOG (c, "moving"); + assert (!c->moved); + char *p = (char *) c; + char *q = arena.copy (p, c->bytes ()); + c->copy = (Clause *) q; + c->moved = true; + LOG ("copied clause[%" PRId64 "] from %p to %p", c->id, (void *) c, + (void *) c->copy); +} + +// This is the moving garbage collector. + +void Internal::copy_non_garbage_clauses () { + + size_t collected_clauses = 0, collected_bytes = 0; + size_t moved_clauses = 0, moved_bytes = 0; + + // First determine 'moved_bytes' and 'collected_bytes'. + // + for (const auto &c : clauses) + if (!c->collect ()) + moved_bytes += c->bytes (), moved_clauses++; + else + collected_bytes += c->bytes (), collected_clauses++; + + PHASE ("collect", stats.collections, + "moving %zd bytes %.0f%% of %zd non garbage clauses", moved_bytes, + percent (moved_bytes, collected_bytes + moved_bytes), + moved_clauses); + (void) moved_clauses, (void) collected_clauses, (void) collected_bytes; + // Prepare 'to' space of size 'moved_bytes'. + // + arena.prepare (moved_bytes); + + // Keep clauses in arena in the same order. + // + if (opts.arenacompact) + for (const auto &c : clauses) + if (!c->collect () && arena.contains (c)) + copy_clause (c); + + if (opts.arenatype == 1 || !watching ()) { + + // Localize according to current clause order. + + // If the option 'opts.arenatype == 1' is set, then this means the + // solver uses the original order of clauses. If there are no watches, + // we can not use the watched based copying policies below. This + // happens if garbage collection is triggered during bounded variable + // elimination. + + // Copy clauses according to the order of calling 'copy_clause', which + // in essence just gives a compacting garbage collector, since their + // relative order is kept, and actually already gives the largest + // benefit due to better cache locality. + + for (const auto &c : clauses) + if (!c->moved && !c->collect ()) + copy_clause (c); + + } else if (opts.arenatype == 2) { + + // Localize according to (original) variable order. + + // This is almost the version used by MiniSAT and descendants. + // Our version uses saved phases too. + + for (int sign = -1; sign <= 1; sign += 2) + for (auto idx : vars) + for (const auto &w : watches (sign * likely_phase (idx))) + if (!w.clause->moved && !w.clause->collect ()) + copy_clause (w.clause); + + } else { + + // Localize according to decision queue order. + + // This is the default for search. It allocates clauses in the order of + // the decision queue and also uses saved phases. It seems faster than + // the MiniSAT version and thus we keep 'opts.arenatype == 3'. + + assert (opts.arenatype == 3); + + for (int sign = -1; sign <= 1; sign += 2) + for (int idx = queue.last; idx; idx = link (idx).prev) + for (const auto &w : watches (sign * likely_phase (idx))) + if (!w.clause->moved && !w.clause->collect ()) + copy_clause (w.clause); + } + + // Do not forget to move clauses which are not watched, which happened in + // a rare situation, and now is only left as defensive code. + // + for (const auto &c : clauses) + if (!c->collect () && !c->moved) + copy_clause (c); + + flush_all_occs_and_watches (); + update_reason_references (); + + // Replace and flush clause references in 'clauses'. + // + const auto end = clauses.end (); + auto j = clauses.begin (), i = j; + for (; i != end; i++) { + Clause *c = *i; + if (c->collect ()) + delete_clause (c); + else + assert (c->moved), *j++ = c->copy, deallocate_clause (c); + } + clauses.resize (j - clauses.begin ()); + if (clauses.size () < clauses.capacity () / 2) + shrink_vector (clauses); + + if (opts.arenasort) + rsort (clauses.begin (), clauses.end (), pointer_rank ()); + + // Release 'from' space completely and then swap 'to' with 'from'. + // + arena.swap (); + + PHASE ("collect", stats.collections, + "collected %zd bytes %.0f%% of %zd garbage clauses", + collected_bytes, + percent (collected_bytes, collected_bytes + moved_bytes), + collected_clauses); +} + +/*------------------------------------------------------------------------*/ + +// Maintaining clause statistics is complex and error prone but necessary +// for proper scheduling of garbage collection, particularly during bounded +// variable elimination. With this function we can check whether these +// statistics are updated correctly. + +void Internal::check_clause_stats () { +#ifndef NDEBUG + int64_t irredundant = 0, redundant = 0, total = 0, irrlits = 0; + for (const auto &c : clauses) { + if (c->garbage) + continue; + if (c->redundant) + redundant++; + else + irredundant++; + if (!c->redundant) + irrlits += c->size; + total++; + } + assert (stats.current.irredundant == irredundant); + assert (stats.current.redundant == redundant); + assert (stats.current.total == total); + assert (stats.irrlits == irrlits); +#endif +} + +/*------------------------------------------------------------------------*/ + +// only delete binary clauses from watch list that are already mark as +// deleted. +void Internal::remove_garbage_binaries () { + if (unsat) + return; + START (collect); + + if (!protected_reasons) + protect_reasons (); + int backtrack_level = level + 1; + Watches saved; + for (auto v : vars) { + for (auto lit : {-v, v}) { + assert (saved.empty ()); + Watches &ws = watches (lit); + const const_watch_iterator end = ws.end (); + watch_iterator j = ws.begin (); + const_watch_iterator i; + for (i = j; i != end; i++) { + Watch w = *i; + *j++ = w; + Clause *c = w.clause; + COVER (!w.binary () && c->size == 2); + if (!w.binary ()) + continue; + if (c->reason && c->garbage) { + COVER (true); + assert (c->size == 2); + backtrack_level = + min (backtrack_level, var (c->literals[0]).level); + LOG ("need to backtrack to before level %d", backtrack_level); + --j; + continue; + } + if (!c->collect ()) + continue; + LOG (c, "removing from watch list"); + --j; + } + ws.resize (j - ws.begin ()); + shrink_vector (ws); + } + } + delete_garbage_clauses (); + unprotect_reasons (); + if (backtrack_level - 1 < level) + backtrack (backtrack_level - 1); + STOP (collect); +} + +/*------------------------------------------------------------------------*/ + +bool Internal::arenaing () { return opts.arena && (stats.collections > 1); } + +void Internal::garbage_collection () { + if (unsat) + return; + START (collect); + report ('G', 1); + stats.collections++; + mark_satisfied_clauses_as_garbage (); + if (!protected_reasons) + protect_reasons (); + if (arenaing ()) + copy_non_garbage_clauses (); + else + delete_garbage_clauses (); + check_clause_stats (); + check_var_stats (); + unprotect_reasons (); + report ('C', 1); + STOP (collect); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/compact.cpp b/src/sat/cadical/compact.cpp new file mode 100644 index 000000000..1bf423993 --- /dev/null +++ b/src/sat/cadical/compact.cpp @@ -0,0 +1,551 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Compacting removes holes generated by inactive variables (fixed, +// eliminated, substituted or pure) by mapping active variables indices down +// to a contiguous interval of indices. + +/*------------------------------------------------------------------------*/ + +bool Internal::compacting () { + if (level) + return false; + if (!opts.compact) + return false; + if (stats.conflicts < lim.compact) + return false; + int inactive = max_var - active (); + assert (inactive >= 0); + if (!inactive) + return false; + if (inactive < opts.compactmin) + return false; + return inactive >= (1e-3 * opts.compactlim) * max_var; +} + +/*------------------------------------------------------------------------*/ + +struct Mapper { + + Internal *internal; + int new_max_var; // New 'max_var' after compacting. + int *table; // Old variable index to new literal map. + int first_fixed; // First fixed variable index. + int map_first_fixed; // Mapped literal of first fixed variable. + signed char first_fixed_val; // Value of first fixed variable. + size_t new_vsize; + + /*----------------------------------------------------------------------*/ + // We produce a compacting garbage collector like map of old 'src' to + // new 'dst' variables. Inactive variables are just skipped except for + // fixed ones which will be mapped to the first fixed variable (in the + // appropriate phase). This avoids to handle the case 'fixed value' + // separately as it is done in Lingeling, where fixed variables are + // mapped to the internal variable '1'. + // + Mapper (Internal *i) + : internal (i), new_max_var (0), first_fixed (0), map_first_fixed (0), + first_fixed_val (0) { + table = new int[internal->max_var + 1u]; + clear_n (table, internal->max_var + 1u); + + assert (!internal->level); + + for (auto src : internal->vars) { + const Flags &f = internal->flags (src); + if (f.active ()) + table[src] = ++new_max_var; + else if (f.fixed () && !first_fixed) + table[first_fixed = src] = map_first_fixed = ++new_max_var; + } + + first_fixed_val = first_fixed ? internal->val (first_fixed) : 0; + new_vsize = new_max_var + 1u; + } + + ~Mapper () { delete[] table; } + + /*----------------------------------------------------------------------*/ + // Map old variable indices. A result of zero means not mapped. + // + int map_idx (int src) { + assert (0 < src); + assert (src <= internal->max_var); + const int res = table[src]; + assert (res <= new_max_var); + return res; + } + + /*----------------------------------------------------------------------*/ + // The 'map_idx' above is just a look-up into the 'table'. Here we have + // to care about signedness of 'src', and in addition that fixed variables + // have all to be mapped to the first fixed variable 'first_fixed'. + // + int map_lit (int src) { + int res = map_idx (abs (src)); + if (!res) { + const signed char tmp = internal->val (src); + if (tmp) { + assert (first_fixed); + res = map_first_fixed; + if (tmp != first_fixed_val) + res = -res; + } + } else if ((src) < 0) + res = -res; + assert (abs (res) <= new_max_var); + return res; + } + + /*----------------------------------------------------------------------*/ + // Map positive variable indices in vector. + // + template void map_vector (vector &v) { + for (auto src : internal->vars) { + const int dst = map_idx (src); + if (!dst) + continue; + assert (0 < dst); + assert (dst <= src); + v[dst] = v[src]; + } + v.resize (new_vsize); + shrink_vector (v); + } + + /*----------------------------------------------------------------------*/ + // Map positive and negative variable indices in two-sided vector. + // + template void map2_vector (vector &v) { + for (auto src : internal->vars) { + const int dst = map_idx (src); + if (!dst) + continue; + assert (0 < dst); + assert (dst <= src); + v[2 * dst] = v[2 * src]; + v[2 * dst + 1] = v[2 * src + 1]; + } + v.resize (2 * new_vsize); + shrink_vector (v); + } + + /*----------------------------------------------------------------------*/ + // Map a vector of literals, flush inactive literals, then resize and + // shrink it to fit the new size after flushing. + // + void map_flush_and_shrink_lits (vector &v) { + const auto end = v.end (); + auto j = v.begin (), i = j; + for (; i != end; i++) { + const int src = *i; + int dst = map_idx (abs (src)); + assert (abs (dst) <= abs (src)); + if (!dst) + continue; + if (src < 0) + dst = -dst; + *j++ = dst; + } + v.resize (j - v.begin ()); + shrink_vector (v); + } +}; + +/*------------------------------------------------------------------------*/ + +static signed char *ignore_clang_analyze_memory_leak_warning; + +void Internal::compact () { + + START (compact); + + assert (active () < max_var); + + stats.compacts++; + + assert (!level); + assert (!unsat); + assert (!conflict); + assert (clause.empty ()); + assert (levels.empty ()); + assert (analyzed.empty ()); + assert (minimized.empty ()); + assert (control.size () == 1); + assert (propagated == trail.size ()); + + garbage_collection (); + + Mapper mapper (this); + + if (mapper.first_fixed) + LOG ("found first fixed %d", + sign (mapper.first_fixed_val) * mapper.first_fixed); + else + LOG ("no variable fixed"); + + if (!assumptions.empty ()) { + assert (!external->assumptions.empty ()); + LOG ("temporarily reset internal assumptions"); + reset_assumptions (); + } + + const bool is_constraint = !constraint.empty (); + if (is_constraint) { + assert (!external->constraint.empty ()); + LOG ("temporarily reset internal constraint"); + reset_constraint (); + } + + /*======================================================================*/ + // In this first part we only map stuff without reallocation / shrinking. + /*======================================================================*/ + + // Flush the external indices. This has to occur before we map 'vals'. + // Also fixes external units. + // + for (auto eidx : external->vars) { + int src = external->e2i[eidx]; + if (!src) { + continue; + } + if (lrat || frat) { + assert (eidx > 0); + assert (external->ext_units.size () >= (size_t) 2 * eidx + 1); + int64_t id1 = external->ext_units[2 * eidx]; + int64_t id2 = external->ext_units[2 * eidx + 1]; + assert (!id1 || !id2); + if (!id1 && !id2) { + int64_t new_id1 = unit_clauses (2 * src); + int64_t new_id2 = unit_clauses (2 * src + 1); + external->ext_units[2 * eidx] = new_id1; + external->ext_units[2 * eidx + 1] = new_id2; + } + } + int dst = mapper.map_lit (src); + LOG ("compact %" PRId64 + " maps external %d to internal %d from internal %d", + stats.compacts, eidx, dst, src); + external->e2i[eidx] = dst; + } + + // Delete garbage units. Needs to occur before resizing unit_clauses + // + if (lrat || frat) { + for (auto src : internal->vars) { + const int dst = mapper.map_idx (src); + assert (dst <= src); + const signed char tmp = internal->val (src); + if (!dst && !tmp) { + unit_clauses (2 * src) = 0; + unit_clauses (2 * src + 1) = 0; + continue; + } + if (!tmp || src == mapper.first_fixed) { + assert (0 < dst); + if (dst == src) + continue; + assert (!unit_clauses (2 * dst) && !unit_clauses (2 * dst + 1)); + unit_clauses (2 * dst) = unit_clauses (2 * src); + unit_clauses (2 * dst + 1) = unit_clauses (2 * src + 1); + unit_clauses (2 * src) = 0; + unit_clauses (2 * src + 1) = 0; + continue; + } + int64_t id = unit_clauses (2 * src); + int lit = src; + if (!id) { + id = unit_clauses (2 * src + 1); + lit = -lit; + } + unit_clauses (2 * src) = 0; + unit_clauses (2 * src + 1) = 0; + assert (id); + } + unit_clauses_idx.resize (2 * mapper.new_vsize); + shrink_vector (unit_clauses_idx); + } + // Map the literals in all clauses. + // + for (const auto &c : clauses) { + assert (!c->garbage); + for (auto &src : *c) { + assert (!val (src)); + int dst; + dst = mapper.map_lit (src); + assert (dst || c->garbage); + src = dst; + } + } + + // Map the blocking literals in all watches. + // + if (!wtab.empty ()) + for (auto lit : lits) + for (auto &w : watches (lit)) + w.blit = mapper.map_lit (w.blit); + + // We first flush inactive variables and map the links in the queue. This + // has to be done before we map the actual links data structure 'links'. + { + int prev = 0, mapped_prev = 0, next; + for (int idx = queue.first; idx; idx = next) { + next = links[idx].next; + if (idx == mapper.first_fixed) + continue; + const int dst = mapper.map_idx (idx); + if (!dst) + continue; + assert (active (idx)); + if (prev) + links[prev].next = dst; + else + queue.first = dst; + links[idx].prev = mapped_prev; + mapped_prev = dst; + prev = idx; + } + if (prev) + links[prev].next = 0; + else + queue.first = 0; + queue.unassigned = queue.last = mapped_prev; + } + + /*======================================================================*/ + // In the second part we map, flush and shrink arrays. + /*======================================================================*/ + + assert (trail.size () == num_assigned); + mapper.map_flush_and_shrink_lits (trail); + propagated = trail.size (); + num_assigned = trail.size (); + if (mapper.first_fixed) { + assert (trail.size () == 1); + var (mapper.first_fixed).trail = 0; // before mapping 'vtab' + } else + assert (trail.empty ()); + + if (!probes.empty ()) + mapper.map_flush_and_shrink_lits (probes); + + if (!sweep_schedule.empty ()) + mapper.map_flush_and_shrink_lits (sweep_schedule); + + /*======================================================================*/ + // In the third part we map stuff and also reallocate memory. + /*======================================================================*/ + + // Now we continue in reverse order of allocated bytes, e.g., see + // 'Internal::enlarge' which reallocates in order of allocated bytes. + + mapper.map_vector (ftab); + mapper.map_vector (parents); + mapper.map_vector (marks); + mapper.map_vector (phases.saved); + mapper.map_vector (phases.forced); + mapper.map_vector (phases.target); + mapper.map_vector (phases.best); + mapper.map_vector (phases.prev); + mapper.map_vector (phases.min); + + // Special code for 'frozentab'. + // + for (auto src : vars) { + const int dst = abs (mapper.map_lit (src)); + if (!dst) + continue; + if (src == dst) + continue; + assert (dst < src); + if ((size_t) src >= frozentab.size ()) + break; + if ((size_t) dst >= frozentab.size ()) + break; + frozentab[dst] += frozentab[src]; + frozentab[src] = 0; + } + frozentab.resize (min (frozentab.size (), mapper.new_vsize)); + shrink_vector (frozentab); + + // Special code for 'relevanttab'. + // + if (external) { + for (auto src : vars) { + const int dst = abs (mapper.map_lit (src)); + if (!dst) + continue; + if (src == dst) + continue; + assert (dst < src); + + relevanttab[dst] += relevanttab[src]; + relevanttab[src] = 0; + } + relevanttab.resize (mapper.new_vsize); + shrink_vector (relevanttab); + } + + /*----------------------------------------------------------------------*/ + + if (!external->assumptions.empty ()) { + + for (const auto &elit : external->assumptions) { + assert (elit); + assert (elit != INT_MIN); + int eidx = abs (elit); + assert (eidx <= external->max_var); + int ilit = external->e2i[eidx]; + assert (ilit); // Because we froze all!!! + if (elit < 0) + ilit = -ilit; + assume (ilit); + } + + PHASE ("compact", stats.compacts, "reassumed %zd external assumptions", + external->assumptions.size ()); + } + + // Special case for 'val' as for 'val' we trade branch less code for + // memory and always allocated an [-maxvar,...,maxvar] array. + { + signed char *new_vals = new signed char[2 * mapper.new_vsize]; + ignore_clang_analyze_memory_leak_warning = new_vals; + new_vals += mapper.new_vsize; + for (auto src : vars) + new_vals[-mapper.map_idx (src)] = vals[-src]; + for (auto src : vars) + new_vals[mapper.map_idx (src)] = vals[src]; + new_vals[0] = 0; + vals -= vsize; + delete[] vals; + vals = new_vals; + vsize = mapper.new_vsize; + } + + // 'constrain' uses 'val', so this code has to be after remapping that + if (is_constraint) { + assert (!level); + assert (!external->constraint.back ()); + for (auto elit : external->constraint) { + assert (elit != INT_MIN); + int eidx = abs (elit); + assert (eidx <= external->max_var); + int ilit = external->e2i[eidx]; + assert (!ilit == !elit); + if (elit < 0) + ilit = -ilit; + LOG ("re adding lit external %d internal %d to constraint", elit, + ilit); + constrain (ilit); + } + PHASE ("compact", stats.compacts, + "added %zd external literals to constraint", + external->constraint.size () - 1); + } + + mapper.map_vector (i2e); + mapper.map2_vector (ptab); + mapper.map_vector (btab); + mapper.map_vector (gtab); + mapper.map_vector (links); + mapper.map_vector (vtab); + if (!ntab.empty ()) + mapper.map2_vector (ntab); + if (!wtab.empty ()) + mapper.map2_vector (wtab); + if (!otab.empty ()) + mapper.map2_vector (otab); + if (!rtab.empty ()) + mapper.map2_vector (rtab); + if (!big.empty ()) + mapper.map2_vector (big); + + /*======================================================================*/ + // In the fourth part we map the binary heap for scores. + /*======================================================================*/ + + // The simplest way to map a binary heap is to get all elements from the + // heap and reinsert them. This could be slightly improved in terms of + // speed if we add a 'flush (int * map)' function to 'Heap', but that is + // pretty complicated and would require that the 'Heap' knows that mapped + // elements with 'zero' destination should be flushed. + + vector saved; + assert (saved.empty ()); + if (!scores.empty ()) { + while (!scores.empty ()) { + const int src = scores.front (); + scores.pop_front (); + const int dst = mapper.map_idx (src); + if (!dst) + continue; + if (src == mapper.first_fixed) + continue; + saved.push_back (dst); + } + scores.erase (); + } + mapper.map_vector (stab); + if (!saved.empty ()) { + for (const auto idx : saved) + scores.push_back (idx); + scores.shrink (); + } + + /*----------------------------------------------------------------------*/ + + PHASE ("compact", stats.compacts, + "reducing internal variables from %d to %d", max_var, + mapper.new_max_var); + + /*----------------------------------------------------------------------*/ + + // Need to adjust the target and best assigned counters too. + + size_t new_target_assigned = 0, new_best_assigned = 0; + + for (auto idx : Range (mapper.new_max_var)) { + if (phases.target[idx]) + new_target_assigned++; + if (phases.best[idx]) + new_best_assigned++; + } + + LOG ("reset target assigned from %zd to %zd", target_assigned, + new_target_assigned); + LOG ("reset best assigned from %zd to %zd", best_assigned, + new_best_assigned); + + target_assigned = new_target_assigned; + best_assigned = new_best_assigned; + no_conflict_until = 0; + notified = 0; + + INIT_EMA (averages.current.trail.fast, opts.ematrailfast); + INIT_EMA (averages.current.trail.slow, opts.ematrailslow); + + /*----------------------------------------------------------------------*/ + + max_var = mapper.new_max_var; + + stats.unused = 0; + stats.inactive = stats.now.fixed = mapper.first_fixed ? 1 : 0; + stats.now.substituted = stats.now.eliminated = stats.now.pure = 0; + + check_var_stats (); + + int64_t delta = opts.compactint * (stats.compacts + 1); + lim.compact = stats.conflicts + delta; + + PHASE ("compact", stats.compacts, + "new compact limit %" PRId64 " after %" PRId64 " conflicts", + lim.compact, delta); + + STOP (compact); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/condition.cpp b/src/sat/cadical/condition.cpp new file mode 100644 index 000000000..7fd70a465 --- /dev/null +++ b/src/sat/cadical/condition.cpp @@ -0,0 +1,940 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Globally blocked clause elimination (which we call here 'conditioning') +// is described first in the PhD thesis of Benjamin Kiesl from 2019. An +// extended version, which in particular describes the algorithm implemented +// below is in our invited ATVA'19 paper [KieslHeuleBiere-ATVA'19]. This +// accordingly needs witnesses consisting potentially of more than one +// literal. It is the first technique implemented in CaDiCaL with this +// feature (PR clause elimination thus should work in principle too). + +// Basically globally blocked clauses are like set blocked clauses, except +// that the witness cube (of literals to be flipped during reconstruction) +// can contain variables which are not in the blocked clause. This +// can simulate some interesting global optimizations like 'headlines' from +// the FAN algorithm for ATPG. The technique was actually motivated to +// simulate this optimization. It turns out that globally blocked clauses +// can be seen as 'conditional autarkies', where in essence the condition +// cube is the negation of the globally blocked redundant clause (it +// needs to contain one autarky literal though) and the autarky part +// represents the witness. + +/*------------------------------------------------------------------------*/ + +// Elimination of globally blocked clauses is first tried in regular +// intervals in terms of the number of conflicts. Then the main heuristics +// is to trigger 'condition' if the decision level is above the current +// moving average of the back jump level. + +// TODO We might need to consider less frequent conditioning. + +bool Internal::conditioning () { + + if (!opts.condition) + return false; + if (!preprocessing && !opts.inprocessing) + return false; + if (preprocessing) + assert (lim.preprocessing); + + // Triggered in regular 'opts.conditionint' conflict intervals. + // + if (lim.condition > stats.conflicts) + return false; + + if (!level) + return false; // One decision necessary. + + if (level <= averages.current.jump) + return false; // Main heuristic. + + if (!stats.current.irredundant) + return false; + double remain = active (); + if (!remain) + return false; + double ratio = stats.current.irredundant / remain; + return ratio <= opts.conditionmaxrat; +} + +/*------------------------------------------------------------------------*/ + +// We start with the current assignment and then temporarily unassign +// literals. They are reassigned afterwards. The global state of the CDCL +// solver should not change though. Thus we copied from 'search_unassign' +// in 'backtrack.cpp' what is needed to unassign literals and then from +// 'search_assign' in 'propagate.cpp' what is needed for reassigning +// literals, but restricted the copied code to only updating the actual +// assignment (in 'vals') and not changing anything else. + +// We use temporarily unassigning for two purposes. First, if a conditional +// literal does not occur negated in a candidate clause it is unassigned. +// Second, as a minor optimization, we first unassign all root-level +// assigned (fixed) literals, to avoid checking the decision level of +// literals during the procedure. + +void Internal::condition_unassign (int lit) { + LOG ("condition unassign %d", lit); + assert (val (lit) > 0); + set_val (lit, 0); +} + +void Internal::condition_assign (int lit) { + LOG ("condition assign %d", lit); + assert (!val (lit)); + set_val (lit, 1); +} + +/*------------------------------------------------------------------------*/ + +// The current partition into conditional part and autarky part during +// refinement is represented through a conditional bit in 'marks'. + +inline bool Internal::is_conditional_literal (int lit) const { + return val (lit) > 0 && getbit (lit, 0); +} + +inline bool Internal::is_autarky_literal (int lit) const { + return val (lit) > 0 && !getbit (lit, 0); +} + +inline void Internal::mark_as_conditional_literal (int lit) { + LOG ("marking %d as conditional literal", lit); + assert (val (lit) > 0); + setbit (lit, 0); + assert (is_conditional_literal (lit)); + assert (!is_autarky_literal (lit)); +} + +inline void Internal::unmark_as_conditional_literal (int lit) { + LOG ("unmarking %d as conditional literal", lit); + assert (is_conditional_literal (lit)); + unsetbit (lit, 0); +} + +/*------------------------------------------------------------------------*/ + +// We also need to know the literals which are in the current clause. These +// are just marked (also in 'marks' but with the (signed) upper two bits). +// We need a signed mark here, since we have to distinguish positive and +// negative occurrences of literals in the candidate clause. + +inline bool Internal::is_in_candidate_clause (int lit) const { + return marked67 (lit) > 0; +} + +inline void Internal::mark_in_candidate_clause (int lit) { + LOG ("marking %d as literal of the candidate clause", lit); + mark67 (lit); + assert (is_in_candidate_clause (lit)); + assert (!is_in_candidate_clause (-lit)); +} + +inline void Internal::unmark_in_candidate_clause (int lit) { + LOG ("unmarking %d as literal of the candidate clause", lit); + assert (is_in_candidate_clause (lit)); + unmark67 (lit); +} + +/*------------------------------------------------------------------------*/ + +struct less_conditioned { + bool operator() (Clause *a, Clause *b) { + return !a->conditioned && b->conditioned; + } +}; + +// This is the function for eliminating globally blocked clauses. It is +// triggered during CDCL search according to 'conditioning' above and uses +// the current assignment as basis to find globally blocked clauses. + +long Internal::condition_round (long delta) { + + long limit; +#ifndef QUIET + long props = 0; +#endif + if (LONG_MAX - delta < stats.condprops) + limit = LONG_MAX; + else + limit = stats.condprops + delta; + + size_t initial_trail_level = trail.size (); + int initial_level = level; + + LOG ("initial trail level %zd", initial_trail_level); + + protect_reasons (); + +#if defined(LOGGING) || !defined(NDEBUG) + int additionally_assigned = 0; +#endif + + for (auto idx : vars) { + const signed char tmp = val (idx); + Var &v = var (idx); + if (tmp) { + if (v.level) { + const int lit = tmp < 0 ? -idx : idx; + if (!active (idx)) { + LOG ("temporarily unassigning inactive literal %d", lit); + condition_unassign (lit); + } + if (frozen (idx)) { + LOG ("temporarily unassigning frozen literal %d", lit); + condition_unassign (lit); + } + } + } else if (frozen (idx)) { + LOG ("keeping frozen literal %d unassigned", idx); + } else if (!active (idx)) { + LOG ("keeping inactive literal %d unassigned", idx); + } else { // if (preprocessing) { + if (initial_level == level) { + level++; + LOG ("new condition decision level"); + } + const int lit = decide_phase (idx, true); + condition_assign (lit); + v.level = level; + trail.push_back (lit); +#if defined(LOGGING) || !defined(NDEBUG) + additionally_assigned++; +#endif + } + } + LOG ("assigned %d additional literals", additionally_assigned); + + // We compute statistics about the size of the assignments. + // + // The initial assignment consists of the non-root-level assigned literals + // split into a conditional and an autarky part. The conditional part + // consists of literals assigned true and occurring negated in a clause + // (touch the clause), which does not contain another literal assigned to + // true. This initial partition is the same for all refinements used in + // checking whether a candidate clause is globally blocked. + // + // For each candidate clause some of the conditional literals have to be + // unassigned, and the autarky is shrunken by turning some of the autarky + // literals into conditional literals (which might get unassigned in a + // later refinement though). + // + // The fix-point of this procedure produces a final assignment, which + // consists of the remaining assigned literals, again split into a + // conditional and an autarky part. + // + struct { + size_t assigned, conditional, autarky; + } initial, remain; + + initial.assigned = 0; + for (auto idx : vars) { + const signed char tmp = val (idx); + if (!tmp) + continue; + if (!var (idx).level) + continue; + LOG ("initial assignment %ds", tmp < 0 ? -idx : idx); + initial.assigned++; + } + + PHASE ("condition", stats.conditionings, "initial assignment of size %zd", + initial.assigned); + + // For each candidate clause we refine the assignment (monotonically), + // by unassigning some conditional literals and turning some autarky + // literals into conditionals. + // + // As the conditional part is usually smaller than the autarky part our + // implementation only explicitly maintains the initial conditional part, + // with conditional bit set to true through 'mark_as_conditional_literal'. + // The autarky part consists of all literals assigned true which do not + // have their conditional bit set to true. Since in both cases the + // literal has to be assigned true, we only need a single bit for both the + // literal as well as its negation (it does not have to be 'signed'). + // + vector conditional; + + vector candidates; // Gather candidate clauses. +#ifndef QUIET + size_t watched = 0; // Number of watched clauses. +#endif + + initial.autarky = initial.assigned; // Initially all are in autarky + initial.conditional = 0; // and none in conditional part. + + // Upper bound on the number of watched clauses. In principle one could + // use 'SIZE_MAX' but this is not available by default (yet). + // + const size_t size_max = clauses.size () + 1; + + // Initialize additional occurrence lists. + // + init_occs (); + + // Number of previously conditioned and unconditioned candidates. + // + size_t conditioned = 0, unconditioned = 0; + + // Now go over all (non-garbage) irredundant clauses and check whether + // they are candidates, have to be watched, or whether they force the + // negation of some of their literals to be conditional initially. + // + for (const auto &c : clauses) { + if (c->garbage) + continue; // Can already be ignored. + if (c->redundant) + continue; // Ignore redundant clauses too. + + // First determine the following numbers for the candidate clause + // (restricted to non-root-level assignments). + // + int positive = 0; // Number true literals. + int negative = 0; // Number false literals. + int watch = 0; // True Literal to watch. + // + size_t minsize = size_max; // Number of occurrences of 'watch'. + // + // But also ignore root-level satisfied but not yet garbage clauses. + // + bool satisfied = false; // Root level satisfied. + // + for (const_literal_iterator l = c->begin (); + !satisfied && l != c->end (); l++) { + const int lit = *l; + const signed char tmp = val (lit); + if (tmp && !var (lit).level) + satisfied = (tmp > 0); + else if (tmp < 0) + negative++; + else if (tmp > 0) { + const size_t size = occs (lit).size (); + if (size < minsize) + watch = lit, minsize = size; + positive++; + } + } + if (satisfied) { // Ignore root-level satisfied clauses. + mark_garbage (c); // But mark them as garbage already now. + continue; // ... with next clause 'c'. + } + + // Candidates are clauses with at least a positive literal in it. + // + if (positive > 0) { + LOG (c, "found %d positive literals in candidate", positive); + candidates.push_back (c); + if (c->conditioned) + conditioned++; + else + unconditioned++; + } + + // Only one positive literal in each clauses with also at least one + // negative literal has to be watched in occurrence lists. These + // watched clauses will be checked to contain only negative literals as + // soon such a positive literal is unassigned. If this is the case + // these false literals have to be unassigned and potentially new + // conditional literals have to be determined. + // + // Note that only conditional literals are unassigned. However it does + // not matter that we might also watch autarky literals, because either + // such an autarky literal remains a witness that the clause is + // satisfied as long it remains an autarky literal. Otherwise at one + // point it becomes conditional and is unassigned, but then a + // replacement watch will be searched. + // + if (negative > 0 && positive > 0) { + LOG (c, "found %d negative literals in candidate", negative); + assert (watch); + assert (val (watch) > 0); + Occs &os = occs (watch); + assert (os.size () == minsize); + os.push_back (c); +#ifndef QUIET + watched++; +#endif + LOG (c, "watching %d with %zd occurrences in", watch, minsize); + } + + // The initial global conditional part for the current assignment is + // extracted from clauses with only negative literals. It is the same + // for all considered candidate clauses. These negative literals make up + // the global conditional part, are marked here. + // + if (negative > 0 && !positive) { + + size_t new_conditionals = 0; + + for (const_literal_iterator l = c->begin (); l != c->end (); l++) { + const int lit = *l; + signed char tmp = val (lit); + if (!tmp) + continue; + assert (tmp < 0); + if (!var (lit).level) + continue; // Not unassigned yet! + if (is_conditional_literal (-lit)) + continue; + mark_as_conditional_literal (-lit); + conditional.push_back (-lit); + new_conditionals++; + } + if (new_conditionals > 0) + LOG (c, "marked %zu negations of literals as conditional in", + new_conditionals); + + initial.conditional += new_conditionals; + assert (initial.autarky >= new_conditionals); + initial.autarky -= new_conditionals; + } + + } // End of loop over all clauses to collect candidates etc. + + PHASE ("condition", stats.conditionings, "found %zd candidate clauses", + candidates.size ()); + PHASE ("condition", stats.conditionings, + "watching %zu literals and clauses", watched); + PHASE ("condition", stats.conditionings, + "initially %zd conditional literals %.0f%%", initial.conditional, + percent (initial.conditional, initial.assigned)); + PHASE ("condition", stats.conditionings, + "initially %zd autarky literals %.0f%%", initial.autarky, + percent (initial.autarky, initial.assigned)); +#ifdef LOGGING + for (size_t i = 0; i < conditional.size (); i++) { + LOG ("initial conditional %d", conditional[i]); + assert (is_conditional_literal (conditional[i])); + } + for (size_t i = 0; i < trail.size (); i++) + if (is_autarky_literal (trail[i])) + LOG ("initial autarky %d", trail[i]); +#endif + assert (initial.conditional == conditional.size ()); + assert (initial.assigned == initial.conditional + initial.autarky); + + stats.condassinit += initial.assigned; + stats.condcondinit += initial.conditional; + stats.condautinit += initial.autarky; + stats.condassvars += active (); + + // To speed-up and particularly simplify the code we unassign all + // root-level variables temporarily, actually all inactive assigned + // variables. This allows us to avoid tests on whether an assigned + // literal is actually root-level assigned and thus should be ignored (not + // considered to be assigned). For this to work we have to ignore root + // level satisfied clauses as done above. These are neither candidates + // nor have to be watched. Remaining originally root-level assigned + // literals in clauses are only set to false. + // + for (const auto &lit : trail) + if (fixed (lit)) + condition_unassign (lit); + + // Stack to save temporarily unassigned (conditional) literals. + // + vector unassigned; + + // Make sure to focus on clauses not tried before by marking clauses which + // have been checked before using the 'conditioned' bit of clauses. If all + // candidates have their bit set, we have to reset it. Since the + // assignment might be completely different then last time and thus also + // the set of candidates this method does not really exactly lead to a + // round robin scheme of scheduling clauses. + // + // TODO consider computing conditioned and unconditioned over all clauses. + // + assert (conditioned + unconditioned == candidates.size ()); + if (conditioned && unconditioned) { + stable_sort (candidates.begin (), candidates.end (), + less_conditioned ()); + PHASE ("condition", stats.conditionings, + "focusing on %zd candidates %.0f%% not tried last time", + unconditioned, percent (unconditioned, candidates.size ())); + } else if (conditioned && !unconditioned) { + for (auto const &c : candidates) { + assert (c->conditioned); + c->conditioned = false; // Reset 'conditioned' bit. + } + PHASE ("condition", stats.conditionings, + "all %zd candidates tried before", conditioned); + } else { + assert (!conditioned); + PHASE ("condition", stats.conditionings, "all %zd candidates are fresh", + unconditioned); + } + + // TODO prune assignments further! + // And thus might result in less watched clauses. + // So watching should be done here and not earlier. + // Also, see below, we might need to consider the negation of unassigned + // literals in candidate clauses as being watched. + + // Now try to block all candidate clauses. + // + long blocked = 0; // Number of Successfully blocked clauses. + // +#ifndef QUIET + size_t untried = candidates.size (); +#endif + for (const auto &c : candidates) { + + if (initial.autarky <= 0) + break; + + if (c->reason) + continue; + + bool terminated_or_limit_hit = true; + if (terminated_asynchronously ()) + LOG ("asynchronous termination detected"); + else if (stats.condprops >= limit) + LOG ("condition propagation limit %ld hit", limit); + else + terminated_or_limit_hit = false; + + if (terminated_or_limit_hit) { + PHASE ("condition", stats.conditionings, + "%zd candidates %.0f%% not tried after %ld propagations", + untried, percent (untried, candidates.size ()), props); + break; + } +#ifndef QUIET + untried--; +#endif + assert (!c->garbage); + assert (!c->redundant); + + LOG (c, "candidate"); + c->conditioned = 1; // Next time later. + + // We watch an autarky literal in the clause, and can stop trying to + // globally block the clause as soon it turns into a conditional + // literal and we can not find another one. If the fix-point assignment + // is reached and we still have an autarky literal left the watched one + // is reported as witness for this clause being globally blocked. + // + int watched_autarky_literal = 0; + + // First mark all true literals in the candidate clause and find an + // autarky literal which witnesses that this clause has still a chance + // to be globally blocked. + // + for (const_literal_iterator l = c->begin (); l != c->end (); l++) { + const int lit = *l; + mark_in_candidate_clause (lit); + if (watched_autarky_literal) + continue; + if (!is_autarky_literal (lit)) + continue; + watched_autarky_literal = lit; + + // TODO assign non-assigned literals to false? + // Which might need to trigger watching additional clauses. + } + + if (!watched_autarky_literal) { + LOG ("no initial autarky literal found"); + for (const_literal_iterator l = c->begin (); l != c->end (); l++) + unmark_in_candidate_clause (*l); + continue; + } + + stats.condcands++; // Only now ... + + LOG ("watching first autarky literal %d", watched_autarky_literal); + + // Save assignment sizes for statistics, logging and checking. + // + remain = initial; + + // Position of next conditional and unassigned literal to process in the + // 'conditional' and the 'unassigned' stack. + // + struct { + size_t conditional, unassigned; + } next = {0, 0}; + + assert (unassigned.empty ()); + assert (conditional.size () == initial.conditional); + + while (watched_autarky_literal && stats.condprops < limit && + next.conditional < conditional.size ()) { + + assert (next.unassigned == unassigned.size ()); + + const int conditional_lit = conditional[next.conditional++]; + LOG ("processing next conditional %d", conditional_lit); + assert (is_conditional_literal (conditional_lit)); + + if (is_in_candidate_clause (-conditional_lit)) { + LOG ("conditional %d negated in candidate clause", conditional_lit); + continue; + } + + LOG ("conditional %d does not occur negated in candidate clause", + conditional_lit); + + condition_unassign (conditional_lit); + assert (!is_conditional_literal (conditional_lit)); + unassigned.push_back (conditional_lit); + + assert (remain.assigned > 0); + assert (remain.conditional > 0); + remain.conditional--; + remain.assigned--; + + while (watched_autarky_literal && stats.condprops < limit && + next.unassigned < unassigned.size ()) { + const int unassigned_lit = unassigned[next.unassigned++]; + LOG ("processing next unassigned %d", unassigned_lit); + assert (!val (unassigned_lit)); +#ifndef QUIET + props++; +#endif + stats.condprops++; + + Occs &os = occs (unassigned_lit); + if (os.empty ()) + continue; + + // Traverse all watched clauses of 'unassigned_lit' and find + // replacement watches or if none is found turn the negation of all + // false autarky literals in that clause into conditional literals. + // If one of those autarky literals is the watched autarky literal + // in the candidate clause, that one has to be updated too. + // + // We expect that this loop is a hot-spot for the procedure and thus + // are more careful about accessing end points for iterating. + // + auto i = os.begin (), j = i; + for (; watched_autarky_literal && j != os.end (); j++) { + Clause *d = *i++ = *j; + + int replacement = 0; // New watched literal in 'd'. + int negative = 0; // Negative autarky literals in 'd'. + + for (const_literal_iterator l = d->begin (); l != d->end (); + l++) { + const int lit = *l; + const signed char tmp = val (lit); + if (tmp > 0) + replacement = lit; + if (tmp < 0 && is_autarky_literal (-lit)) + negative++; + } + + if (replacement) { + LOG ("found replacement %d for unassigned %d", replacement, + unassigned_lit); + LOG (d, "unwatching %d in", unassigned_lit); + i--; // Drop watch! + LOG (d, "watching %d in", replacement); + + assert (replacement != unassigned_lit); + occs (replacement).push_back (d); + + continue; // ... with next watched clause 'd'. + } + + LOG ("no replacement found for unassigned %d", unassigned_lit); + + // Keep watching 'd' by 'unassigned_lit' if no replacement found. + + if (!negative) { + LOG (d, "no negative autarky literals left in"); + continue; // ... with next watched clause 'd'. + } + + LOG (d, "found %d negative autarky literals in", negative); + + for (const_literal_iterator l = d->begin (); + watched_autarky_literal && l != d->end (); l++) { + const int lit = *l; + if (!is_autarky_literal (-lit)) + continue; + mark_as_conditional_literal (-lit); + conditional.push_back (-lit); + + remain.conditional++; + assert (remain.autarky > 0); + remain.autarky--; + + if (-lit != watched_autarky_literal) + continue; + + LOG ("need to replace autarky literal %d in candidate", -lit); + replacement = 0; + + // TODO save starting point because we only move it forward? + + for (const_literal_iterator k = c->begin (); + !replacement && k != c->end (); k++) { + const int other = *k; + if (is_autarky_literal (other)) + replacement = other; + } + watched_autarky_literal = replacement; + + if (replacement) { + LOG (c, "watching autarky %d instead %d in candidate", + replacement, watched_autarky_literal); + watched_autarky_literal = replacement; + } else { + LOG ("failed to find an autarky replacement"); + watched_autarky_literal = 0; // Breaks out of 4 loops!!!!! + } + } // End of loop of turning autarky literals into conditionals. + } // End of loop of all watched clauses of an unassigned literal. + // + // We might abort the occurrence traversal early but already + // removed some watches, thus have to just copy the rest. + // + if (i < j) { + while (j != os.end ()) + *i++ = *j++; + LOG ("flushed %zd occurrences of %d", os.end () - i, + unassigned_lit); + os.resize (i - os.begin ()); + } + } // End of loop which goes over all unprocessed unassigned literals. + } // End of loop which goes over all unprocessed conditional literals. + + // We are still processing the candidate 'c' and now have reached a + // final fix-point assignment partitioned into a conditional and an + // autarky part, or during unassigned literals figured that there is no + // positive autarky literal left in 'c'. + + LOG ("remaining assignment of size %zd", remain.assigned); + LOG ("remaining conditional part of size %zd", remain.conditional); + LOG ("remaining autarky part of size %zd", remain.autarky); + // + assert (remain.assigned - remain.conditional == remain.autarky); + // +#if defined(LOGGING) || !defined(NDEBUG) + // + // This is a sanity check, that the size of our implicit representation + // of the autarky part matches our 'remain' counts. We need the same + // code for determining autarky literals as in the loop below which adds + // autarky literals to the extension stack. + // + struct { + size_t assigned, conditional, autarky; + } check; + check.assigned = check.conditional = check.autarky = 0; + for (size_t i = 0; i < trail.size (); i++) { + const int lit = trail[i]; + if (val (lit)) { + check.assigned++; + if (is_conditional_literal (lit)) { + LOG ("remaining conditional %d", lit); + assert (!is_autarky_literal (lit)); + check.conditional++; + } else { + assert (is_autarky_literal (lit)); + LOG ("remaining autarky %d", lit); + check.autarky++; + } + } else { + assert (!is_autarky_literal (lit)); + assert (!is_conditional_literal (lit)); + } + } + assert (remain.assigned == check.assigned); + assert (remain.conditional == check.conditional); + assert (remain.autarky == check.autarky); +#endif + + // Success if an autarky literal is left in the clause and + // we did not abort the loop too early because the propagation + // limit was hit. + // + if (watched_autarky_literal && stats.condprops < limit) { + assert (is_autarky_literal (watched_autarky_literal)); + assert (is_in_candidate_clause (watched_autarky_literal)); + + blocked++; + stats.conditioned++; + LOG (c, "positive autarky literal %d globally blocks", + watched_autarky_literal); + + LOG ("remaining %zd assigned literals %.0f%%", remain.assigned, + percent (remain.assigned, initial.assigned)); + LOG ("remaining %zd conditional literals %.0f%%", remain.conditional, + percent (remain.conditional, remain.assigned)); + LOG ("remaining %zd autarky literals %.0f%%", remain.autarky, + percent (remain.autarky, remain.assigned)); + + // A satisfying assignment of a formula after removing a globally + // blocked clause might not satisfy that clause. As for variable + // elimination and classical blocked clauses, we thus maintain an + // extension stack for reconstructing an assignment which both + // satisfies the remaining formula as well as the clause. + // + // For globally blocked clauses we simply have to flip all literals in + // the autarky part and thus save the autarky on the extension stack + // in addition to the removed clause. In the classical situation (in + // bounded variable elimination etc.) we simply save one literal on + // the extension stack. + // + // TODO find a way to shrink the autarky part or some other way to + // avoid pushing too many literals on the extension stack. + // + external->push_zero_on_extension_stack (); + for (const auto &lit : trail) + if (is_autarky_literal (lit)) + external->push_witness_literal_on_extension_stack (lit); + if (proof) + proof->weaken_minus (c); + external->push_clause_on_extension_stack (c); + + mark_garbage (c); + + stats.condassrem += remain.assigned; + stats.condcondrem += remain.conditional; + stats.condautrem += remain.autarky; + stats.condassirem += initial.assigned; + } + + // In this last part specific to one candidate clause, we have to get + // back to the initial assignment and reset conditionals. First we + // assign all the unassigned literals (if necessary). + // + if (!unassigned.empty ()) { + LOG ("reassigning %zd literals", unassigned.size ()); + while (!unassigned.empty ()) { + const int lit = unassigned.back (); + unassigned.pop_back (); + condition_assign (lit); + } + } + + // Then we remove from the conditional stack autarky literals which + // became conditional and also reset their 'conditional' bit. + // + if (initial.conditional < conditional.size ()) { + LOG ("flushing %zd autarky literals from conditional stack", + conditional.size () - initial.conditional); + while (initial.conditional < conditional.size ()) { + const int lit = conditional.back (); + conditional.pop_back (); + unmark_as_conditional_literal (lit); + } + } + + // Finally unmark all literals in the candidate clause. + // + for (const_literal_iterator l = c->begin (); l != c->end (); l++) + unmark_in_candidate_clause (*l); + + } // End of loop over all candidate clauses. + + PHASE ("condition", stats.conditionings, + "globally blocked %ld clauses %.0f%%", blocked, + percent (blocked, candidates.size ())); + + // Unmark initial conditional variables. + // + for (const auto &lit : conditional) + unmark_as_conditional_literal (lit); + + erase_vector (unassigned); + erase_vector (conditional); + erase_vector (candidates); + + // Unassign additionally assigned literals. + // +#if defined(LOGGING) || !defined(NDEBUG) + int additionally_unassigned = 0; +#endif + while (trail.size () > initial_trail_level) { + int lit = trail.back (); + trail.pop_back (); + condition_unassign (lit); +#if defined(LOGGING) || !defined(NDEBUG) + additionally_unassigned++; +#endif + } + LOG ("unassigned %d additionally assigned literals", + additionally_unassigned); + assert (additionally_unassigned == additionally_assigned); + + if (level > initial_level) { + LOG ("reset condition decision level"); + level = initial_level; + } + + reset_occs (); + delete_garbage_clauses (); + + // Reassign previously assigned variables again. + // + LOG ("reassigning previously assigned variables"); + for (size_t i = 0; i < initial_trail_level; i++) { + const int lit = trail[i]; + const signed char tmp = val (lit); + assert (tmp >= 0); + if (!tmp) + condition_assign (lit); + } + +#ifndef NDEBUG + for (const auto &lit : trail) + assert (!marked (lit)); +#endif + + unprotect_reasons (); + + return blocked; +} + +void Internal::condition (bool update_limits) { + + if (unsat) + return; + if (!stats.current.irredundant) + return; + + START_SIMPLIFIER (condition, CONDITION); + stats.conditionings++; + + // Propagation limit to avoid too much work in 'condition'. We mark + // tried candidate clauses after giving up, such that next time we run + // 'condition' we can try them. + // + long limit = stats.propagations.search; + limit *= opts.conditioneffort; + limit /= 1000; + if (limit < opts.conditionmineff) + limit = opts.conditionmineff; + if (limit > opts.conditionmaxeff) + limit = opts.conditionmaxeff; + assert (stats.current.irredundant); + limit *= 2.0 * active () / (double) stats.current.irredundant; + limit = max (limit, 2l * active ()); + + PHASE ("condition", stats.conditionings, + "started after %" PRIu64 " conflicts limited by %ld propagations", + stats.conflicts, limit); + + long blocked = condition_round (limit); + + STOP_SIMPLIFIER (condition, CONDITION); + report ('g', !blocked); + + if (!update_limits) + return; + + long delta = opts.conditionint * (stats.conditionings + 1); + lim.condition = stats.conflicts + delta; + + PHASE ("condition", stats.conditionings, + "next limit at %" PRIu64 " after %ld conflicts", lim.condition, + delta); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/config.cpp b/src/sat/cadical/config.cpp new file mode 100644 index 000000000..f63d1e4b7 --- /dev/null +++ b/src/sat/cadical/config.cpp @@ -0,0 +1,101 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +struct NameVal { + const char *name; + int val; +}; + +/*------------------------------------------------------------------------*/ + +// These are dummy configurations, which require additional code. + +static NameVal default_config[1]; // With '-pedantic' just '[]' or +static NameVal plain_config[1]; // '[0]' gave a warning. + +/*------------------------------------------------------------------------*/ + +// Here we have the pre-defined default configurations. + +static NameVal sat_config[] = { + {"elimeffort", 10}, + {"stabilizeonly", 1}, + {"subsumeeffort", 60}, +}; + +static NameVal unsat_config[] = { + {"stabilize", 0}, + {"walk", 0}, +}; + +/*------------------------------------------------------------------------*/ + +#define CONFIGS \ +\ + CONFIG (default, "set default advanced internal options") \ + CONFIG (plain, "disable all internal preprocessing options") \ + CONFIG (sat, "set internal options to target satisfiable instances") \ + CONFIG (unsat, "set internal options to target unsatisfiable instances") + +static const char *configs[] = { +#define CONFIG(N, D) #N, + CONFIGS +#undef CONFIG +}; + +static size_t num_configs = sizeof configs / sizeof *configs; + +/*------------------------------------------------------------------------*/ + +bool Config::has (const char *name) { +#define CONFIG(N, D) \ + if (!strcmp (name, #N)) \ + return true; + CONFIGS +#undef CONFIG + return false; +} + +bool Config::set (Options &opts, const char *name) { + if (!strcmp (name, "default")) { + opts.reset_default_values (); + return true; + } + if (!strcmp (name, "plain")) { + opts.disable_preprocessing (); + return true; + } +#define CONFIG(N, D) \ + do { \ + if (strcmp (name, #N)) \ + break; \ + const NameVal *BEGIN = N##_config; \ + const NameVal *END = BEGIN + sizeof N##_config / sizeof (NameVal); \ + for (const NameVal *P = BEGIN; P != END; P++) { \ + assert (Options::has (P->name)); \ + opts.set (P->name, P->val); \ + } \ + return true; \ + } while (0); + CONFIGS +#undef CONFIG + return false; +} + +/*------------------------------------------------------------------------*/ + +void Config::usage () { +#define CONFIG(N, D) printf (" %-14s " D "\n", "--" #N); + CONFIGS +#undef CONFIG +} + +/*------------------------------------------------------------------------*/ + +const char **Config::begin () { return configs; } +const char **Config::end () { return &configs[num_configs]; } + +} // namespace CaDiCaL diff --git a/src/sat/cadical/config.hpp b/src/sat/cadical/config.hpp new file mode 100644 index 000000000..5510ce0eb --- /dev/null +++ b/src/sat/cadical/config.hpp @@ -0,0 +1,20 @@ +#ifndef _config_hpp_INCLUDED +#define _config_hpp_INCLUDED + +namespace CaDiCaL { + +class Options; + +struct Config { + + static bool has (const char *); + static bool set (Options &, const char *); + static void usage (); + + static const char **begin (); + static const char **end (); +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/congruence.cpp b/src/sat/cadical/congruence.cpp new file mode 100644 index 000000000..4c8de9d52 --- /dev/null +++ b/src/sat/cadical/congruence.cpp @@ -0,0 +1,7555 @@ +#include "congruence.hpp" +#include "internal.hpp" +#include +#include +#include +#include +#include + +namespace CaDiCaL { + +Closure::Closure (Internal *i) + : internal (i), table (128, Hash (nonces)) +#ifdef LOGGING + , + fresh_id (internal->clause_id) +#endif +{ +} + +char &Closure::lazy_propagated (int lit) { + return lazy_propagated_idx[internal->vidx (lit)]; +} + +void update_ite_flags (Gate *g) { + int8_t f = g->degenerated_ite; + const int lhs = g->lhs; + const int cond = g->rhs [0]; + const int then_lit = g->rhs[1]; + const int else_lit = g->rhs[2]; + + if (lhs == cond) { + f |= Special_ITE_GATE::NO_NEG_THEN; + f |= Special_ITE_GATE::NO_PLUS_ELSE; + } + if (lhs == -cond) { + f |= Special_ITE_GATE::NO_PLUS_THEN; + f |= Special_ITE_GATE::NO_NEG_ELSE; + } + if (lhs == then_lit) { + f |= Special_ITE_GATE::NO_PLUS_THEN; + f |= Special_ITE_GATE::NO_NEG_THEN; + } + if (lhs == else_lit) { + f |= Special_ITE_GATE::NO_PLUS_ELSE; + f |= Special_ITE_GATE::NO_NEG_ELSE; + } + g->degenerated_ite = f; + assert (lhs != -then_lit); + assert (lhs != -else_lit); + assert (cond != then_lit); + assert (cond != else_lit); + assert (cond != -then_lit); + assert (cond != -else_lit); +} + +void check_correct_ite_flags (const Gate *const g) { +#ifndef NDEBUG + const int8_t f = g->degenerated_ite; + const int lhs = g->lhs; + const int cond = g->rhs [0]; + const int then_lit = g->rhs[1]; + const int else_lit = g->rhs[2]; + assert (g->pos_lhs_ids.size () == 4); + if (g->pos_lhs_ids[0].clause == nullptr) + assert ((f & Special_ITE_GATE::NO_PLUS_THEN)); + if (g->pos_lhs_ids[1].clause == nullptr) + assert (f & Special_ITE_GATE::NO_NEG_THEN); + if (g->pos_lhs_ids[2].clause == nullptr) + assert (f & Special_ITE_GATE::NO_PLUS_ELSE); + if (g->pos_lhs_ids[3].clause == nullptr) + assert (f & Special_ITE_GATE::NO_NEG_ELSE); + if (lhs == cond) { + assert (f & Special_ITE_GATE::NO_NEG_THEN); + assert (f & Special_ITE_GATE::NO_PLUS_ELSE); + } + if (lhs == -cond) { + assert (f & Special_ITE_GATE::NO_PLUS_THEN); + assert (f & Special_ITE_GATE::NO_NEG_ELSE); + } + if (lhs == then_lit) { + assert (f & Special_ITE_GATE::NO_PLUS_THEN); + assert (f & Special_ITE_GATE::NO_NEG_THEN); + } + if (lhs == else_lit) { + assert (f & Special_ITE_GATE::NO_PLUS_ELSE); + assert (f & Special_ITE_GATE::NO_NEG_ELSE); + } + assert (lhs != -then_lit); + assert (lhs != -else_lit); + assert (cond != then_lit); + assert (cond != else_lit); + assert (cond != -then_lit); + assert (cond != -else_lit); +#else + (void)g; +#endif +} + +/*------------------------------------------------------------------------*/ + +static size_t hash_lits (std::array &nonces, + const vector &lits) { + size_t hash = 0; + const auto end_nonces = end (nonces); + const auto begin_nonces = begin (nonces); + auto n = begin_nonces; + for (auto lit : lits) { + hash += lit; + hash *= *n++; + hash = (hash << 4) | (hash >> 60); + if (n == end_nonces) + n = begin_nonces; + } + hash ^= hash >> 32; + return hash; +} + +size_t Hash::operator() (const Gate *const g) const { + assert (hash_lits (nonces, g->rhs) == g->hash); + return g->hash; +} + +bool gate_contains (Gate *g, int lit) { + return find (begin (g->rhs), end (g->rhs), lit) != end (g->rhs); +} +/*------------------------------------------------------------------------*/ +struct compact_binary_rank { + typedef uint64_t Type; + uint64_t operator() (const CompactBinary &a) { + return ((uint64_t) a.lit1 << 32) + a.lit2; + }; +}; + +struct compact_binary_order { + bool operator() (const CompactBinary &a, const CompactBinary &b) { + return compact_binary_rank () (a) < compact_binary_rank () (b); + }; +}; + +bool Closure::find_binary (int lit, int other) const { + const auto offsize = + offsetsize[internal->vlit (lit)]; // in C++17: [offset, size] = + const auto offset = offsize.first; + const auto size = offsize.second; + const auto begin = std::begin (binaries) + offset; + const auto end = std::begin (binaries) + size; + assert (end <= std::end (binaries)); + const CompactBinary target = CompactBinary (nullptr, 0, lit, other); + auto it = std::lower_bound (begin, end, target, compact_binary_order ()); + // search_binary only returns a bool + bool found = (it != end && it->lit1 == lit && it->lit2 == other); + if (found) { + LOG ("found binary [%zd] %d %d", it->id, lit, other); + if (internal->lrat) + lrat_chain.push_back (it->id); + } + return found; +} + +void Closure::extract_binaries () { + if (!internal->opts.congruencebinaries) + return; + START (extractbinaries); + offsetsize.resize (internal->max_var * 2 + 3, make_pair (0, 0)); + + // in kissat this is done during watch clearing. TODO: consider doing this + // too. + for (Clause *c : internal->clauses) { + if (c->garbage) + continue; + if (c->redundant && c->size != 2) + continue; + if (c->size > 2) + continue; + assert (c->size == 2); + const int lit = c->literals[0]; + const int other = c->literals[1]; + const bool already_sorted = + internal->vlit (lit) < internal->vlit (other); + binaries.push_back (CompactBinary (c, c->id, + already_sorted ? lit : other, + already_sorted ? other : lit)); + } + + MSORT (internal->opts.radixsortlim, begin (binaries), end (binaries), + compact_binary_rank (), compact_binary_order ()); + + { + const size_t size = binaries.size (); + size_t i = 0; + while (i < size) { + CompactBinary bin = binaries[i]; + const int lit = bin.lit1; + size_t j = i; + while (j < size && binaries[j].lit1 == lit) { + ++j; + } + assert (j >= i); + assert (j <= size); + offsetsize[internal->vlit (lit)] = make_pair (i, j); + i = j; + } + } + + size_t extracted = 0, already_present = 0, duplicated = 0; + + const size_t size = internal->clauses.size (); + for (size_t i = 0; i < size; ++i) { + Clause *d = internal->clauses[i]; // binary clauses are appended, so + // reallocation possible + if (d->garbage) + continue; + if (d->redundant) + continue; + if (d->size != 3) + continue; + const int *lits = d->literals; + const int a = lits[0]; + const int b = lits[1]; + const int c = lits[2]; // obfuscating d->literals[2] which triggers an error in pedandic mode + if (internal->val (a)) + continue; + if (internal->val (b)) + continue; + if (internal->val (c)) + continue; + int l = 0, k = 0; + if (find_binary (-a, b) || find_binary (-a, c)) { + l = b, k = c; + } else if (find_binary (-b, a) || find_binary (-b, c)) { + l = a, k = c; + } else if (find_binary (-c, a) || find_binary (-c, b)) { + l = a, k = b; + } else + continue; + LOG (d, "strengthening"); + if (!find_binary (l, k)) { + if (internal->lrat) + lrat_chain.push_back (d->id); + add_binary_clause (l, k); + ++extracted; + } else { + ++already_present; + if (internal->lrat) + lrat_chain.clear (); + } + } + lrat_chain.clear (); + + offsetsize.clear (); + + // kissat has code to remove duplicates, which we have already removed + // before starting congruence + MSORT (internal->opts.radixsortlim, begin (binaries), end (binaries), + compact_binary_rank (), compact_binary_order ()); + const size_t new_size = binaries.size (); + { + size_t i = 0; + for (size_t j = 1; j < new_size; ++j) { + assert (i < j); + if (binaries[i].lit1 == binaries[j].lit1 && + binaries[i].lit2 == binaries[j].lit2) { + // subsuming later clause + subsume_clause (binaries[i].clause, + binaries[j].clause); // the local one is specialized + ++duplicated; + } else { + binaries[++i] = binaries[j]; + } + } + assert (i <= new_size); + binaries.resize (i); + } + binaries.clear (); + STOP (extractbinaries); + LOG ("extracted %zu binaries (plus %zu already present and %zu " + "duplicates)", + extracted, already_present, duplicated); +} + +/*------------------------------------------------------------------------*/ +// marking structure for congruence closure, by reference +signed char &Closure::marked (int lit) { + assert (internal->vlit (lit) < marks.size ()); + return marks[internal->vlit (lit)]; +} + +void Closure::unmark_all () { + for (auto lit : internal->analyzed) { + assert (marked (lit)); + marked (lit) = 0; + } + internal->analyzed.clear (); +} + +void Closure::set_mu1_reason (int lit, Clause *c) { + assert (marked (lit) & 1); + LOG (c, "mu1 %d -> %zd", lit, c->id); + mu1_ids[internal->vlit (lit)] = LitClausePair (lit, c); +} + +void Closure::set_mu2_reason (int lit, Clause *c) { + assert (marked (lit) & 2); + if (!internal->lrat) + return; + LOG (c, "mu2 %d -> %zd", lit, c->id); + mu2_ids[internal->vlit (lit)] = LitClausePair (lit, c); +} + +void Closure::set_mu4_reason (int lit, Clause *c) { + assert (marked (lit) & 4); + if (!internal->lrat) + return; + LOG (c, "mu4 %d -> %zd", lit, c->id); + mu4_ids[internal->vlit (lit)] = LitClausePair (lit, c); +} + +LitClausePair Closure::marked_mu1 (int lit) { + return mu1_ids[internal->vlit (lit)]; +} + +LitClausePair Closure::marked_mu2 (int lit) { + return mu2_ids[internal->vlit (lit)]; +} + +LitClausePair Closure::marked_mu4 (int lit) { + return mu4_ids[internal->vlit (lit)]; +} + +struct sort_literals_by_var_rank { + CaDiCaL::Internal *internal; + sort_literals_by_var_rank (Internal *i) : internal (i) {} + + typedef uint64_t Type; + + Type operator() (const int &a) const { return internal->vlit (a); } +}; +struct sort_literals_by_var_rank_except { + CaDiCaL::Internal *internal; + int lhs; + int except; + sort_literals_by_var_rank_except (Internal *i, int my_lhs, int except2) + : internal (i), lhs (my_lhs), except (except2) {} + sort_literals_by_var_rank_except (Internal *i, int my_lhs) + : internal (i), lhs (my_lhs), except (0) {} + typedef uint64_t Type; + Type operator() (const int &a) const { + Type res = 0; + if (abs (a) == abs (except)) + res = 1 - (a > 0); + else if (abs (a) == abs (lhs)) + res = 3 - (a > 0); + else + res = internal->vlit (a) + 2; // probably +2 enough + return ~res; + } +}; + +struct sort_literals_by_var_smaller_except { + CaDiCaL::Internal *internal; + int lhs; + int except; + sort_literals_by_var_smaller_except (Internal *i, int my_lhs, int except2) + : internal (i), lhs (my_lhs), except (except2) {} + sort_literals_by_var_smaller_except (Internal *i, int my_lhs) + : internal (i), lhs (my_lhs), except (0) {} + bool operator() (const int &a, const int &b) const { + return sort_literals_by_var_rank_except (internal, lhs, except) (a) < + sort_literals_by_var_rank_except (internal, lhs, except) (b); + if (abs (a) == abs (except) && abs (b) != abs (except)) + return false; + if (abs (a) != abs (except) && abs (b) == abs (except)) + return true; + if (abs (a) == abs (lhs) && abs (b) != abs (lhs)) + return false; + if (abs (a) != abs (lhs) && abs (b) == abs (lhs)) + return true; + return sort_literals_by_var_rank (internal) (a) > + sort_literals_by_var_rank (internal) (b); + } +}; +struct sort_literals_by_var_smaller { + CaDiCaL::Internal *internal; + sort_literals_by_var_smaller (Internal *i) : internal (i) {} + bool operator() (const int &a, const int &b) const { + return sort_literals_by_var_rank (internal) (a) < + sort_literals_by_var_rank (internal) (b); + } +}; + +void Closure::sort_literals_by_var_except (vector &rhs, int lhs, + int except2) { + MSORT (internal->opts.radixsortlim, begin (rhs), end (rhs), + sort_literals_by_var_rank_except (internal, lhs, except2), + sort_literals_by_var_smaller_except (internal, lhs, except2)); +} +void Closure::sort_literals_by_var (vector &rhs) { + MSORT (internal->opts.radixsortlim, begin (rhs), end (rhs), + sort_literals_by_var_rank (internal), + sort_literals_by_var_smaller (internal)); +} +/*------------------------------------------------------------------------*/ +int &Closure::representative (int lit) { + assert (internal->vlit (lit) < representant.size ()); + return representant[internal->vlit (lit)]; +} +int Closure::representative (int lit) const { + assert (internal->vlit (lit) < representant.size ()); + return representant[internal->vlit (lit)]; +} + +int &Closure::eager_representative (int lit) { + assert (internal->vlit (lit) < eager_representant.size ()); + return eager_representant[internal->vlit (lit)]; +} + +int Closure::eager_representative (int lit) const { + assert (internal->vlit (lit) < eager_representant.size ()); + return eager_representant[internal->vlit (lit)]; +} + +int Closure::find_lrat_representative_with_marks (int lit) { + int res = lit; + int nxt = lit; + do { + res = nxt; + nxt = representative (nxt); + if (nxt != res) { + LOG ("%d has reason %" PRIu64, res, representative_id (res)); + lrat_chain.push_back (representative_id (res)); + } + } while (nxt != res || marked (nxt) || marked (-nxt)); + + return nxt; +} +int Closure::find_representative (int lit) { + int res = lit; + int nxt = lit; + do { + res = nxt; + nxt = representative (nxt); + } while (nxt != res); + + return res; +} + +int Closure::find_representative_and_compress (int lit, bool update_eager) { + LOG ("finding representative of %d", lit); + int res = lit; + int nxt = lit; + int path_length = 0; + do { + res = nxt; + nxt = representative (nxt); + ++path_length; + LOG ("updating %d -> %d", res, nxt); + } while (nxt != res); + + if (path_length > 2) { + LOG ("learning new rewriting from %d to %d (current path length: %d)", + lit, res, path_length); + if (update_eager) + eager_representative (lit) = res; + if (internal->lrat) { + produce_representative_lrat (lit); + Clause *equiv = add_tmp_binary_clause (-lit, res); + + if (equiv) { + representative_id (lit) = equiv->id; + if (update_eager) + eager_representative_id (lit) = equiv->id; + } + } + if (internal->lrat) + lrat_chain.clear (); + } else if (path_length == 2) { + if (update_eager) { + LOG ("updating information %d -> %d in eager", lit, res); + eager_representative (lit) = res; + if (internal->lrat) + eager_representative_id (lit) = representative_id (lit); + assert (!internal->lrat || eager_representative_id (lit)); + } + } + + if (lit != res) { + representative (lit) = res; + } + LOG ("representative of %d is %d", lit, res); + return res; +} + +void Closure::push_lrat_unit (int lit) { + if (!internal->lrat) + return; + assert (internal->val (lit) > 0); + LRAT_ID id = internal->unit_id (lit); + lrat_chain.push_back (id); +} + +int Closure::find_eager_representative (int lit) { + int res = lit; + int nxt = lit; + do { + res = nxt; + nxt = eager_representative (nxt); + } while (nxt != res); + + return res; +} + +int Closure::find_eager_representative_and_compress (int lit) { + if (!internal->lrat) + return find_representative (lit); + int res = lit; + int nxt = lit; + int path_length = 0; + do { + res = nxt; + nxt = eager_representative (nxt); + ++path_length; + } while (nxt != res); + + // assert (res == find_representative (lit)); + // we have to do path compression to support LRAT proofs + if (path_length > 2) { + LOG ("learning new rewriting from %d to %d (current path length: %d)", + lit, res, path_length); + std::vector tmp_lrat_chain; + if (internal->lrat) { + tmp_lrat_chain = std::move (lrat_chain); + lrat_chain.clear (); + produce_eager_representative_lrat (lit); + } + eager_representative (lit) = res; + Clause *equiv = add_tmp_binary_clause (-lit, res); + equiv->hyper = true; + + if (internal->lrat && equiv) { + eager_representative_id (lit) = equiv->id; + } + if (internal->lrat) { + lrat_chain = std::move (tmp_lrat_chain); + } + } else if (path_length == 2) { + LOG ("duplicated information %d -> %d to eager with clause %" PRIu64, + lit, res, eager_representative_id (lit)); + assert (eager_representative (lit) == res); + assert (!internal->lrat || eager_representative_id (lit)); + } + assert (internal->clause.empty ()); + return res; +} + +void Closure::find_representative_and_compress_both (int lit) { + find_representative_and_compress (lit, false); + find_representative_and_compress (-lit, false); +} + +void Closure::import_lazy_and_find_eager_representative_and_compress_both ( + int lit) { + find_representative_and_compress (lit); + find_eager_representative_and_compress (lit); + find_representative_and_compress (-lit); + find_eager_representative_and_compress (-lit); +} + +void Closure::produce_representative_lrat (int lit) { + assert (internal->lrat); + LOG ("production of LRAT chain for %d with representative %" PRIu64, lit, + representative_id (lit)); + assert (internal->lrat); + assert (lrat_chain.empty ()); + int res = lit; + int nxt = lit; + assert (nxt != representative (nxt)); + do { + res = nxt; + nxt = representative (nxt); + if (nxt != res) { + LOG ("%d has reason %" PRIu64, res, representative_id (res)); + lrat_chain.push_back (representative_id (res)); + } + } while (nxt != res); +} + +void Closure::produce_eager_representative_lrat (int lit) { + assert (internal->lrat); + LOG ("production of LRAT chain for %d with representative %" PRIu64, lit, + eager_representative_id (lit)); + assert (internal->lrat); + assert (lrat_chain.empty ()); + int res = lit; + int nxt = lit; + assert (nxt != eager_representative (nxt)); + do { + res = nxt; + nxt = eager_representative (nxt); + if (nxt != res) { + LOG ("%d has reason %" PRIu64, res, eager_representative_id (res)); + lrat_chain.push_back (eager_representative_id (res)); + } + } while (nxt != res); +} + +LRAT_ID Closure::find_representative_lrat (int lit) { + if (!internal->lrat) + return 0; + int res = lit; +#ifndef NDEBUG + int nxt = representative (res); + assert (nxt == representative (res)); +#endif + LOG ("checking for existing LRAT chain for %d with clause %" PRIu64, lit, + eager_representative_id (res)); + assert (representative_id (res)); + return representative_id (res); +} + +LRAT_ID Closure::find_eager_representative_lrat (int lit) { + if (!internal->lrat) + return 0; + int res = lit; +#ifndef NDEBUG + int nxt = eager_representative (res); + assert (nxt == eager_representative (res)); +#endif + LOG ("checking for existing LRAT chain for %d with clause %" PRIu64, lit, + eager_representative_id (res)); + assert (eager_representative_id (res)); + return eager_representative_id (res); +} + +LRAT_ID &Closure::eager_representative_id (int lit) { + assert (internal->vlit (lit) < eager_representant_id.size ()); + return eager_representant_id[internal->vlit (lit)]; +} +LRAT_ID Closure::eager_representative_id (int lit) const { + assert (internal->vlit (lit) < eager_representant_id.size ()); + return eager_representant_id[internal->vlit (lit)]; +} + +LRAT_ID &Closure::representative_id (int lit) { + assert (internal->vlit (lit) < representant_id.size ()); + return representant_id[internal->vlit (lit)]; +} +LRAT_ID Closure::representative_id (int lit) const { + assert (internal->vlit (lit) < representant_id.size ()); + return representant_id[internal->vlit (lit)]; +} + +void Closure::mark_garbage (Gate *g) { + LOG (g, "marking as garbage"); + assert (!g->garbage); + g->garbage = true; + garbage.push_back (g); +} + +bool Closure::remove_gate (GatesTable::iterator git) { + assert (git != end (table)); + assert (!internal->unsat); + (*git)->indexed = false; + LOG ((*git), "removing from hash table"); + table.erase (git); + return true; +} + +bool Closure::remove_gate (Gate *g) { + if (!g->indexed) + return false; + assert (!internal->unsat); + assert (table.find (g) != end (table)); + table.erase (table.find (g)); + g->indexed = false; + LOG (g, "removing from hash table"); + return true; +} + +void Closure::index_gate (Gate *g) { + assert (!g->indexed); + assert (!internal->unsat); + assert (g->arity () > 1); + assert (g->hash == hash_lits (nonces, g->rhs)); + LOG (g, "adding to hash table"); + table.insert (g); + g->indexed = true; +} + +void Closure::produce_rewritten_clause_lrat_and_clean ( + std::vector &litIds, int except_lhs, bool remove_units) { + assert (internal->lrat_chain.empty ()); + for (auto &litId : litIds) { + assert (litId.clause); + litId.clause = produce_rewritten_clause_lrat (litId.clause, except_lhs, + remove_units); + litId.current_lit = find_eager_representative (litId.current_lit); + } + litIds.erase ( + std::remove_if (begin (litIds), end (litIds), + [] (const LitClausePair &p) { return !p.clause; }), + end (litIds)); +} + +void Closure::produce_rewritten_clause_lrat_and_clean ( + std::vector &litIds, int except_lhs, + size_t &old_position1, size_t &old_position2, bool remove_units) { + assert (internal->lrat_chain.empty ()); + assert (old_position1 != old_position2); + size_t j = 0; + for (size_t i = 0; i < litIds.size (); ++i) { + assert (j <= i); + litIds[j].clause = produce_rewritten_clause_lrat ( + litIds[i].clause, except_lhs, remove_units); + litIds[j].current_lit = + find_eager_representative (litIds[i].current_lit); + if (i == old_position1) { + if (litIds[j].clause) + old_position1 = j; + else + old_position1 = -1; + } + if (i == old_position2) { + if (litIds[j].clause) + old_position2 = j; + else + old_position2 = -1; + } + if (litIds[j].clause) + ++j; + } + litIds.resize (j); +} + +void Closure::compute_rewritten_clause_lrat_simple (Clause *c, int except) { + assert (internal->clause.empty ()); + assert (internal->lrat_chain.empty ()); + assert (clause.empty ()); + assert (lrat_chain.empty ()); + bool changed = false; + bool tautology = false; + for (auto lit : *c) { + LOG ("checking if %d is required", lit); + if (internal->marked2 (lit)) { + continue; + } + if (internal->marked2 (-lit)) { + tautology = true; + break; + } + if (lit == except || lit == -except) { + internal->mark2 (lit); + clause.push_back (lit); + continue; + } + if (internal->val (lit) < 0) { +#if 1 + LOG ("found unit %d, removing it", -lit); + LRAT_ID id = internal->unit_id (-lit); + lrat_chain.push_back (id); + changed = true; + continue; +#else + LOG ("found unit %d, but ignoring it", -lit); +#endif + } + if (internal->val (lit) > 0) { + LOG ("found positive unit, so clause is subsumed by unit"); + tautology = true; + } + const int other = find_eager_representative_and_compress (lit); + const bool marked = internal->marked2 (other); + const bool neg_marked = internal->marked2 (-other); + if (!marked) + internal->mark2 (other); + if (neg_marked) { + tautology = true; + LOG ("tautology due to %d -> %d", lit, other); + } else if (lit == other && marked) { + changed = true; + LOG ("%d -> %d already in", lit, other); + } else if (lit != other) { + if (!marked) + clause.push_back (other); + changed = true; + lrat_chain.push_back (eager_representative_id (lit)); + } else if (!marked) + clause.push_back (lit); + } + + for (auto lit : *c) { + internal->unmark (lit); + } + + for (auto lit : clause) { + internal->unmark (lit); + } + + lrat_chain.push_back (c->id); + if (tautology) { + LOG ("generated clause is a tautology"); + lrat_chain.clear (); + clause.clear (); + } else if (changed && clause.size () == 1) { + LOG (lrat_chain, "LRAT chain"); + } else { + LOG (c, "oops this should not happen"); + assert (false); + } +} + +Clause *Closure::new_tmp_clause (std::vector &clause) { + assert (internal->lrat); + assert (!clause.empty ()); + assert (!lrat_chain.empty ()); + bool clear = false; + + LOG (clause, "learn new tmp clause"); + assert (clause.size () >= 2); + internal->external->check_learned_clause (); + + assert (internal->clause.size () <= (size_t) INT_MAX); + const int size = (int) clause.size (); + assert (size >= 2); + + size_t bytes = Clause::bytes (size); + Clause *c = (Clause *) new char[bytes]; + DeferDeleteArray clause_delete ((char *) c); + + c->id = ++internal->clause_id; + + c->conditioned = false; + c->covered = false; + c->enqueued = false; + c->frozen = false; + c->garbage = false; + c->gate = false; + c->hyper = false; + c->instantiated = false; + c->moved = false; + c->reason = false; + c->redundant = false; + c->transred = false; + c->subsume = false; + c->swept = false; + c->flushed = false; + c->vivified = false; + c->vivify = false; + c->used = 0; + + c->glue = size; + c->size = size; + c->pos = 2; + + for (int i = 0; i < size; i++) + c->literals[i] = clause[i]; + + // Just checking that we did not mess up our sophisticated memory layout. + // This might be compiler dependent though. Crucial for correctness. + // + assert (c->bytes () == bytes); + + clause_delete.release (); + LOG (c, "new pointer %p", (void *) c); + + if (clear) + clause.clear (); + + if (internal->proof) { + internal->proof->add_derived_clause (c, lrat_chain); + } + extra_clauses.push_back (c); + assert (internal->lrat_chain.empty ()); + return c; +} + +Clause *Closure::new_clause () { + assert (internal->clause.empty () || clause.empty ()); + bool clear = false; + if (internal->clause.empty ()) { + swap (internal->clause, clause); + clear = true; + } + + Clause *c = internal->new_clause (false); + + if (clear) + internal->clause.clear (); + + if (internal->proof) { + internal->proof->add_derived_clause (c, lrat_chain); + } + + return c; +} + +// TODO we here duplicate the arguments of push_id_and_rewriting_lrat but we +// probably do not need that. +void Closure::produce_rewritten_clause_lrat ( + std::vector &litIds, int except_lhs, bool remove_units) { + assert (internal->lrat_chain.empty ()); + for (auto &litId : litIds) { + if (!litId.clause) + continue; + litId.clause = produce_rewritten_clause_lrat (litId.clause, except_lhs, + remove_units); + litId.current_lit = find_eager_representative (litId.current_lit); + } +} + +Clause *Closure::produce_rewritten_clause_lrat (Clause *c, int except_lhs, + bool remove_units, bool fail_on_unit) { + assert (internal->clause.empty ()); + assert (internal->lrat_chain.empty ()); + auto tmp_lrat (std::move (lrat_chain)); + lrat_chain.clear (); + LOG (c, "rewriting clause for LRAT proof, except for rewriting %d", + except_lhs); + assert (internal->clause.empty ()); + assert (lrat_chain.empty ()); + bool changed = false; + bool tautology = false; + for (auto lit : *c) { + LOG ("checking if %d is required", lit); + if (internal->marked2 (lit)) { + continue; + } + if (internal->marked2 (-lit)) { + tautology = true; + break; + } + if (lit == except_lhs || lit == -except_lhs) { + internal->mark2 (lit); + clause.push_back (lit); + continue; + } + if (internal->val (lit) < 0) { + if (remove_units || lazy_propagated (lit)) { + LOG ("found unit %d, removing it", -lit); + LRAT_ID id = internal->unit_id (-lit); + lrat_chain.push_back (id); + changed = true; + continue; + } else + LOG ("found unit %d, but ignoring it", -lit); + } + if (internal->val (lit) > 0) { + LOG ("found positive unit %d, so clause is subsumed by unit", lit); + if (remove_units || lazy_propagated (lit)) + tautology = true; + } + const int other = find_eager_representative_and_compress (lit); + const bool marked = internal->marked2 (other); + const bool neg_marked = internal->marked2 (-other); + if (!marked) + internal->mark2 (other); + if (neg_marked) { + tautology = true; + LOG ("tautology due to %d -> %d", lit, other); + } else if (lit == other && marked) { + changed = true; + LOG ("%d -> %d already in", lit, other); + } else if (lit != other) { + if (!marked) + clause.push_back (other); + changed = true; + lrat_chain.push_back (eager_representative_id (lit)); + } else if (!marked) + clause.push_back (lit); + } + + for (auto lit : *c) { + internal->unmark (lit); + } + + for (auto lit : clause) { + internal->unmark (lit); + } + + lrat_chain.push_back (c->id); + Clause *d; + assert (internal->clause.empty ()); + if (tautology) { + LOG ("generated clause is a tautology"); + d = nullptr; + lrat_chain.clear (); + } else if (changed && clause.size () == 1) { + LOG (lrat_chain, "LRAT chain"); + if (fail_on_unit) { + d = nullptr; + assert (false && "rewriting produced a unit clause"); + } else { + d = c; + } + } else if (changed) { + LOG (lrat_chain, "LRAT Chain"); + d = new_tmp_clause (clause); + LOG (d, "rewritten clause to"); + } else { + LOG (c, "clause is unchanged, so giving up"); + lrat_chain.clear (); + d = c; + } + clause.clear (); + lrat_chain = std::move (tmp_lrat); + assert (internal->clause.empty ()); + return d; +} + +void Closure::push_id_on_chain (std::vector &chain, + Rewrite rewrite, int lit) { + LOG ("adding reason %zd for rewriting %d marked", + lit == rewrite.src ? rewrite.id1 : rewrite.id2, lit); + chain.push_back (lit == rewrite.src ? rewrite.id1 : rewrite.id2); +} + +void Closure::push_id_and_rewriting_lrat_unit (Clause *c, Rewrite rewrite1, + std::vector &chain, + bool insert_id_after, + Rewrite rewrite2, + int except_lhs, + int except_lhs2) { + LOG (c, + "computing normalized LRAT chain for clause to produce unit, " + "rewriting except for %d (%" PRIu64 ", %" PRIu64 ") and %d (%" PRIu64 + ", %" PRIu64 ") and skipping %d and %d", + rewrite1.src, rewrite1.id1, rewrite1.id2, rewrite2.src, rewrite2.id1, + rewrite2.id2, except_lhs, except_lhs2); + assert (c); + std::vector units, rewriting; + for (auto other : *c) { + // unclear how to achieve this in the simplify context where other == + // g->lhs might be set assert (internal->val (other) <= 0 || other == + // except); + if (other == except_lhs || other == -except_lhs) { + // do nothing; + } else if (other == except_lhs2 || other == -except_lhs2) { + // do nothing; + } else if (internal->val (other) < 0) { + LOG ("found unit %d", -other); + LRAT_ID id = internal->unit_id (-other); + units.push_back (id); + } else if (other == rewrite1.src && rewrite1.id1) { + push_id_on_chain (rewriting, rewrite1, other); + } else if (other == -rewrite1.src && rewrite1.id2) { + push_id_on_chain (rewriting, rewrite1, other); + } else if (other == rewrite2.src && rewrite2.id1) { + push_id_on_chain (rewriting, rewrite2, other); + } else if (other == -rewrite2.src && rewrite2.id2) { + push_id_on_chain (rewriting, rewrite2, other); + } else if (other != find_eager_representative_and_compress (other)) { +#if defined(LOGGING) || !defined(NDEBUG) + const int rewritten_other = eager_representative (other); + assert (other != rewritten_other); + LOG ("reason for representative of %d %d is %" PRIu64 "", other, + rewritten_other, find_eager_representative_lrat (other)); +#endif + rewriting.push_back (find_eager_representative_lrat (other)); + } else { + LOG ("no rewriting needed for %d", other); + } + } + for (auto id : units) + chain.push_back (id); + + if (!insert_id_after) + chain.push_back (c->id); + for (auto id : rewriting) + chain.push_back (id); + + if (insert_id_after) + chain.push_back (c->id); +} + +// Note: it is important that the Rewrite takes over the normal rewriting, +// because we can force rewriting that way that have not been done eagerly +// yet. +void Closure::push_id_and_rewriting_lrat_full (Clause *c, Rewrite rewrite1, + std::vector &chain, + bool insert_id_after, + Rewrite rewrite2, + int except_lhs, + int except_lhs2) { + LOG (c, + "computing normalized LRAT chain for clause, rewriting except for " + "%d (%" PRIu64 ", %" PRIu64 ") and %d (%" PRIu64 ", %" PRIu64 + ") and skipping %d and %d", + rewrite1.src, rewrite1.id1, rewrite1.id2, rewrite2.src, rewrite2.id1, + rewrite2.id2, except_lhs, except_lhs2); + assert (c); + if (!insert_id_after) + chain.push_back (c->id); + for (auto other : *c) { + // unclear how to achieve this in the simplify context where other == + // g->lhs might be set assert (internal->val (other) <= 0 || other == + // except); + if (other == except_lhs) { + // do nothing; + } else if (other == except_lhs2) { + // do nothing; + } else if (internal->val (other) < 0) { + LOG ("found unit %d", -other); + LRAT_ID id = internal->unit_id (-other); + assert (id); + chain.push_back (id); + } else if (other == rewrite1.src && rewrite1.id1) { + push_id_on_chain (chain, rewrite1, other); + } else if (other == -rewrite1.src && rewrite1.id2) { + push_id_on_chain (chain, rewrite1, other); + } else if (other == rewrite2.src && rewrite2.id1) { + push_id_on_chain (chain, rewrite2, other); + } else if (other == -rewrite2.src && rewrite2.id2) { + push_id_on_chain (chain, rewrite2, other); + } else { + assert (other == find_eager_representative (other)); + LOG ("no rewriting needed for %d", other); + } + } + if (insert_id_after) + chain.push_back (c->id); +} + +// Note: it is important that the Rewrite takes over the normal rewriting, +// because we can force rewriting that way that have not been done eagerly +// yet. +void Closure::push_id_on_chain (std::vector &chain, Clause *c) { + assert (c); + chain.push_back (c->id); + LOG (lrat_chain, "chain"); +} + +void Closure::push_id_on_chain (std::vector &chain, + const std::vector &reasons) { + for (auto litId : reasons) { + LOG (litId.clause, "found lrat in gate %d from %zd", litId.current_lit, + litId.clause->id); + push_id_on_chain (chain, litId.clause); + } + LOG (lrat_chain, "chain from %zd reasons", reasons.size ()); +} + +void Closure::learn_congruence_unit_when_lhs_set (Gate *g, int src, + LRAT_ID id1, LRAT_ID id2, + int dst) { + if (!internal->lrat) + return; + LOG ("calculating LRAT chain learn_congruence_unit_when_lhs_set"); + assert (!g->pos_lhs_ids.empty ()); + assert (internal->analyzed.empty ()); + assert (internal->val (g->lhs) < 0); + switch (g->tag) { + case Gate_Type::And_Gate: + LOG (lrat_chain, "lrat"); + LOG (lrat_chain, "lrat"); + for (auto litId : g->neg_lhs_ids) + push_id_and_rewriting_lrat_unit ( + litId.clause, Rewrite (src, dst, id1, id2), lrat_chain); + LOG (lrat_chain, "lrat"); + break; + default: + assert (false); + } +} + +// Something very important here: as we are producing a unit, we cannot +// simplify or rewrite the clauses as this will produce units. +void Closure::learn_congruence_unit_falsifies_lrat_chain ( + Gate *g, int src, int dst, int clashing, int falsified, int unit) { + if (!internal->lrat) + return; + assert (!g->pos_lhs_ids.empty ()); + assert (internal->analyzed.empty ()); + assert (lrat_chain.empty ()); + std::vector proof_chain; + switch (g->tag) { + case Gate_Type::And_Gate: + if (clashing) { + LOG ("clashing %d where -lhs=%d", clashing, -g->lhs); + // Example: -2 = 1&3 and 3=2 + // The proof consists in taking the binary clause of the clashing + // literal + if (clashing == -g->lhs) { + for (auto litId : g->pos_lhs_ids) { + LOG (litId.clause, + "found lrat in gate %d from %zd (looking for %d)", + litId.current_lit, litId.clause->id, falsified); + if (litId.current_lit == clashing) { + push_id_and_rewriting_lrat_unit ( + litId.clause, Rewrite (), proof_chain, true, Rewrite (), + g->degenerated_and_neg || g->degenerated_and_pos ? 0 : -g->lhs); + } + } + } else { + // Example: 3 = (-1&2) and 2=1 + // The proof consists in taking the binary clause with the rewrites + // Example where the rewrite must be before: + // 2: 3v2 + // 9: -2v1 + // 6: 3v1 + // The chain cannot start by 9 + if (g->degenerated_and_neg || g->degenerated_and_pos) { + LOG ("%d %d %d", src, dst, g->lhs); + if (src == g->lhs || dst == g->lhs) { + LOG ("degenerated AND gate with dst=lhs"); + for (const auto &litId : g->pos_lhs_ids) { + LOG (litId.clause, "definition clause %d ->", + litId.current_lit); + if (litId.current_lit == clashing) { + push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), + proof_chain, true, + Rewrite (), 0); + LOG (proof_chain, "produced lrat chain so far"); + } + } + assert (!proof_chain.empty ()); + } else { + LOG ("degenerated AND gate with conflict without LHS"); + for (const auto &litId : g->pos_lhs_ids) { + LOG (litId.clause, "definition clause %d ->", + litId.current_lit); + push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), + proof_chain, false, + Rewrite (), -g->lhs); + LOG (proof_chain, "produced lrat chain so far"); + } + } + } else { + LOG ("normal AND gate"); + for (const auto &litId : g->pos_lhs_ids) { + push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), + proof_chain, false, Rewrite (), + g->degenerated_and_neg || g->degenerated_and_pos ? 0 : -g->lhs); + LOG (proof_chain, "produced lrat chain so far"); + } + } + } + LOG (proof_chain, "produced lrat chain"); + } else if (falsified) { + LOG ("falsifies %d", falsified); + // Example is 3=(1&2) with 2=false or 3=(1&4) with 4=2 and 2=false + // (can happen when the unit was derived in the middle of the + // rewriting) + for (auto litId : g->pos_lhs_ids) { + LOG (litId.clause, + "found lrat in gate %d from %zd (looking for %d)", + litId.current_lit, litId.clause->id, falsified); + if (litId.current_lit == falsified || + (litId.current_lit == src && dst == falsified)) { + push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), + proof_chain, true, Rewrite (), + -dst, -g->lhs); + } + } + } else { + assert (unit); + // Example is 1 = 2&3 where 2 and 3 are false + for (auto litId : g->neg_lhs_ids) { + push_id_and_rewriting_lrat_unit (litId.clause, Rewrite (), + proof_chain); + } + LOG (proof_chain, "produced lrat chain"); + break; + } + lrat_chain = std::move (proof_chain); + break; + default: + assert (false); + } + (void) unit; +} + +bool Closure::fully_propagate () { + if (internal->unsat) + return false; + LOG ("fully propagating"); + assert (internal->watching ()); + assert (full_watching); + bool no_conflict = internal->propagate (); + + if (no_conflict) + return true; + internal->learn_empty_clause (); + if (internal->lrat) + lrat_chain.clear (); + + return false; +} +bool Closure::learn_congruence_unit (int lit, bool delay_propagation, bool force_propagation) { + if (internal->unsat) + return false; + const signed char val_lit = internal->val (lit); + if (val_lit > 0) { + LOG ("already set lit %d", lit); + if (internal->lrat) + lrat_chain.clear (); + if (force_propagation) + return fully_propagate(); + return true; + } + LOG ("adding unit %s", LOGLIT (lit)); + ++internal->stats.congruence.units; + assert (!internal->lrat || !lrat_chain.empty ()); + if (val_lit < 0) { + if (internal->lrat) { + assert (internal->lrat_chain.empty ()); + LRAT_ID id = internal->unit_id (-lit); + internal->lrat_chain.push_back (id); + for (auto id : lrat_chain) + internal->lrat_chain.push_back (id); + lrat_chain.clear (); + } + internal->learn_empty_clause (); + return false; + } + + LOG (lrat_chain, "assigning due to LRAT chain"); + swap (lrat_chain, internal->lrat_chain); + internal->assign_unit (lit); + assert (lrat_chain.empty ()); + assert (internal->lrat_chain.empty ()); + if (delay_propagation) + return false; + else return fully_propagate (); +} + +// for merging the literals there are many cases +// TODO: LRAT does not work if the LHS is not in normal form and if the +// representative is also in the gate. +bool Closure::merge_literals_lrat ( + Gate *g, Gate *h, int lit, int other, + const std::vector &extra_reasons_lit, + const std::vector &extra_reasons_ulit) { + assert (!internal->unsat); + assert (g->lhs == lit); + assert (g == h || h->lhs == other); + (void) g, (void) h; + LOG ("merging literals %s and %s", LOGLIT(lit), LOGLIT(other)); + // TODO: this should not update_eager but still calculate the LRAT chain + // below! + const int repr_lit = find_representative_and_compress (lit, false); + const int repr_other = find_representative_and_compress (other, false); + find_representative_and_compress (-lit, false); + find_representative_and_compress (-other, false); + LOG ("merging literals %d [=%d] and %d [=%d]", lit, repr_lit, other, + repr_other); + + if (repr_lit == repr_other) { + LOG ("already merged %d and %d", lit, other); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + + const int val_lit = internal->val (lit); + const int val_other = internal->val (other); + if (val_lit) { + if (val_lit == val_other) { + LOG ("not merging lits %d and %d assigned to same value", lit, other); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + } + + // For LRAT we need to distinguish more cases for a more regular + // reconstruction. + // + // 1. if lit = -other, then we learn lit and -lit to derive false + // + // 2. otherwise, we learn the new clauses lit = -other (which are two real + // clauses). + // + // 2a. if repr_lit = -repr_other, we learn the units repr_lit and + // -repr_lit to derive false + // + // 2b. otherwise, we learn the equivalences repr_lit = -repr_other + // (which are two real clauses) + // + // Without LRAT this is easier, as we directly learn the conclusion + // (either false or the equivalence). The steps can also not be merged + // because repr_lit can appear in the gate and hence in the resolution + // chain. + int smaller_repr = repr_lit; + int larger_repr = repr_other; + int smaller = lit; + int larger = other; + const std::vector *smaller_chain = &extra_reasons_ulit; + const std::vector *larger_chain = &extra_reasons_lit; + + if (abs (smaller_repr) > abs (larger_repr)) { + swap (smaller_repr, larger_repr); + swap (smaller, larger); + swap (smaller_chain, larger_chain); + } + + assert (find_representative (smaller_repr) == smaller_repr); + assert (find_representative (larger_repr) == larger_repr); + if (lit == -other) { + assert (chain.empty ()); + LOG ("merging clashing %d and %d", lit, other); + if (internal->proof) { + if (internal->lrat) { + for (auto id : *smaller_chain) + lrat_chain.push_back (id); + } + unsimplified.push_back (smaller); + LRAT_ID id = simplify_and_add_to_proof_chain (unsimplified); + + if (internal->lrat) { + internal->lrat_chain.push_back (id); + for (auto id : *larger_chain) + internal->lrat_chain.push_back (id); + LOG (internal->lrat_chain, "lrat chain"); + } + } + internal->learn_empty_clause (); + delete_proof_chain (); + return false; + } + + LOG ("merging %d and %d first and then the equivalences of %d and %d " + "with LRAT", + lit, other, repr_lit, repr_other); + Clause *eq1_tmp = nullptr; + if (internal->lrat) { + lrat_chain = *smaller_chain; + eq1_tmp = add_tmp_binary_clause (-larger, smaller); + } + assert (!internal->lrat || eq1_tmp); + + Clause *eq2_tmp = nullptr; + if (internal->lrat) { + lrat_chain = *larger_chain; + LOG (lrat_chain, "lrat chain"); + + eq2_tmp = add_tmp_binary_clause (larger, -smaller); + // the order in the clause is important for the + // repr_lit == -repr_other to get the right chain + } + assert (!internal->lrat || eq2_tmp); + if (internal->lrat) + lrat_chain.clear (); + + if (repr_lit == -repr_other) { + // now derive empty clause + Rewrite rew1, rew2; + if (internal->lrat) { + // no need to calculate push_id_and_rewriting_lrat here because all + // the job is done by the arguments already + rew1 = Rewrite (lit == repr_lit ? 0 : lit, repr_lit, + lit == repr_lit ? 0 : representative_id (lit), + lit == repr_lit ? 0 : representative_id (-lit)); + rew2 = Rewrite (other == repr_other ? 0 : other, repr_other, + other == repr_other ? 0 : representative_id (other), + other == repr_other ? 0 : representative_id (-other)); + push_id_and_rewriting_lrat_unit (eq1_tmp, rew1, lrat_chain, true, + rew2); + swap (lrat_chain, internal->lrat_chain); + } + internal->assign_unit (-larger_repr); + if (internal->lrat) { + internal->lrat_chain.clear (); + + if (larger != larger_repr) + push_lrat_unit (-larger_repr); + push_id_and_rewriting_lrat_unit ( + eq2_tmp, rew1, lrat_chain, true, rew2, + larger != larger_repr ? larger_repr : 0); + LOG (lrat_chain, "lrat chain"); + swap (lrat_chain, internal->lrat_chain); + } + internal->learn_empty_clause (); + if (internal->lrat) + internal->lrat_chain.clear (); + return false; + } + + if (val_lit) { + if (val_lit == val_other) { + LOG ("not merging lits %d and %d assigned to same value", lit, other); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + if (val_lit == -val_other) { + LOG ("merging lits %d and %d assigned to inconsistent value", lit, + other); + 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; + } + + assert (!val_other); + LOG ("merging assigned %d and unassigned %d", lit, other); + 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); + assert (lrat_chain.empty ()); + const int unit = (val_other < 0) ? -lit : lit; + if (internal->lrat) { + Clause *c; + if (lit == smaller) { + if (val_lit < 0) + c = eq1_tmp; + else + c = eq2_tmp; + } else { + if (val_lit < 0) + c = eq2_tmp; + else + c = eq1_tmp; + } + push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, + Rewrite (), lit, unit); + } + learn_congruence_unit (unit); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + + Clause *eq1_repr, *eq2_repr; + if (smaller_repr != smaller || larger != larger_repr) { + if (internal->lrat) { + lrat_chain.clear (); + Rewrite rew1 = Rewrite ( + smaller_repr != smaller ? smaller : 0, + smaller_repr != smaller ? smaller_repr : 0, + smaller_repr != smaller ? representative_id (smaller) : 0, + smaller_repr != smaller ? representative_id (-smaller) : 0); + Rewrite rew2 = + Rewrite (larger_repr != larger ? larger : 0, + larger_repr != larger ? larger_repr : 0, + larger_repr != larger ? representative_id (larger) : 0, + larger_repr != larger ? representative_id (-larger) : 0); + push_id_and_rewriting_lrat_full (eq1_tmp, rew1, lrat_chain, true, + rew2); + } + eq1_repr = learn_binary_tmp_or_full_clause (-larger_repr, smaller_repr); + } else { + if (internal->lrat) + eq1_repr = maybe_promote_tmp_binary_clause (eq1_tmp); + else + eq1_repr = maybe_add_binary_clause (-larger_repr, smaller_repr); + } + + if (internal->lrat) { + lrat_chain.clear (); + } + + if (smaller_repr != smaller || larger != larger_repr) { + + if (internal->lrat) { + lrat_chain.clear (); + // eq2 = larger, -smaller + Rewrite rew1 = Rewrite ( + smaller_repr != smaller ? smaller : 0, + smaller_repr != smaller ? smaller_repr : 0, + smaller_repr != smaller ? representative_id (smaller) : 0, + smaller_repr != smaller ? representative_id (-smaller) : 0); + Rewrite rew2 = + Rewrite (larger_repr != larger ? larger : 0, + larger_repr != larger ? larger_repr : 0, + larger_repr != larger ? representative_id (larger) : 0, + larger_repr != larger ? representative_id (-larger) : 0); + push_id_and_rewriting_lrat_full (eq2_tmp, rew1, lrat_chain, true, + rew2); + } + + eq2_repr = learn_binary_tmp_or_full_clause (larger_repr, -smaller_repr); + + } else { + if (internal->lrat) + eq2_repr = maybe_promote_tmp_binary_clause (eq2_tmp); + else + eq2_repr = maybe_add_binary_clause (larger_repr, -smaller_repr); + } + lrat_chain.clear (); + + if (internal->lrat) { + representative_id (larger_repr) = eq1_repr->id; + assert (std::find (begin (*eq1_repr), end (*eq1_repr), -larger_repr) != + end (*eq1_repr)); + representative_id (-larger_repr) = eq2_repr->id; + 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; + assert (lrat_chain.empty ()); + return true; +} + +// Variant when there are no gates +// TODO: this is exactly the same as the other function without the gates. +// Kill the arguments! +bool Closure::merge_literals_lrat ( + int lit, int other, const std::vector &extra_reasons_lit, + const std::vector &extra_reasons_ulit) { + assert (!internal->unsat); + LOG ("merging literals %s and %s", LOGLIT (lit), LOGLIT (other)); + // TODO: this should not update_eager but still calculate the LRAT chain + // below! + const int repr_lit = find_representative_and_compress (lit, false); + const int repr_other = find_representative_and_compress (other, false); + find_representative_and_compress (-lit, false); + find_representative_and_compress (-other, false); + LOG ("merging literals %s [=%d] and %s [=%d]", LOGLIT (lit), repr_lit, + LOGLIT (other), repr_other); + LOG (lrat_chain, "lrat chain beginning of merge"); + + if (repr_lit == repr_other) { + LOG ("already merged %s and %s", LOGLIT (lit), LOGLIT (other)); + if (internal->lrat) + lrat_chain.clear (); + return false; + } + + const int val_lit = internal->val (lit); + const int val_other = internal->val (other); + + // For LRAT we need to distinguish more cases for a more regular + // reconstruction. + // + // 1. if lit = -other, then we learn lit and -lit to derive false + // + // 2. otherwise, we learn the new clauses lit = -other (which are two real + // clauses). + // + // 2a. if repr_lit = -repr_other, we learn the units repr_lit and + // -repr_lit to derive false + // + // 2b. otherwise, we learn the equivalences repr_lit = -repr_other + // (which are two real clauses) + // + // Without LRAT this is easier, as we directly learn the conclusion + // (either false or the equivalence). The steps can also not be merged + // because repr_lit can appear in the gate and hence in the resolution + // chain. + int smaller_repr = repr_lit; + int larger_repr = repr_other; + int val_smaller = val_lit; + int val_larger = val_other; + int smaller = lit; + int larger = other; + const std::vector *smaller_chain = &extra_reasons_ulit; + const std::vector *larger_chain = &extra_reasons_lit; + + if (abs (smaller_repr) > abs (larger_repr)) { + swap (smaller_repr, larger_repr); + swap (smaller, larger); + swap (smaller_chain, larger_chain); + swap (val_smaller, val_larger); + } + + assert (find_representative (smaller_repr) == smaller_repr); + 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) { + 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); + assert (eq1_tmp); + } + 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); + assert (eq2_tmp); + } + + 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); + } + 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); + 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; + } + + assert (!val_other); + LOG ("merging assigned %d and unassigned %d", lit, other); + 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); + assert (lrat_chain.empty ()); + const int unit = (val_other < 0) ? -lit : lit; + if (internal->lrat) { + Clause *c; + if (lit == smaller) { + 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; + 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); + 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; + assert (lrat_chain.empty ()); + return true; +} + +inline void Closure::promote_clause (Clause *c) { + if (internal->lrat) + check_not_tmp_binary_clause (c); + if (!c) + return; + if (!c->redundant) + return; + LOG (c, "turning redundant subsuming clause into irredundant clause"); + c->redundant = false; + if (internal->proof) + internal->proof->strengthen (c->id); + internal->stats.current.irredundant++; + internal->stats.added.irredundant++; + internal->stats.irrlits += c->size; + assert (internal->stats.current.redundant > 0); + internal->stats.current.redundant--; + assert (internal->stats.added.redundant > 0); + internal->stats.added.redundant--; + // ... and keep 'stats.added.total'. +} + +// This function is rather tricky for LRAT. If you have 2 = 1 and 3=4 you +// cannot add 2=3. You really to connect the representatives directly +// therefore you actually need to learn the clauses 2->3->4 and -2->1 and +// vice-versa +bool Closure::merge_literals_equivalence (int lit, int other, Clause *c1, + Clause *c2) { + assert (!internal->unsat); + LRAT_ID id1 = c1 ? c1->id : 0; + LRAT_ID id2 = c2 ? c2->id : 0; + if (internal->lrat) { + assert (c1); + assert (c2); + assert (c1->size == 2); + assert (c2->size == 2); + assert (c1->literals[0] == lit || c1->literals[1] == lit); + assert (c2->literals[0] == other || c2->literals[1] == other); + assert (c1->literals[0] == -other || c1->literals[1] == -other); + assert (c2->literals[0] == -lit || c2->literals[1] == -lit); + check_not_tmp_binary_clause (c1); + check_not_tmp_binary_clause (c2); + } + int repr_lit = find_representative (lit); + int repr_other = find_representative (other); + find_representative_and_compress_both (lit); + find_representative_and_compress_both (other); + LOG ("merging literals %d [=%d] and %d [=%d] lrat", lit, repr_lit, other, + repr_other); + + if (repr_lit == repr_other) { + LOG ("already merged %d and %d", lit, other); + return false; + } + const int val_lit = internal->val (lit); + const int val_other = internal->val (other); + + if (val_lit) { + if (val_lit == val_other) { + LOG ("not merging lits %d and %d assigned to same value", lit, other); + return false; + } + if (val_lit == -val_other) { + if (internal->lrat) + lrat_chain.push_back (internal->unit_id (lit)), + lrat_chain.push_back (internal->unit_id (other)); + LOG ("merging lits %d and %d assigned to inconsistent value", lit, + other); + internal->learn_empty_clause (); + return false; + } + + assert (!val_other); + LOG ("merging assigned %d and unassigned %d", lit, other); + const int unit = (val_lit < 0) ? -other : other; + if (internal->lrat) + lrat_chain.push_back (internal->unit_id (unit)); + if (val_lit < 0) + lrat_chain.push_back (c2->id); + else + lrat_chain.push_back (c1->id); + learn_congruence_unit (unit); + return false; + } + + if (!val_lit && val_other) { + LOG ("merging assigned %d and unassigned %d", other, lit); + const int unit = (val_other < 0) ? -lit : lit; + if (internal->lrat) + lrat_chain.push_back ( + internal->unit_id (val_other < 0 ? -other : other)); + if (val_lit < 0) + lrat_chain.push_back (c1->id); + else + lrat_chain.push_back (c2->id); + learn_congruence_unit (unit); + return false; + } + + int smaller_repr = repr_lit; + int larger_repr = repr_other; + int smaller = lit; + int larger = other; + + if (abs (smaller_repr) > abs (larger_repr)) { + swap (smaller_repr, larger_repr); + swap (smaller, larger); + } + + assert (find_representative (smaller_repr) == smaller_repr); + assert (find_representative (larger_repr) == larger_repr); + + if (repr_lit == -repr_other) { + LOG ("merging clashing %d [=%d] and %d[=%d], smaller: %d", lit, + repr_lit, other, repr_other, smaller); + if (internal->lrat) { + Rewrite rew1 = + Rewrite (lit, lit == repr_lit ? 0 : repr_lit, + lit == repr_lit ? 0 : find_representative_lrat (lit), + lit == repr_lit ? 0 : find_representative_lrat (-lit)); + Rewrite rew2 = Rewrite ( + other, other == repr_other ? 0 : repr_other, + other == repr_other ? 0 : find_representative_lrat (other), + other == repr_other ? 0 : find_representative_lrat (-other)); + push_id_and_rewriting_lrat_unit (c1, rew1, lrat_chain, true, rew2); + LOG (lrat_chain, "lrat chain"); + swap (internal->lrat_chain, lrat_chain); + } + internal->assign_unit (repr_lit); + if (internal->lrat) { + lrat_chain.clear (); + LRAT_ID id = internal->unit_id (repr_lit); + assert (id); + lrat_chain.push_back (id); + if (lit != repr_lit) { + const LRAT_ID repr_id2 = find_representative_lrat (-lit); + lrat_chain.push_back (repr_id2); + } + lrat_chain.push_back (id2); + if (other != repr_other) { + const LRAT_ID repr_larger_id2 = find_representative_lrat (other); + lrat_chain.push_back (repr_larger_id2); + } + LOG (lrat_chain, "lrat chain"); + swap (internal->lrat_chain, lrat_chain); + } + internal->learn_empty_clause (); + return false; + } + + LOG ("merging %d [=%d] and %d [=%d]", lit, repr_lit, other, repr_other); + promote_clause (c1), promote_clause (c2); + bool learn_clause = (lit != repr_lit) || (other != repr_other); + if (learn_clause) { + if (internal->lrat) { + if (lit != repr_lit) { + LOG ("adding chain for lit %d -> %d", lit, repr_lit); + lrat_chain.push_back (find_representative_lrat (lit)); + } + if (other != repr_other) { + LOG ("adding chain for lit %d -> %d", -other, -repr_other); + lrat_chain.push_back (find_representative_lrat (-other)); + } + lrat_chain.push_back (id1); + } + Clause *eq1 = learn_binary_tmp_or_full_clause (repr_lit, -repr_other); + + if (internal->lrat) { + lrat_chain.clear (); + if (lit != repr_lit) + lrat_chain.push_back (find_representative_lrat (-lit)); + if (other != repr_other) + lrat_chain.push_back (find_representative_lrat (other)); + lrat_chain.push_back (id2); + } + Clause *eq2 = learn_binary_tmp_or_full_clause (-repr_lit, repr_other); + if (internal->lrat) { + lrat_chain.clear (); + if (smaller_repr == repr_lit) { + assert (larger_repr == repr_other); + representative_id (-larger_repr) = eq2->id; + assert (std::find (eq2->begin (), eq2->end (), larger_repr) != + eq2->end ()); + representative_id (larger_repr) = eq1->id; + assert (std::find (eq1->begin (), eq1->end (), -larger_repr) != + eq1->end ()); + } else { + assert (larger_repr == repr_lit); + representative_id (-larger_repr) = eq1->id; + assert (std::find (eq1->begin (), eq1->end (), larger_repr) != + eq1->end ()); + representative_id (larger_repr) = eq2->id; + assert (std::find (eq2->begin (), eq2->end (), -larger_repr) != + eq2->end ()); + } + } + + } else if (internal->lrat) { + LOG ("not learning new clause, using already existing one"); + if (smaller_repr == repr_lit) { + LOG ("setting ids of %d: %" PRIu64 "; %d: %" PRIu64 " (case 1)", + larger, id1, -larger, id2); + representative_id (-larger_repr) = id2; + representative_id (larger_repr) = id1; + } else { + LOG ("setting ids of %d: %" PRIu64 "; %d: %" PRIu64 " (case 2)", + larger, id2, -larger, id1); + representative_id (-larger_repr) = id1; + representative_id (larger_repr) = id2; + } + } + + LOG ("updating %d -> %d", larger_repr, smaller_repr); + representative (larger_repr) = smaller_repr; + representative (-larger_repr) = -smaller_repr; + schedule_literal (larger_repr); + ++internal->stats.congruence.congruent; + return true; +} + +/*------------------------------------------------------------------------*/ +GOccs &Closure::goccs (int lit) { return gtab[internal->vlit (lit)]; } + +void Closure::connect_goccs (Gate *g, int lit) { + LOG (g, "connect %d to", lit); + // incorrect for ITE + // assert (std::find(begin (goccs (lit)), end (goccs (lit)), g) == + // std::end (goccs (lit))); + goccs (lit).push_back (g); +} + +uint64_t &Closure::largecount (int lit) { + assert (internal->vlit (lit) < glargecounts.size ()); + return glargecounts[internal->vlit (lit)]; +} + +/*------------------------------------------------------------------------*/ +// Initialization + +void Closure::init_closure () { + representant.resize (2 * internal->max_var + 3); + eager_representant.resize (2 * internal->max_var + 3); + if (internal->lrat) { + eager_representant_id.resize (2 * internal->max_var + 3); + representant_id.resize (2 * internal->max_var + 3); + lazy_propagated_idx.resize (internal->max_var + 1, false); + for (auto lit : internal->trail) + lazy_propagated (lit) = true; + } + marks.resize (2 * internal->max_var + 3); + mu1_ids.resize (2 * internal->max_var + 3); + if (internal->lrat) { + mu2_ids.resize (2 * internal->max_var + 3); + mu4_ids.resize (2 * internal->max_var + 3); + } +#ifndef NDEBUG + for (auto &it : mu1_ids) + it.current_lit = 0, it.clause = nullptr; + for (auto &it : mu2_ids) + it.current_lit = 0, it.clause = nullptr; + for (auto &it : mu4_ids) + it.current_lit = 0, it.clause = nullptr; +#endif + scheduled.resize (internal->max_var + 1); + gtab.resize (2 * internal->max_var + 3); + for (auto v : internal->vars) { + representative (v) = v; + representative (-v) = -v; + eager_representative (v) = v; + eager_representative (-v) = -v; + } + units = internal->propagated; + Random rand (internal->stats.congruence.rounds); + for (auto &n : nonces) { + n = 1 | rand.next (); + } +#ifdef LOGGING + fresh_id = internal->clause_id; +#endif + internal->init_noccs (); + internal->init_occs (); +} + +void Closure::init_and_gate_extraction () { + LOG ("[gate-extraction]"); + for (Clause *c : internal->clauses) { + if (c->garbage) + continue; + if (c->redundant && c->size != 2) + continue; + if (c->size > 2) + continue; + assert (c->size == 2); + const int lit = c->literals[0]; + const int other = c->literals[1]; + internal->noccs (lit)++; + internal->noccs (other)++; + internal->watch_clause (c); + } +} + +/*------------------------------------------------------------------------*/ +void Closure::check_and_gate_implied (Gate *g) { + assert (internal->clause.empty ()); + assert (g->tag == Gate_Type::And_Gate); + if (internal->lrat) + return; + LOG (g, "checking implied"); + const int lhs = g->lhs; + const int not_lhs = -lhs; + for (auto other : g->rhs) + check_binary_implied (not_lhs, other); + internal->clause = g->rhs; + check_implied (); + internal->clause.clear (); +} + +void Closure::delete_proof_chain () { + if (!internal->proof) { + assert (chain.empty ()); + return; + } + if (chain.empty ()) + return; + LOG ("starting deletion of proof chain"); + auto &clause = internal->clause; + assert (clause.empty ()); + uint32_t id1 = UINT32_MAX, id2 = UINT32_MAX; + LRAT_ID id = 0; + + LOG (chain, "chain:"); + for (auto lit : chain) { + LOG ("reading %d from chain", lit); + if (id1 == UINT32_MAX) { + id1 = lit; + id = (LRAT_ID) id1; + continue; + } + if (id2 == UINT32_MAX) { + id2 = lit; + id = ((LRAT_ID) id1 << 32) + id2; + continue; + } + if (lit) { // parsing the id first + LOG ("found %d as literal in chain", lit); + clause.push_back (lit); + } else { + assert (id); + internal->proof->delete_clause (id, false, clause); + clause.clear (); + id = 0, id1 = UINT32_MAX, id2 = UINT32_MAX; + } + } + assert (clause.empty ()); + chain.clear (); + LOG ("finished deletion of proof chain"); +} + +/*------------------------------------------------------------------------*/ +// Simplification +bool Closure::skip_and_gate (Gate *g) { + assert (g->tag == Gate_Type::And_Gate); + if (g->garbage) + return true; + const int lhs = g->lhs; + if (internal->val (lhs) > 0) { + mark_garbage (g); + return true; + } + + assert (g->arity () > 1); + return false; +} + +bool Closure::skip_xor_gate (Gate *g) { + assert (g->tag == Gate_Type::XOr_Gate); + if (g->garbage) + return true; + assert (g->arity () > 1); + return false; +} + +void Closure::shrink_and_gate (Gate *g, int falsifies, int clashing) { + if (falsifies) { + g->rhs.resize (1); + g->rhs[0] = falsifies; + g->hash = hash_lits (nonces, g->rhs); + } else if (clashing) { + LOG (g, "gate before clashing on %d", clashing); + g->rhs.resize (2); + g->rhs[0] = clashing; + g->rhs[1] = -clashing; + g->hash = hash_lits (nonces, g->rhs); + LOG (g, "gate after clashing on %d", clashing); + } + g->shrunken = true; +} + +void Closure::update_and_gate_unit_build_lrat_chain ( + Gate *g, int src, LRAT_ID id1, LRAT_ID id2, int dst, + std::vector &extra_reasons_lit, + std::vector &extra_reasons_ulit) { + LOG ("generate chain for gate boiling down to unit"); + if (g->neg_lhs_ids.size () != 1) { + + if (g->degenerated_and_neg || g->degenerated_and_pos) { + // can happen for 4 = AND 3 4 (degenerated with only the clause -4 3) + // with a rewriting 4 -> 1 (unchanged clause) + // and later 1 -> 3 (unchanged clause) + // but you do not know anymore from the gate that it is degenerated + assert (g->pos_lhs_ids.size () == 1); + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[0].clause, Rewrite (), + extra_reasons_ulit, true, Rewrite ()); + return; + } + assert (g->lhs == g->rhs[0] || (g->lhs == src && g->rhs[0] == dst)); + assert (g->pos_lhs_ids.size () <= 1); // either degenerated or empty A = A + return; + } + assert (g->neg_lhs_ids.size () == 1); + assert (!g->pos_lhs_ids.empty ()); + + const int repr_lit = find_representative (g->lhs); + const int repr_other = find_representative (g->rhs[0]); + if (repr_lit == repr_other) { + LOG ("skipping already merged"); + return; + } + + push_id_and_rewriting_lrat_unit (g->neg_lhs_ids[0].clause, Rewrite (), + extra_reasons_ulit, true, Rewrite (), + g->lhs, -dst); + LOG (extra_reasons_ulit, "lrat chain for negative side"); + + lrat_chain.clear (); + + assert (!g->pos_lhs_ids.empty ()); + for (auto litId : g->pos_lhs_ids) + push_id_and_rewriting_lrat_unit ( + litId.clause, Rewrite (src, dst, id1, id2), extra_reasons_lit, true, + Rewrite (), -g->lhs, dst); + LOG (extra_reasons_lit, "lrat chain for positive side"); +} + +void Closure::update_and_gate_build_lrat_chain ( + Gate *g, Gate *h, std::vector &extra_reasons_lit, + std::vector &extra_reasons_ulit, bool remove_units) { + assert (g != h); + LOG (g, "merging"); + LOG (h, "with"); + // If the LHS are identical, do not even attempt to build the LRAT chain + if (find_representative (g->lhs) == find_representative (h->lhs)) + return; + // set to same value, don't do anything + if (internal->val (g->lhs) && internal->val (g->lhs) == internal->val (h->lhs)) + return; + const bool g_tautology = gate_contains (g, g->lhs); + const bool h_tautology = gate_contains (h, h->lhs); + if (g_tautology && h_tautology) { + LOG ("both gates are a tautology"); + // special case: actually we have an equivalence due to binary clauses + // and all gate clauses (except one binary) are actually tautologies + for (auto &litId : g->pos_lhs_ids) { + if (litId.current_lit == h->lhs) { + assert (extra_reasons_lit.empty ()); + LOG (litId.clause, "binary clause to push into the reason"); + litId.clause = produce_rewritten_clause_lrat (litId.clause, g->lhs, + remove_units); + assert (litId.clause); + extra_reasons_lit.push_back (litId.clause->id); + } + } + assert (!extra_reasons_lit.empty ()); + assert (extra_reasons_lit.size () == 1); + + for (auto &litId : h->pos_lhs_ids) { + if (litId.current_lit == g->lhs) { + assert (extra_reasons_ulit.empty ()); + 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); + assert (litId.clause); + extra_reasons_ulit.push_back (litId.clause->id); + } + } + assert (!extra_reasons_ulit.empty ()); + assert (extra_reasons_ulit.size () == 1); + return; + } + if (g_tautology || h_tautology) { + // special case: actually we have an equivalence due to binary clauses + // and some of the clauses from the gate are actually tautologies + assert (g_tautology != h_tautology); + Gate *tauto = (g_tautology ? g : h); + Gate *other = (g_tautology ? h : g); + LOG (tauto, "one gate is a tautology"); + assert (tauto != other); + assert (tauto == h || tauto == g); + + auto &extra_reasons_tauto = + (!g_tautology ? extra_reasons_lit : extra_reasons_ulit); + auto &extra_reasons_other = + (!g_tautology ? extra_reasons_ulit : extra_reasons_lit); + + // one direction: the binary clause already exists + for (auto &litId : other->pos_lhs_ids) { + if (litId.current_lit == tauto->lhs) { + assert (litId.clause); + assert (extra_reasons_tauto.empty ()); + LOG (litId.clause, "binary clause to push into the reason"); + litId.clause = produce_rewritten_clause_lrat ( + litId.clause, other->lhs, remove_units); + assert (litId.clause); + extra_reasons_tauto.push_back (litId.clause->id); + } + } + assert (!extra_reasons_tauto.empty ()); + + // other direction, we have to resolve + LOG (tauto, "now the other direction"); + for (auto &litId : tauto->pos_lhs_ids) { + assert (litId.clause); + LOG (litId.clause, + "binary clause from %d to push into the reason [avoiding %d]", + litId.current_lit, tauto->lhs); + if (litId.current_lit != tauto->lhs) { + LOG (litId.clause, "binary clause to push into the reason"); + 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; + 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)); + } + 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); + assert (!extra_reasons_tauto.empty ()); + assert (!extra_reasons_other.empty ()); + return; + } + // default: resolve all clauses + // first rewrite + // TODO: do we really need dest as second exclusion? + produce_rewritten_clause_lrat_and_clean (h->pos_lhs_ids, -h->lhs, + remove_units); + assert (internal->clause.empty ()); + produce_rewritten_clause_lrat_and_clean (h->neg_lhs_ids, -h->lhs, + remove_units); + assert (internal->clause.empty ()); + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, -g->lhs, + remove_units); + assert (internal->clause.empty ()); + produce_rewritten_clause_lrat_and_clean (g->neg_lhs_ids, -g->lhs, + remove_units); + assert (internal->clause.empty ()); + + push_id_on_chain (extra_reasons_ulit, h->pos_lhs_ids); + push_id_on_chain (extra_reasons_ulit, g->neg_lhs_ids[0].clause); + lrat_chain.clear (); + LOG (extra_reasons_ulit, "lrat chain for negative side"); + + push_id_on_chain (extra_reasons_lit, g->pos_lhs_ids); + push_id_on_chain (extra_reasons_lit, h->neg_lhs_ids); + + lrat_chain.clear (); + LOG (extra_reasons_lit, "lrat chain for positive side"); + assert (!extra_reasons_lit.empty ()); + assert (!extra_reasons_ulit.empty ()); +} + +void Closure::update_and_gate (Gate *g, GatesTable::iterator it, int src, + int dst, LRAT_ID id1, LRAT_ID id2, + int falsifies, int clashing) { + LOG (g, "update and gate of arity %ld", g->arity ()); + assert (lrat_chain.empty ()); + bool garbage = true; + if (falsifies || clashing) { + if (internal->lrat) + learn_congruence_unit_falsifies_lrat_chain (g, src, dst, clashing, + falsifies, 0); + int unit = -g->lhs; + if (unit == src) + unit = dst; + else if (unit == -src) + unit = -dst; + learn_congruence_unit (unit); + if (internal->lrat) + lrat_chain.clear (); + } else if (g->arity () == 1) { + const signed char v = internal->val (g->lhs); + if (v > 0) { + if (internal->lrat) + learn_congruence_unit_falsifies_lrat_chain (g, src, dst, 0, 0, + g->lhs); + learn_congruence_unit (g->rhs[0]); + if (internal->lrat) + lrat_chain.clear (); + } else if (v < 0) { + if (internal->lrat) + learn_congruence_unit_when_lhs_set (g, src, id1, id2, dst); + learn_congruence_unit (-g->rhs[0]); + if (internal->lrat) + lrat_chain.clear (); + } else { + std::vector extra_reasons_lit; + std::vector extra_reasons_ulit; + if (internal->lrat) + update_and_gate_unit_build_lrat_chain (g, src, id1, id2, g->rhs[0], + extra_reasons_lit, + extra_reasons_ulit); + if (merge_literals_lrat (g, g, g->lhs, g->rhs[0], extra_reasons_lit, + extra_reasons_ulit)) { + ++internal->stats.congruence.unaries; + ++internal->stats.congruence.unary_and; + } + } + } else { + assert (g->arity () > 1); + sort_literals_by_var (g->rhs); + Gate *h = find_and_lits (g->rhs, g); + assert (g != h); + if (h) { + assert (garbage); + std::vector extra_reasons_lit2; + std::vector extra_reasons_ulit2; + if (internal->lrat) + update_and_gate_build_lrat_chain (g, h, extra_reasons_lit2, + extra_reasons_ulit2); + if (merge_literals_lrat (g, h, g->lhs, h->lhs, extra_reasons_lit2, + extra_reasons_ulit2)) + ++internal->stats.congruence.ands; + } else { + if (g->indexed) { + LOG (g, "removing from table"); + (void) table.erase (it); + } + g->hash = hash_lits (nonces, g->rhs); + LOG (g, "inserting gate into table"); + assert (table.count (g) == 0); + table.insert (g); + g->indexed = true; + garbage = false; + if (internal->lrat) + lrat_chain.clear (); + } + } + + if (garbage && !internal->unsat) + mark_garbage (g); +} + +void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { + assert (g->tag == Gate_Type::XOr_Gate); + assert (!internal->unsat && chain.empty ()); + LOG (g, "updating"); + bool garbage = true; + // TODO Florian LRAT for learn_congruence_unit + assert (g->arity () == 0 || internal->clause.empty ()); + assert (clause.empty ()); + if (g->arity () == 0) { + if (internal->clause.size ()) { + assert (!internal->proof || (internal->clause.size () == 1 && + internal->clause.back () == -g->lhs)); + assert (!internal->lrat || lrat_chain.size ()); + internal->clause.clear (); + + } else if (internal->lrat) { + simplify_unit_xor_lrat_clauses (g->pos_lhs_ids, g->lhs); + assert (clause.size () && clause.back () == -g->lhs); + clause.clear (); + } + + if (internal->lrat && internal->val (-g->lhs) < 0) { + internal->lrat_chain.push_back (internal->unit_id (g->lhs)); + for (auto id : lrat_chain) + internal->lrat_chain.push_back (id); + lrat_chain.clear (); + internal->learn_empty_clause(); + } else + learn_congruence_unit (-g->lhs); + + assert (clause.empty ()); + } else if (g->arity () == 1) { + std::vector reasons_implication, reasons_back; + if (internal->lrat) { + vector first; + simplify_and_sort_xor_lrat_clauses (g->pos_lhs_ids, first, g->lhs); + g->pos_lhs_ids = first; + assert (g->pos_lhs_ids.size () == 2); + reasons_implication.push_back (g->pos_lhs_ids[0].clause->id); + reasons_back.push_back (g->pos_lhs_ids[1].clause->id); + } + const signed char v = internal->val (g->lhs); + if (v > 0) { + if (internal->lrat) { + push_lrat_unit (g->lhs); + lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); + } + learn_congruence_unit (g->rhs[0]); + } else if (v < 0) { + if (internal->lrat) { + push_lrat_unit (-g->lhs); + lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); + } + learn_congruence_unit (-g->rhs[0]); + } else if (merge_literals_lrat ( + g->lhs, g->rhs[0], reasons_implication, + reasons_back)) { // TODO Florian merge with LRAT + ++internal->stats.congruence.unaries; + ++internal->stats.congruence.unary_and; + } + assert (clause.empty ()); + } else { + Gate *h = find_xor_gate (g); + if (h) { + assert (garbage); + std::vector reasons_implication, reasons_back; + add_xor_matching_proof_chain (g, g->lhs, h->pos_lhs_ids, h->lhs, + reasons_implication, reasons_back); + if (merge_literals_lrat (g->lhs, h->lhs, reasons_implication, + reasons_back)) + ++internal->stats.congruence.xors; + delete_proof_chain (); + } else { + if (g->indexed) { + remove_gate (git); + } + g->hash = hash_lits (nonces, g->rhs); + LOG (g, "reinserting in table"); + table.insert (g); + g->indexed = true; + assert (table.find (g) != end (table)); + garbage = false; + } + assert (clause.empty ()); + } + if (garbage && !internal->unsat) + mark_garbage (g); + assert (clause.empty ()); +} + +void Closure::simplify_and_gate (Gate *g) { + if (skip_and_gate (g)) + return; + GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); + assert (!g->indexed || git != end (table)); + LOG (g, "simplifying"); + int falsifies = 0; + std::vector::iterator it = begin (g->rhs); + bool ulhs_in_rhs = false; + for (auto lit : g->rhs) { + const signed char v = internal->val (lit); + if (v > 0) { + continue; + } + if (v < 0) { + falsifies = lit; + continue; + } + if (lit == -g->lhs) + ulhs_in_rhs = true; + *it++ = lit; + if (lit == g->lhs) + g->degenerated_and_pos = true; + if (lit == -g->lhs) + g->degenerated_and_neg = true; + } + + if (internal->lrat) { // updating reasons + size_t i = 0, size = g->pos_lhs_ids.size (); + for (size_t j = 0; j < size; ++j) { + LOG ("looking at %d [%ld %ld]", g->pos_lhs_ids[j].current_lit, i, j); + g->pos_lhs_ids[i] = g->pos_lhs_ids[j]; + if (!g->degenerated_and_pos && + internal->val (g->pos_lhs_ids[i].current_lit) && + g->pos_lhs_ids[i].current_lit != falsifies) + continue; + LOG ("keeping %d [%ld %ld]", g->pos_lhs_ids[i].current_lit, i, j); + ++i; + } + LOG ("resizing to %ld", i); + g->pos_lhs_ids.resize (i); + } + + assert (it <= end (g->rhs)); // can be equal when ITE are converted to + // ands leading to + assert (it >= begin (g->rhs)); + LOG (g, "shrunken"); + + g->shrunken = true; + g->rhs.resize (it - std::begin (g->rhs)); + g->hash = hash_lits (nonces, g->rhs); + + LOG (g, "shrunken"); + shrink_and_gate (g, falsifies); + std::vector reasons_lrat_src, reasons_lrat_usrc; + + update_and_gate (g, git, 0, 0, 0, 0, falsifies, 0); + ++internal->stats.congruence.simplified_ands; + ++internal->stats.congruence.simplified; + + if (ulhs_in_rhs) { // missing in Kissat, TODO: port back + assert (gate_contains (g, -g->lhs)); + if (internal->lrat) { + for (auto litId : g->pos_lhs_ids) { + if (litId.current_lit == g->lhs) { + compute_rewritten_clause_lrat_simple (litId.clause, 0); + break; + } + } + } + learn_congruence_unit (-g->lhs); + } +} + +bool Closure::simplify_gate (Gate *g) { + switch (g->tag) { + case Gate_Type::And_Gate: + simplify_and_gate (g); + break; + case Gate_Type::XOr_Gate: + simplify_xor_gate (g); + break; + case Gate_Type::ITE_Gate: + simplify_ite_gate (g); + break; + default: + assert (false); + break; + } + assert (lrat_chain.empty ()); + return !internal->unsat; +} + +bool Closure::simplify_gates (int lit) { + const auto &occs = goccs (lit); + for (Gate *g : occs) { + assert (lrat_chain.empty ()); + assert (clause.empty ()); + if (!simplify_gate (g)) + return false; + } + return true; +} +/*------------------------------------------------------------------------*/ +// AND gates + +Gate *Closure::find_and_lits (const vector &rhs, Gate *except) { + assert (is_sorted (begin (rhs), end (rhs), + sort_literals_by_var_smaller (internal))); + return find_gate_lits (rhs, Gate_Type::And_Gate, except); +} + +// search for the gate in the hash-table. We cannot use find, as we might +// be changing a gate, so there might be 2 gates with the same LHS (the one +// we are changing and the other we are looking for) +Gate *Closure::find_gate_lits (const vector &rhs, Gate_Type typ, + Gate *except) { + Gate *g = new Gate; + g->tag = typ; + g->rhs = rhs; + g->hash = hash_lits (nonces, g->rhs); + g->lhs = 0; + g->garbage = false; +#ifdef LOGGING + g->id = 0; +#endif + const auto &its = table.equal_range (g); + Gate *h = nullptr; + for (auto it = its.first; it != its.second; ++it) { + LOG ((*it), "checking gate in the table"); + if (*it == except) + continue; + assert ((*it)->lhs != g->lhs); + if ((*it)->tag != g->tag) + continue; + if ((*it)->rhs != g->rhs) + continue; + h = *it; + break; + } + + if (h) { + LOG (g, "searching"); + LOG (h, "already existing"); + delete g; + return h; + } + + else { + LOG (g->rhs, "gate not found in table"); + delete g; + return nullptr; + } +} + +Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { + rhs.clear (); + auto &lits = this->lits; + + for (auto lit : lits) { + if (lhs != lit) { + assert (lhs != -lit); + rhs.push_back (-lit); + } + } + + assert (rhs.size () + 1 == lits.size ()); + sort_literals_by_var (this->rhs); + + Gate *h = find_and_lits (this->rhs); + Gate *g = new Gate; + g->lhs = lhs; + g->tag = Gate_Type::And_Gate; + if (internal->lrat) { + g->neg_lhs_ids.push_back (LitClausePair (lhs, base_clause)); + for (auto i : lrat_chain_and_gate) + g->pos_lhs_ids.push_back (i); +#ifdef LOGGING + std::vector result; + transform (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), + back_inserter (result), + [] (const LitClausePair &x) { return x.clause->id; }); + LOG (result, "lrat chain positive (%d):", lhs); + result.clear (); + transform (begin (g->neg_lhs_ids), end (g->neg_lhs_ids), + back_inserter (result), + [] (const LitClausePair &x) { return x.clause->id; }); + LOG (result, "lrat chain negative (%d):", lhs); +#endif + } + + if (internal->lrat) + lrat_chain_and_gate.clear (); + + if (h) { + std::vector reasons_lrat_src, reasons_lrat_usrc; + if (internal->lrat) + merge_and_gate_lrat_produce_lrat (g, h, reasons_lrat_src, + reasons_lrat_usrc); + if (merge_literals_lrat (g, h, lhs, h->lhs, reasons_lrat_src, + reasons_lrat_usrc)) { + LOG ("found merged literals"); + ++internal->stats.congruence.ands; + } + return nullptr; + } else { + g->rhs = {rhs}; + assert (!internal->lrat || + g->pos_lhs_ids.size () == + g->arity ()); // otherwise we need intermediate clauses + g->garbage = false; + g->indexed = true; + g->shrunken = false; + g->hash = hash_lits (nonces, g->rhs); + + table.insert (g); + ++internal->stats.congruence.gates; +#ifdef LOGGING + g->id = fresh_id++; +#endif + LOG (g, "creating new"); + for (auto lit : g->rhs) { + connect_goccs (g, lit); + } + } + return g; +} + +Gate *Closure::find_first_and_gate (Clause *base_clause, int lhs) { + assert (internal->analyzed.empty ()); + const int not_lhs = -lhs; + LOG ("trying to find AND gate with first LHS %d", (lhs)); + LOG ("negated LHS %d occurs in %zd binary clauses", (not_lhs), + internal->occs (not_lhs).size ()); + unsigned matched = 0; + + const size_t arity = lits.size () - 1; + + for (auto w : internal->watches (not_lhs)) { + LOG (w.clause, "checking clause for candidates"); + assert (w.binary ()); + assert (w.clause->size == 2); + assert (w.clause->literals[0] == -lhs || w.clause->literals[1] == -lhs); + const int other = w.blit; + signed char &mark = marked (other); + if (mark) { + LOG ("marking %d mu2", other); + ++matched; + assert (~(mark & 2)); + mark |= 2; + internal->analyzed.push_back (other); + set_mu2_reason (other, w.clause); + if (internal->lrat) + lrat_chain_and_gate.push_back (LitClausePair (other, w.clause)); + } + } + + LOG ("found %zd initial LHS candidates", internal->analyzed.size ()); + if (matched < arity) { + if (internal->lrat) + lrat_chain_and_gate.clear (); + return nullptr; + } + + Gate *g = new_and_gate (base_clause, lhs); + + if (internal->lrat) { + lrat_chain_and_gate.clear (); + } + return g; +} + +Clause *Closure::learn_binary_tmp_or_full_clause (int a, int b) { + Clause *eq1; + if (internal->lrat) { + eq1 = add_tmp_binary_clause (a, b); + eq1 = maybe_promote_tmp_binary_clause (eq1); + } else + eq1 = maybe_add_binary_clause (a, b); + return eq1; +} + +Clause *Closure::maybe_add_binary_clause (int a, int b) { + assert (internal->clause.empty ()); + assert (internal->lrat_chain.empty ()); + assert (!internal->lrat); + assert (lrat_chain.empty ()); + LOG ("learning binary clause %d %d", a, b); + if (internal->unsat) + return nullptr; + if (a == -b) + return nullptr; + if (!internal->lrat) { + const signed char a_value = internal->val (a); + if (a_value > 0) + return nullptr; + const signed char b_value = internal->val (b); + if (b_value > 0) + return nullptr; + int unit = 0; + if (a == b) + unit = a; + else if (a_value < 0 && !b_value) { + unit = b; + } else if (!a_value && b_value < 0) + unit = a; + if (unit) { + LOG ("clause reduced to unit %d", unit); + learn_congruence_unit (unit); + return nullptr; + } + assert (!a_value), assert (!b_value); + } + return add_binary_clause (a, b); +} + +Clause *Closure::add_binary_clause (int a, int b) { + assert (internal->clause.empty ()); + internal->clause.push_back (a); + internal->clause.push_back (b); + if (internal->lrat) { + assert (lrat_chain.size () >= 1); + assert (internal->lrat_chain.empty ()); + swap (internal->lrat_chain, lrat_chain); + } + LOG (internal->lrat_chain, "chain"); + Clause *res = internal->new_hyper_ternary_resolved_clause_and_watch ( + false, full_watching); + const bool already_sorted = internal->vlit (a) < internal->vlit (b); + binaries.push_back (CompactBinary (res, res->id, already_sorted ? a : b, + already_sorted ? b : a)); + if (!full_watching) + new_unwatched_binary_clauses.push_back (res); + LOG (res, "learning clause"); + internal->clause.clear (); + if (internal->lrat) { + internal->lrat_chain.clear (); + } + assert (internal->clause.empty ()); + assert (internal->lrat_chain.empty ()); + return res; +} + +void Closure::check_not_tmp_binary_clause (Clause *c) { +#ifndef NDEBUG + assert (internal->lrat); + assert (internal->lrat_chain.empty ()); + assert (c->size == 2); + if (internal->val (c->literals[0]) || internal->val (c->literals[1])) + return; + assert (std::find (begin (extra_clauses), end (extra_clauses), c) == + end (extra_clauses)); +#else + (void) c; +#endif +}; + +Clause *Closure::maybe_promote_tmp_binary_clause (Clause *c) { + assert (internal->lrat); + assert (internal->lrat_chain.empty ()); + assert (c->size == 2); + LOG (c, "promoting tmp"); +#ifndef NDEBUG + assert (std::find (begin (extra_clauses), end (extra_clauses), c) != + end (extra_clauses)); +#endif + if (internal->val (c->literals[0]) || internal->val (c->literals[1])) + return c; + lrat_chain.push_back (c->id); + Clause *res = add_binary_clause (c->literals[0], c->literals[1]); + LOG (res, "promoted to"); + return res; +}; + +Clause *Closure::add_tmp_binary_clause (int a, int b) { + assert (internal->clause.empty ()); + assert (internal->lrat_chain.empty ()); + assert (internal->lrat); + LOG ("learning tmp binary clause %d %d", a, b); + if (internal->unsat) + return nullptr; + if (a == -b) + return nullptr; + assert (internal->clause.empty ()); + internal->clause.push_back (a); + internal->clause.push_back (b); + if (internal->lrat) { + assert (lrat_chain.size () >= 1); + assert (internal->lrat_chain.empty ()); + } + LOG (lrat_chain, "chain"); + Clause *res = new_tmp_clause (internal->clause); + internal->clause.clear (); + if (internal->lrat) { + lrat_chain.clear (); + } + assert (internal->clause.empty ()); + assert (internal->lrat_chain.empty ()); + LOG (res, "promoted to"); + return res; +} + +Gate *Closure::find_remaining_and_gate (Clause *base_clause, int lhs) { + const int not_lhs = -lhs; + + if (marked (not_lhs) < 2) { + LOG ("skipping no-candidate LHS %d (%d)", lhs, marked (not_lhs)); + return nullptr; + } + + LOG ("trying to find AND gate with remaining LHS %d", (lhs)); + LOG ("negated LHS %d occurs times in %zd binary clauses", (not_lhs), + internal->noccs (-lhs)); + + const size_t arity = lits.size () - 1; + size_t matched = 0; + assert (1 < arity); + + for (auto w : internal->watches (not_lhs)) { + assert (w.binary ()); +#ifdef LOGGING + Clause *c = w.clause; + LOG (c, "checking"); + assert (c->size == 2); + assert (c->literals[0] == not_lhs || c->literals[1] == not_lhs); +#endif + const int other = w.blit; + signed char &mark = marked (other); + if (!mark) + continue; + ++matched; + if (!(mark & 2)) { + lrat_chain_and_gate.push_back (LitClausePair (other, w.clause)); + LOG ("pushing %d -> %zd", other, w.clause->id); + continue; + } + LOG ("marking %d mu4", other); + assert (!(mark & 4)); + mark |= 4; + lrat_chain_and_gate.push_back (LitClausePair (other, w.clause)); + if (internal->lrat) + set_mu4_reason (other, w.clause); + } + + { + auto q = begin (internal->analyzed); + assert (!internal->analyzed.empty ()); + assert (marked (not_lhs) == 3); + for (auto lit : internal->analyzed) { + signed char &mark = marked (lit); + if (lit == not_lhs) { + mark = 1; + continue; + } + + assert ((mark & 3) == 3); + if (mark & 4) { + mark = 3; + *q = lit; + ++q; + LOG ("keeping LHS candidate %d", -lit); + } else { + LOG ("dropping LHS candidate %d", -lit); + mark = 1; + } + } + assert (q != end (internal->analyzed)); + assert (marked (not_lhs) == 1); + internal->analyzed.resize (q - begin (internal->analyzed)); + LOG ("after filtering %zu LHS candidate remain", + internal->analyzed.size ()); + } + + if (matched < arity) { + if (internal->lrat) + lrat_chain_and_gate.clear (); + return nullptr; + } + + if (!internal->lrat) + lrat_chain_and_gate.clear (); + return new_and_gate (base_clause, lhs); +} + +struct congruence_occurrences_rank { + Internal *internal; + congruence_occurrences_rank (Internal *s) : internal (s) {} + typedef uint64_t Type; + Type operator() (int a) { + uint64_t res = internal->noccs (-a); + res <<= 32; + res |= a; + return res; + } +}; + +struct congruence_occurrences_larger { + Internal *internal; + congruence_occurrences_larger (Internal *s) : internal (s) {} + bool operator() (const int &a, const int &b) const { + return congruence_occurrences_rank (internal) (a) < + congruence_occurrences_rank (internal) (b); + } +}; + +void Closure::extract_and_gates_with_base_clause (Clause *c) { + assert (!c->garbage); + assert (lrat_chain.empty ()); + LOG (c, "extracting and gates with clause"); + unsigned size = 0; + const unsigned arity_limit = + min (internal->opts.congruenceandarity, MAX_ARITY); + const unsigned size_limit = arity_limit + 1; + size_t max_negbincount = 0; + lits.clear (); + + for (int lit : *c) { + signed char v = internal->val (lit); + if (v < 0) { + // push_lrat_unit (-lit); + continue; + } + if (v > 0) { + assert (!internal->level); + LOG (c, "found satisfied clause"); + internal->mark_garbage (c); + if (internal->lrat) + lrat_chain.clear (); + return; + } + if (++size > size_limit) { + LOG (c, "clause is actually too large, thus skipping"); + if (internal->lrat) + lrat_chain.clear (); + return; + } + const size_t count = internal->noccs (-lit); + if (!count) { + LOG (c, + "%d negated does not occur in any binary clause, thus skipping", + lit); + if (internal->lrat) + lrat_chain.clear (); + return; + } + + if (count > max_negbincount) + max_negbincount = count; + lits.push_back (lit); + } + + if (size < 3) { + LOG (c, "is actually too small, thus skipping"); + if (internal->lrat) + lrat_chain.clear (); + assert (lrat_chain.empty ()); + return; + } + + const size_t arity = size - 1; + if (max_negbincount < arity) { + LOG (c, + "all literals have less than %lu negated occurrences" + "thus skipping", + arity); + if (internal->lrat) + lrat_chain.clear (); + return; + } + + internal->analyzed.clear (); + size_t reduced = 0; + const size_t clause_size = lits.size (); + for (size_t i = 0; i < clause_size; ++i) { + const int lit = lits[i]; + const unsigned count = internal->noccs (-lit); + marked (-lit) = 1; + set_mu1_reason (-lit, c); + if (count < arity) { + if (reduced < i) { + lits[i] = lits[reduced]; + lits[reduced++] = lit; + } else if (reduced == i) + ++reduced; + } + } + const size_t reduced_size = clause_size - reduced; + assert (reduced_size); + LOG (c, "trying as base arity %lu AND gate", arity); + assert (begin (lits) + reduced_size <= end (lits)); + MSORT (internal->opts.radixsortlim, begin (lits), + begin (lits) + reduced_size, + congruence_occurrences_rank (internal), + congruence_occurrences_larger (internal)); + bool first = true; + unsigned extracted = 0; + + for (size_t i = 0; i < clause_size; ++i) { + assert (lrat_chain.empty ()); + if (internal->unsat) + break; + if (c->garbage) + break; + const int lhs = lits[i]; + LOG ("trying LHS candidate literal %d with %ld negated occurrences", + (lhs), internal->noccs (-lhs)); + + if (first) { + first = false; + assert (internal->analyzed.empty ()); + if (find_first_and_gate (c, lhs) != nullptr) { + assert (lrat_chain.empty ()); + ++extracted; + } + } else if (internal->analyzed.empty ()) { + LOG ("early abort AND gate search"); + break; + } else if (find_remaining_and_gate (c, lhs)) { + assert (lrat_chain.empty ()); + ++extracted; + } + } + + unmark_all (); + LOG (lits, "finish unmarking"); + for (auto lit : lits) { + marked (-lit) = 0; + } + lrat_chain_and_gate.clear (); + if (extracted) + LOG (c, "extracted %u with arity %lu AND base", extracted, arity); +} + +void Closure::reset_and_gate_extraction () { + internal->clear_noccs (); + internal->clear_watches (); +} + +void Closure::extract_and_gates () { + assert (!full_watching); + if (!internal->opts.congruenceand) + return; + START (extractands); + marks.resize (internal->max_var * 2 + 3); + init_and_gate_extraction (); + + const size_t size = internal->clauses.size (); + for (size_t i = 0; i < size && !internal->terminated_asynchronously (); + ++i) { // we can learn new binary clauses, but no for loop + assert (lrat_chain.empty ()); + Clause *c = internal->clauses[i]; + if (c->garbage) + continue; + if (c->size == 2) + continue; + if (c->hyper) + continue; + if (c->redundant) + continue; + extract_and_gates_with_base_clause (c); + assert (lrat_chain.empty ()); + } + + reset_and_gate_extraction (); + STOP (extractands); +} + +/*------------------------------------------------------------------------*/ +// XOR gates + +uint64_t &Closure::new_largecounts (int lit) { + assert (internal->vlit (lit) < gnew_largecounts.size ()); + return gnew_largecounts[internal->vlit (lit)]; +} + +uint64_t &Closure::largecounts (int lit) { + assert (internal->vlit (lit) < glargecounts.size ()); + return glargecounts[internal->vlit (lit)]; +} + +bool parity_lits (const vector &lits) { + unsigned res = 0; + for (auto lit : lits) + res ^= (lit < 0); + return res; +} + +void inc_lits (vector &lits) { + bool carry = true; + for (size_t i = 0; i < lits.size () && carry; ++i) { + int lit = lits[i]; + carry = (lit < 0); + lits[i] = -lit; + } +} + +void Closure::check_ternary (int a, int b, int c) { + assert (internal->clause.empty ()); + if (internal->lrat) + return; + auto &clause = internal->clause; + assert (clause.empty ()); + clause.push_back (a); + clause.push_back (b); + clause.push_back (c); + internal->external->check_learned_clause (); + if (internal->proof) { + const LRAT_ID id = internal->clause_id++; + internal->proof->add_derived_clause (id, false, clause, {}); + internal->proof->delete_clause (id, false, clause); + } + + clause.clear (); +} + +void Closure::check_binary_implied (int a, int b) { + assert (internal->clause.empty ()); + if (internal->lrat) + return; + auto &clause = internal->clause; + assert (clause.empty ()); + clause.push_back (a); + clause.push_back (b); + check_implied (); + clause.clear (); +} + +void Closure::check_implied () { + if (internal->lrat) + return; + internal->external->check_learned_clause (); +} + +void Closure::add_xor_shrinking_proof_chain (Gate *g, int pivot) { + assert (internal->clause.empty ()); + assert (clause.empty ()); + if (!internal->proof) + return; + LOG (g, "starting XOR shrinking proof chain"); + vector first; + vector newclauses; + if (internal->lrat) { + simplify_and_sort_xor_lrat_clauses (g->pos_lhs_ids, first, g->lhs, + pivot); + gate_sort_lrat_reasons (first, pivot, g->lhs); + } + + auto &clause = internal->clause; + + const int lhs = g->lhs; + clause.push_back (-lhs); + for (auto lit : g->rhs) + clause.push_back (lit); + + const bool parity = (lhs > 0); + assert (parity == parity_lits (clause)); + const size_t size = clause.size (); + const unsigned end = 1u << (size - 1); + assert (!internal->lrat || first.size () == 2 * end); +#ifdef LOGGING + for (auto pair : first) { + LOG (pair.clause, "key %d", pair.current_lit); + } +#endif + // TODO Florian adjust indices of first depending on order... + // + for (unsigned i = 0; i != end; ++i) { + while (i && parity != parity_lits (clause)) + inc_lits (clause); + LOG (clause, "xor shrinking clause"); + if (!internal->lrat) { + clause.push_back (pivot); + check_and_add_to_proof_chain (clause); + clause.pop_back (); + clause.push_back (-pivot); + check_and_add_to_proof_chain (clause); + clause.pop_back (); + } + if (internal->lrat) { + assert (lrat_chain.empty ()); + lrat_chain.push_back (first[2 * i].clause->id); + lrat_chain.push_back (first[2 * i + 1].clause->id); + } + if (clause.size () > 1) { + if (internal->lrat) { + Clause *c = new_tmp_clause (clause); + newclauses.push_back (LitClausePair (0, c)); + lrat_chain.clear (); + } else { + check_and_add_to_proof_chain (clause); + } + } + if (clause.size () == 1) + return; + inc_lits (clause); + } + g->pos_lhs_ids.swap (newclauses); + + clause.clear (); +} + +void Closure::check_xor_gate_implied (Gate const *const g) { + assert (internal->clause.empty ()); + assert (g->tag == Gate_Type::XOr_Gate); + if (internal->lrat) { + return; + } + const int lhs = g->lhs; + LOG (g, "checking implied"); + auto &clause = internal->clause; + assert (clause.empty ()); + for (auto other : g->rhs) { + assert (other > 0); + clause.push_back (other); + } + clause.push_back (-lhs); + const unsigned arity = g->arity (); + const unsigned end = 1u << arity; + const bool parity = (lhs > 0); + + for (unsigned i = 0; i != end; ++i) { + while (i && parity_lits (clause) != parity) + inc_lits (clause); + internal->external->check_learned_clause (); + if (internal->proof) { + internal->proof->add_derived_clause (internal->clause_id, false, + clause, {}); + internal->proof->delete_clause (internal->clause_id, false, clause); + } + inc_lits (clause); + } + clause.clear (); +} + +Gate *Closure::find_xor_lits (const vector &rhs) { + assert (is_sorted (begin (rhs), end (rhs), + sort_literals_by_var_smaller (internal))); + return find_gate_lits (rhs, Gate_Type::XOr_Gate); +} + +Gate *Closure::find_xor_gate (Gate *g) { + assert (g->tag == Gate_Type::XOr_Gate); + assert (is_sorted (begin (g->rhs), end (g->rhs), + sort_literals_by_var_smaller (internal))); + return find_gate_lits (g->rhs, Gate_Type::XOr_Gate); +} + +void Closure::reset_xor_gate_extraction () { internal->clear_occs (); } + +bool Closure::normalize_ite_lits_gate (Gate *g) { + auto &rhs = g->rhs; + assert (rhs.size () == 3); + if (internal->lrat) + check_correct_ite_flags (g); + LOG (rhs, "RHS = "); + if (rhs[0] < 0) { + rhs[0] = -rhs[0]; + std::swap (rhs[1], rhs[2]); + if (internal->lrat) { + assert (g->pos_lhs_ids.size () == 4); + std::swap (g->pos_lhs_ids[0], g->pos_lhs_ids[2]); + std::swap (g->pos_lhs_ids[1], g->pos_lhs_ids[3]); + const int8_t flag = g->degenerated_ite; + const int8_t plus_then = flag & NO_PLUS_THEN; + const int8_t neg_then = flag & NO_NEG_THEN; + const int8_t plus_else = flag & NO_PLUS_ELSE; + const int8_t neg_else = flag & NO_NEG_ELSE; + g->degenerated_ite = (plus_then ? Special_ITE_GATE::NO_PLUS_ELSE + : Special_ITE_GATE::NORMAL) | + (neg_then ? Special_ITE_GATE::NO_NEG_ELSE + : Special_ITE_GATE::NORMAL) | + (plus_else ? Special_ITE_GATE::NO_PLUS_THEN + : Special_ITE_GATE::NORMAL) | + (neg_else ? Special_ITE_GATE::NO_NEG_THEN + : Special_ITE_GATE::NORMAL); + assert (g->pos_lhs_ids[0].current_lit == rhs[1]); + assert (g->pos_lhs_ids[2].current_lit == rhs[2]); + if (internal->lrat) + check_correct_ite_flags (g); + } + } + if (rhs[1] > 0) + return false; + if (internal->lrat) + check_correct_ite_flags (g); + rhs[1] = -rhs[1]; + rhs[2] = -rhs[2]; + LOG (rhs, "RHS = "); + if (internal->lrat) { + assert (g->pos_lhs_ids.size () == 4); + std::swap (g->pos_lhs_ids[0], g->pos_lhs_ids[1]); + std::swap (g->pos_lhs_ids[2], g->pos_lhs_ids[3]); + const int8_t flag = g->degenerated_ite; + const int8_t plus_then = flag & NO_PLUS_THEN; + const int8_t neg_then = flag & NO_NEG_THEN; + const int8_t plus_else = flag & NO_PLUS_ELSE; + const int8_t neg_else = flag & NO_NEG_ELSE; + g->degenerated_ite = (plus_then ? Special_ITE_GATE::NO_NEG_THEN + : Special_ITE_GATE::NORMAL) | + (neg_then ? Special_ITE_GATE::NO_PLUS_THEN + : Special_ITE_GATE::NORMAL) | + (plus_else ? Special_ITE_GATE::NO_NEG_ELSE + : Special_ITE_GATE::NORMAL) | + (neg_else ? Special_ITE_GATE::NO_PLUS_ELSE + : Special_ITE_GATE::NORMAL); + assert (g->pos_lhs_ids[0].current_lit == rhs[1]); + assert (g->pos_lhs_ids[2].current_lit == rhs[2]); + // incorrect as we have not negated the LHS yet! + // check_correct_ite_flags (g); + } + + LOG (g->rhs, "g/RHS = "); + return true; +} + +#ifndef NDEBUG +bool is_tautological_ite_gate (Gate *g) { + assert (g->tag == Gate_Type::ITE_Gate); + assert (g->rhs.size () == 3); + const int cond_lit = g->rhs[0]; + const int then_lit = g->rhs[1]; + const int else_lit = g->rhs[2]; + return cond_lit == then_lit || cond_lit == else_lit; +} +#endif + +Gate *Closure::find_ite_gate (Gate *g, bool &negate_lhs) { + negate_lhs = normalize_ite_lits_gate (g); + LOG (g, "post normalize"); + return find_gate_lits (g->rhs, Gate_Type::ITE_Gate, g); +} + +LRAT_ID Closure::check_and_add_to_proof_chain (vector &clause) { + internal->external->check_learned_clause (); + const LRAT_ID id = ++internal->clause_id; + if (internal->proof) { + if (internal->lrat) { + assert (internal->lrat_chain.empty ()); + assert (lrat_chain.size () >= 1); + } + internal->proof->add_derived_clause (id, true, clause, lrat_chain); + lrat_chain.clear (); + } + return id; +} + +void Closure::add_clause_to_chain (std::vector unsimplified, + LRAT_ID id) { + const uint32_t id2_higher = (id >> 32); + const uint32_t id2_lower = (uint32_t) (id & (LRAT_ID) (uint32_t) (-1)); + assert (id == ((LRAT_ID) id2_higher << 32) + (LRAT_ID) id2_lower); + chain.push_back (id2_higher); + chain.push_back (id2_lower); + LOG (unsimplified, "pushing to chain"); + chain.insert (end (chain), begin (unsimplified), end (unsimplified)); + chain.push_back (0); +} + +LRAT_ID Closure::simplify_and_add_to_proof_chain (vector &unsimplified, + LRAT_ID delete_id) { + vector &clause = internal->clause; + assert (clause.empty ()); +#ifndef NDEBUG + for (auto lit : unsimplified) { + assert (!(marked (lit) & 4)); + } +#endif + + bool trivial = false; + for (auto lit : unsimplified) { + signed char &lit_mark = marked (lit); + if (lit_mark & 4) + continue; + signed char ¬_lit_mark = marked (-lit); + if (not_lit_mark & 4) { + trivial = true; + break; + } + lit_mark |= 4; + clause.push_back (lit); + } + for (auto lit : clause) { + signed char &mark = marked (lit); + assert (mark & 4); + mark &= ~4u; + } + + LRAT_ID id = 0; + if (!trivial) { + if (delete_id) { + if (internal->proof) { + internal->proof->delete_clause (delete_id, true, clause); + lrat_chain.clear (); + } + } else { + id = check_and_add_to_proof_chain (clause); + add_clause_to_chain (clause, id); + } + } else { + LOG ("skipping trivial proof"); + lrat_chain.clear (); + } + clause.clear (); + return id; +} +/*------------------------------------------------------------------------*/ +void Closure::add_ite_turned_and_binary_clauses (Gate *g) { + if (!internal->proof) + return; + if (internal->lrat) + return; + LOG ("starting ITE turned AND supporting binary clauses"); + assert (unsimplified.empty ()); + assert (chain.empty ()); + int not_lhs = -g->lhs; + unsimplified.push_back (not_lhs); + unsimplified.push_back (g->rhs[0]); + simplify_and_add_to_proof_chain (unsimplified); + unsimplified.pop_back (); + unsimplified.push_back (g->rhs[1]); + simplify_and_add_to_proof_chain (unsimplified); + unsimplified.clear (); +} +void Closure::simplify_unit_xor_lrat_clauses ( + const vector &source, int lhs) { + assert (internal->lrat); + for (auto pair : source) { + compute_rewritten_clause_lrat_simple (pair.clause, lhs); + if (lrat_chain.size ()) + break; + } + assert (clause.size () == 1); +} +void Closure::simplify_and_sort_xor_lrat_clauses ( + const vector &source, vector &target, + int lhs, int except2, bool flip) { + assert (internal->lrat); + for (auto pair : source) { + Clause *c = produce_rewritten_clause_lrat (pair.clause, lhs); + if (c) { + target.push_back (LitClausePair (0, c)); + } + } + gate_sort_lrat_reasons (target, lhs, except2, flip); +} +void Closure::add_xor_matching_proof_chain ( + Gate *g, int lhs1, const vector &clauses2, int lhs2, + vector &to_lrat, vector &back_lrat) { + if (lhs1 == lhs2) + return; + if (!internal->proof) + return; + assert (unsimplified.empty ()); + unsimplified = g->rhs; + vector first; + vector second; + if (internal->lrat) { + simplify_and_sort_xor_lrat_clauses (g->pos_lhs_ids, first, lhs1); + simplify_and_sort_xor_lrat_clauses (clauses2, second, lhs2, 0, 1); + g->pos_lhs_ids = first; + } + LOG ("starting XOR matching proof"); + // for lrat + vector first_ids; + vector second_ids; + for (auto pair : first) { + bool first = pair.current_lit & 1; + int rest = pair.current_lit >> 1; + rest &= ~(1 << (g->rhs.size () - 1)); + if (first == (lhs1 > 0)) { + first_ids.push_back (LitIdPair (rest, pair.clause->id)); + } else { + second_ids.push_back (LitIdPair (rest, pair.clause->id)); + } + LOG (pair.clause, "key %d", pair.current_lit); + } + for (auto pair : second) { + bool first = pair.current_lit & 1; + int rest = pair.current_lit >> 1; + rest &= ~(1 << (g->rhs.size () - 1)); + if (first == (lhs2 < 0)) { + first_ids.push_back (LitIdPair (rest, pair.clause->id)); + } else { + second_ids.push_back (LitIdPair (rest, pair.clause->id)); + } + LOG (pair.clause, "key %d", pair.current_lit); + } + // TODO Florian: resort and ids after every round + do { + vector first_tmp; + vector second_tmp; + assert (!unsimplified.empty ()); + unsimplified.pop_back (); + const size_t size = unsimplified.size (); + assert (size < 32); + const size_t off = 1u << size; + for (size_t i = 0; i != off; ++i) { + int32_t n = 0; + if (internal->lrat) { + n = number_from_xor_reason_reversed (unsimplified); + assert (lrat_chain.empty ()); + for (auto pair : first_ids) { + if (pair.lit == n) + lrat_chain.push_back (pair.id); + } + assert (lrat_chain.size () == 2); + } + unsimplified.push_back (-lhs1); + unsimplified.push_back (lhs2); + const LRAT_ID id1 = simplify_and_add_to_proof_chain (unsimplified); + unsimplified.resize (unsimplified.size () - 2); + if (internal->lrat) { + int32_t rest = n &= ~(1 << (unsimplified.size () - 1)); + first_tmp.push_back (LitIdPair (rest, id1)); + n = number_from_xor_reason_reversed (unsimplified); + lrat_chain.clear (); + for (auto pair : second_ids) { + if (pair.lit == n) + lrat_chain.push_back (pair.id); + } + assert (lrat_chain.size () == 2); + } + unsimplified.push_back (lhs1); + unsimplified.push_back (-lhs2); + const LRAT_ID id2 = simplify_and_add_to_proof_chain (unsimplified); + unsimplified.resize (unsimplified.size () - 2); + if (internal->lrat) { + lrat_chain.clear (); + int32_t rest = n &= ~(1 << (unsimplified.size () - 1)); + second_tmp.push_back (LitIdPair (rest, id2)); + } + inc_lits (unsimplified); + } + if (internal->lrat) { + first_ids.swap (first_tmp); + second_ids.swap (second_tmp); + } + } while (!unsimplified.empty ()); + if (internal->lrat) { + assert (first_ids.size () == 1); + assert (second_ids.size () == 1); + to_lrat.push_back (first_ids.back ().id); + back_lrat.push_back (second_ids.back ().id); + } + assert (!internal->lrat || to_lrat.size () == 1); + assert (!internal->lrat || back_lrat.size () == 1); + LOG ("finished XOR matching proof"); + assert (unsimplified.empty ()); +} + +// this function needs to either put the clauses from +// lrat_chain_and_gate into g->pos_neg_ids or clear it or do something with +// it if you merge gates. +Gate *Closure::new_xor_gate (const vector &glauses, + int lhs) { + rhs.clear (); + + for (auto lit : lits) { + if (lhs != lit && -lhs != lit) { + assert (lit > 0); + rhs.push_back (lit); + } + } + assert (rhs.size () + 1 == lits.size ()); + sort_literals_by_var (rhs); + Gate *g = find_xor_lits (this->rhs); + if (g) { + check_xor_gate_implied (g); + std::vector reasons_implication, reasons_back; + add_xor_matching_proof_chain (g, g->lhs, glauses, lhs, + reasons_implication, reasons_back); + if (merge_literals_lrat (g->lhs, lhs, reasons_implication, + reasons_back)) { + ++internal->stats.congruence.xors; + } + delete_proof_chain (); + assert (internal->unsat || chain.empty ()); + } else { + g = new Gate; + g->lhs = lhs; + g->tag = Gate_Type::XOr_Gate; + g->rhs = {rhs}; + g->garbage = false; + g->indexed = true; + g->shrunken = false; + g->hash = hash_lits (nonces, g->rhs); + for (auto pair : glauses) + g->pos_lhs_ids.push_back (pair); + table.insert (g); + ++internal->stats.congruence.gates; +#ifdef LOGGING + g->id = fresh_id++; +#endif + LOG (g, "creating new"); + check_xor_gate_implied (g); + for (auto lit : g->rhs) { + connect_goccs (g, lit); + } + } + return g; +} +uint32_t +Closure::number_from_xor_reason_reversed (const std::vector &rhs) { + uint32_t n = 0; + assert (is_sorted (rhs.rbegin (), rhs.rend (), + sort_literals_by_var_smaller_except (internal, 0, 0))); + assert (rhs.size () <= 32); + for (auto r = rhs.rbegin (); r != rhs.rend (); r++) { + int lit = *r; + n *= 2; + n += !(lit > 0); + } + return n; +} + +uint32_t Closure::number_from_xor_reason (const std::vector &rhs, + int lhs, int except, bool flip) { + uint32_t n = 0; + assert (is_sorted ( + begin (rhs), end (rhs), + sort_literals_by_var_smaller_except (internal, lhs, except))); + (void) lhs, (void) except; + assert (rhs.size () <= 32); + for (auto lit : rhs) { + n *= 2; + n += !(lit > 0) ^ flip; + flip = 0; + } + return n; +} + +// this is how I planned to sort it and produce the number +// Look at this first +void Closure::gate_sort_lrat_reasons (LitClausePair &litId, int lhs, + int except2, bool flip) { + assert (clause.empty ()); + std::copy (begin (*litId.clause), end (*litId.clause), + back_inserter (clause)); + sort_literals_by_var_except (clause, lhs, except2); + litId.current_lit = number_from_xor_reason (clause, lhs, except2, flip); + clause.clear (); +} + +struct smaller_pair_first_rank { + typedef size_t Type; + Type operator() (const LitClausePair &a) { return a.current_lit; } +}; + +// this is how I planned to sort it and produce the number +// Look at this first +void Closure::gate_sort_lrat_reasons (std::vector &xs, + int lhs, int except2, bool flip) { + assert (clause.empty ()); + assert (!xs.empty ()); + for (auto &litId : xs) { + gate_sort_lrat_reasons (litId, lhs, except2, flip); + } + + rsort (begin (xs), end (xs), smaller_pair_first_rank ()); + +#ifndef NDEBUG + std::for_each (begin (xs), end (xs), [&xs] (const LitClausePair &x) { + assert (x.clause->size == xs[1].clause->size); + }); +#endif +} + +void Closure::init_xor_gate_extraction (std::vector &candidates) { + const unsigned arity_limit = internal->opts.congruencexorarity; + assert (arity_limit < 32); // we use unsigned int. + const unsigned size_limit = arity_limit + 1; + glargecounts.resize (2 * internal->vsize, 0); + + for (auto c : internal->clauses) { + LOG (c, "considering clause for XOR"); + if (c->redundant) + continue; + if (c->garbage) + continue; + if (c->size < 3) + continue; + unsigned size = 0; + for (auto lit : *c) { + const signed char v = internal->val (lit); + if (v < 0) + continue; + if (v > 0) { + LOG (c, "satisfied by %d", lit); + internal->mark_garbage (c); + goto CONTINUE_COUNTING_NEXT_CLAUSE; + } + if (size == size_limit) + goto CONTINUE_COUNTING_NEXT_CLAUSE; + ++size; + } + + if (size < 3) + continue; + for (auto lit : *c) { + if (internal->val (lit)) + continue; + ++largecounts (lit); + } + + LOG (c, "considering clause for XOR as candidate"); + candidates.push_back (c); + CONTINUE_COUNTING_NEXT_CLAUSE:; + } + + LOG ("considering %zd out of %zd", candidates.size (), + internal->irredundant ()); + const unsigned rounds = internal->opts.congruencexorcounts; +#ifdef LOGGING + const size_t original_size = candidates.size (); +#endif + LOG ("resizing glargecounts to size %zd", glargecounts.size ()); + for (unsigned round = 0; round < rounds; ++round) { + LOG ("round %d of XOR extraction", round); + size_t removed = 0; + gnew_largecounts.resize (2 * internal->vsize); + unsigned cand_size = candidates.size (); + size_t j = 0; + for (size_t i = 0; i < cand_size; ++i) { + Clause *c = candidates[i]; + LOG (c, "considering"); + unsigned size = 0; + for (auto lit : *c) { + if (!internal->val (lit)) + ++size; + } + assert (3 <= size); + assert (size <= size_limit); + const unsigned arity = size - 1; + const unsigned needed_clauses = 1u << (arity - 1); + for (auto lit : *c) { + if (largecounts (lit) < needed_clauses) { + LOG (c, "not enough occurrences, so ignoring"); + removed++; + goto CONTINUE_WITH_NEXT_CANDIDATE_CLAUSE; + } + } + for (auto lit : *c) + if (!internal->val (lit)) + new_largecounts (lit)++; + candidates[j++] = candidates[i]; + + CONTINUE_WITH_NEXT_CANDIDATE_CLAUSE:; + } + candidates.resize (j); + glargecounts = std::move (gnew_largecounts); + gnew_largecounts.clear (); + LOG ("moving counts %zd", glargecounts.size ()); + if (!removed) + break; + + LOG ("after round %d, %zd (%ld %%) remain", round, candidates.size (), + candidates.size () / (1 + original_size) * 100); + } + + for (auto c : candidates) { + for (auto lit : *c) + internal->occs (lit).push_back (c); + } +} + +Clause *Closure::find_large_xor_side_clause (std::vector &lits) { + unsigned least_occurring_literal = 0; + unsigned count_least_occurring = UINT_MAX; + const size_t size_lits = lits.size (); +#if defined(LOGGING) || !defined(NDEBUG) + const unsigned arity = size_lits - 1; +#endif +#ifndef NDEBUG + const unsigned count_limit = 1u << (arity - 1); +#endif + LOG (lits, "trying to find arity %u XOR side clause", arity); + for (auto lit : lits) { + assert (!internal->val (lit)); + marked (lit) = 1; + unsigned count = largecount (lit); + assert (count_limit <= count); + if (count >= count_least_occurring) + continue; + count_least_occurring = count; + least_occurring_literal = lit; + } + Clause *res = 0; + assert (least_occurring_literal); + LOG ("searching XOR side clause watched by %d#%u", + least_occurring_literal, count_least_occurring); + LOG ("searching for size %ld", size_lits); + for (auto c : internal->occs (least_occurring_literal)) { + LOG (c, "checking"); + assert (c->size != 2); // TODO kissat has break + if (c->garbage) + continue; + if ((size_t) c->size < size_lits) + continue; + size_t found = 0; + for (auto other : *c) { + const signed char value = internal->val (other); + if (value < 0) + continue; + if (value > 0) { + LOG (c, "found satisfied %d in", other); + internal->mark_garbage (c); + assert (c->garbage); + break; + } + if (marked (other)) + found++; + else { + LOG ("not marked %d", other); + found = 0; + break; + } + } + if (found == size_lits && !c->garbage) { + res = c; + break; + } else { + LOG ("too few literals"); + } + } + for (auto lit : lits) + marked (lit) = 0; + if (res) + LOG (res, "found matching XOR side"); + else + LOG ("no matching XOR side clause found"); + return res; +} + +void Closure::extract_xor_gates_with_base_clause (Clause *c) { + LOG (c, "checking clause"); + lits.clear (); + int smallest = 0; + int largest = 0; + const unsigned arity_limit = internal->opts.congruencexorarity; + const unsigned size_limit = arity_limit + 1; + unsigned negated = 0, size = 0; + bool first = true; + for (auto lit : *c) { + const signed char v = internal->val (lit); + if (v < 0) + continue; + if (v > 0) { + internal->mark_garbage (c); + return; + } + if (size == size_limit) { + LOG (c, "size limit reached"); + return; + } + + if (first) { + largest = smallest = lit; + first = false; + } else { + assert (smallest); + assert (largest); + if (internal->vlit (lit) < internal->vlit (smallest)) { + LOG ("new smallest %d", lit); + smallest = lit; + } + if (internal->vlit (lit) > internal->vlit (largest)) { + if (largest < 0) { + LOG (c, "not largest %d (largest: %d) occurs negated in XOR base", + lit, largest); + return; + } + largest = lit; + } + } + if (lit < 0 && internal->vlit (lit) < internal->vlit (largest)) { + LOG (c, "negated literal %d not largest in XOR base", lit); + return; + } + if (lit < 0 && negated++) { + LOG (c, "more than one negated literal in XOR base"); + return; + } + lits.push_back (lit); + ++size; + } + assert (size == lits.size ()); + if (size < 3) { + LOG (c, "short XOR base clause"); + return; + } + + LOG ("double checking if possible"); + const unsigned arity = size - 1; + const unsigned needed_clauses = 1u << (arity - 1); + for (auto lit : lits) { + for (int sign = 0; sign != 2; ++sign, lit = -lit) { + unsigned count = largecount (lit); + if (count >= needed_clauses) + continue; + LOG (c, + "literal %d in XOR base clause only occurs %u times in large " + "clause thus skipping", + lit, count); + return; + } + } + + LOG ("checking for XOR side clauses"); + assert (smallest && largest); + const unsigned end = 1u << arity; + assert (negated == parity_lits (lits)); + unsigned found = 0; + vector glauses; + glauses.push_back (LitClausePair (0, c)); + for (unsigned i = 0; i != end; ++i) { + while (i && parity_lits (lits) != negated) + inc_lits (lits); + if (i) { + // the clause must be stored + // you can use `lrat_chain_and_gate` for this + Clause *d = find_large_xor_side_clause (lits); + if (!d) + return; + assert (!d->redundant); + glauses.push_back (LitClausePair (i, d)); + } else + assert (!c->redundant); + inc_lits (lits); + ++found; + } + + while (parity_lits (lits) != negated) + inc_lits (lits); + LOG (lits, "found all needed %u matching clauses:", found); + assert (found == 1u << arity); + if (negated) { + auto p = begin (lits); + int lit; + while ((lit = *p) > 0) + p++; + LOG ("flipping RHS literal %d", (lit)); + *p = -lit; + } + LOG (lits, "normalized negations"); + unsigned extracted = 0; + for (auto lhs : lits) { + if (!negated) + lhs = -lhs; + Gate *g = new_xor_gate (glauses, lhs); + if (g) + extracted++; + if (internal->unsat) + break; + } + if (!extracted) + LOG ("no arity %u XOR gate extracted", arity); +} +void Closure::extract_xor_gates () { + assert (!full_watching); + if (!internal->opts.congruencexor) + return; + START (extractxors); + LOG ("starting extracting XOR"); + std::vector candidates = {}; + init_xor_gate_extraction (candidates); + for (auto c : candidates) { + if (internal->unsat) + break; + if (c->garbage) + continue; + extract_xor_gates_with_base_clause (c); + } + reset_xor_gate_extraction (); + STOP (extractxors); +} + +/*------------------------------------------------------------------------*/ +void Closure::find_units () { + size_t units = 0; + for (auto v : internal->vars) { + RESTART: + if (!internal->flags (v).active ()) + continue; + for (int sgn = -1; sgn < 1; sgn += 2) { + int lit = v * sgn; + for (auto w : internal->watches (lit)) { + if (!w.binary ()) + continue; // todo check that binaries first + const int other = w.blit; + if (marked (-other)) { + LOG (w.clause, "binary clause %d %d and %d %d give unit %d", lit, + other, lit, -other, lit); + ++units; + if (internal->lrat) { + lrat_chain.push_back (w.clause->id); + lrat_chain.push_back (marked_mu1 (-other).clause->id); + } + bool failed = !learn_congruence_unit (lit); + unmark_all (); + if (failed) + return; + else + goto RESTART; + } + if (marked (other)) + continue; + marked (other) = 1; + set_mu1_reason (other, w.clause); + internal->analyzed.push_back (other); + } + unmark_all (); + } + assert (internal->analyzed.empty ()); + } + LOG ("found %zd units", units); +} + +void Closure::find_equivalences () { + assert (!internal->unsat); + + for (auto v : internal->vars) { + RESTART: + if (!internal->flags (v).active ()) + continue; + int lit = v; + for (auto w : internal->watches (lit)) { + if (!w.binary ()) + break; + assert (w.size == 2); + const int other = w.blit; + if (internal->vlit (lit) > internal->vlit (other)) + continue; + if (marked (other)) + continue; + internal->analyzed.push_back (other); + marked (other) = true; + set_mu1_reason (other, w.clause); + } + + if (internal->analyzed.empty ()) + continue; + + for (auto w : internal->watches (-lit)) { + if (!w.binary ()) + break; // binary clauses are first + const int other = w.blit; + if (internal->vlit (-lit) > internal->vlit (other)) + continue; + assert (-lit != other); + LOG ("binary clause %d %d", -lit, other); + if (marked (-other)) { + int lit_repr = find_representative (lit); + int other_repr = find_representative (other); + LOG ("found equivalence %d %d with %d and %d as the representative", + lit, other, lit_repr, other_repr); + if (lit_repr != other_repr) { + // if (internal->lrat) { + // // This cannot work + // // if you have 2 = 1 and 3=4 + // // you cannot add 2=3. You really to connect the + // representatives directly + // // therefore you actually need to learn the clauses 2->3->4 + // and -2->1 and vice-versa eager_representative_id (other) = + // marked_mu1 (-other).clause->id; eager_representative_id + // (-other) = w.clause->id; assert (eager_representative_id + // (other) != -1); LOG ("lrat: %d (%zd) %d (%zd)", other, + // eager_representative_id (other), -other, + // eager_representative_id (-other)); + // } + promote_clause (marked_mu1 (-other).clause); + promote_clause (w.clause); + LOG (w.clause, "merging"); + LOG (marked_mu1 (-other).clause, "with"); + if (merge_literals_equivalence ( + lit, other, + internal->lrat ? marked_mu1 (-other).clause : nullptr, + w.clause)) { + ++internal->stats.congruence.congruent; + } + unmark_all (); + if (internal->unsat) + return; + else + goto RESTART; + } + } + } + unmark_all (); + } + assert (internal->analyzed.empty ()); + LOG ("found %zd equivalences", schedule.size ()); +} + +/*------------------------------------------------------------------------*/ +// Initialization + +void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, + LRAT_ID id2) { + if (skip_and_gate (g)) + return; + if (!gate_contains (g, src)) + return; + if (internal->val (src)) { + // In essence the code below does the same thing as simplify_and_gate + // but the necessary LRAT chain are different. + simplify_and_gate (g); + return; + } + assert (src); + assert (dst); + assert (internal->val (src) == internal->val (dst)); + GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); + LOG (g, "rewriting %d into %d in", src, dst); + int clashing = 0, falsifies = 0; + unsigned dst_count = 0, not_dst_count = 0; + auto q = begin (g->rhs); + for (int &lit : g->rhs) { + if (lit == src) + lit = dst; + if (lit == -g->lhs) { + LOG ("found negated LHS literal %d", lit); + clashing = lit; + g->degenerated_and_neg = true; + break; + } + if (lit == g->lhs) + g->degenerated_and_pos = true; + const signed char val = internal->val (lit); + if (val > 0) { + continue; + } + if (val < 0) { + LOG ("found falsifying literal %d", (lit)); + falsifies = lit; + break; + } + if (lit == dst) { + if (not_dst_count) { + LOG ("clashing literals %d and %d", (-dst), (dst)); + clashing = -dst; + break; + } + if (dst_count++) + continue; + } + if (lit == -dst) { + if (dst_count) { + assert (!not_dst_count); + LOG ("clashing literals %d and %d", (dst), (-dst)); + clashing = -dst; + break; + } + assert (!not_dst_count); + ++not_dst_count; + } + *q++ = lit; + } + LOG (lrat_chain, "lrat chain after rewriting"); + + if (internal->lrat) { // updating reasons in the chain. +#ifdef LOGGING + for (auto litId : g->pos_lhs_ids) { + LOG (litId.clause, "%d ->", litId.current_lit); + } +#endif + // We remove all assigned literals except the falsified literal such + // that we can produce an LRAT chain + size_t i = 0, size = g->pos_lhs_ids.size (); + bool found = false; + assert (!falsifies || !clashing); + const int orig_falsifies = falsifies == dst ? src : falsifies; + const int orig_clashing = + clashing == -dst ? -src : (clashing == dst ? src : clashing); + int keep_clashing = clashing; + LOG ("keeping chain for falsifies: %d aka %d and clashing: %d aka %d", + falsifies, orig_falsifies, clashing, orig_clashing); + for (size_t j = 0; j < size; ++j) { + LOG (g->pos_lhs_ids[j].clause, "looking at %d [%zd %zd] with val %d", + g->pos_lhs_ids[j].current_lit, i, j, + internal->val (g->pos_lhs_ids[i].current_lit)); + g->pos_lhs_ids[i] = g->pos_lhs_ids[j]; + if (keep_clashing && g->pos_lhs_ids[i].current_lit != orig_clashing && + g->pos_lhs_ids[i].current_lit != -orig_clashing && + g->pos_lhs_ids[i].current_lit != keep_clashing && + g->pos_lhs_ids[i].current_lit != -keep_clashing) + continue; + if (internal->val (g->pos_lhs_ids[i].current_lit) && + g->pos_lhs_ids[i].current_lit != src && + g->pos_lhs_ids[i].current_lit != orig_falsifies) + continue; + if (g->pos_lhs_ids[i].current_lit == dst) { + if (!found) + found = true; + else + continue; // we have already one defining clause + } + + LOG ("maybe keeping %d [%zd %zd], src: %d, found: %d", + g->pos_lhs_ids[i].current_lit, i, j, src, found); + if (g->pos_lhs_ids[i].current_lit == src) { + if (!found) + g->pos_lhs_ids[i].current_lit = dst, found = true; + else + continue; // we have already one defining clause + } + LOG ("keeping %d [%zd %zd]", g->pos_lhs_ids[i].current_lit, i, j); + ++i; + } + LOG ("resizing to %zd", i); + assert (i); + g->pos_lhs_ids.resize (i); + } + + if (q != end (g->rhs)) { + g->rhs.resize (q - begin (g->rhs)); + g->shrunken = true; + } + assert (dst_count <= 2); + assert (not_dst_count <= 1); + + std::vector reasons_lrat_src, reasons_lrat_usrc; + shrink_and_gate (g, falsifies, clashing); + LOG (g, "rewritten as"); + assert (!internal->lrat || !g->pos_lhs_ids.empty ()); + // check_and_gate_implied (g); + update_and_gate (g, git, src, dst, id1, id2, falsifies, clashing); + ++internal->stats.congruence.rewritten_ands; +} + +bool Closure::rewrite_gate (Gate *g, int dst, int src, LRAT_ID id1, + LRAT_ID id2) { + switch (g->tag) { + case Gate_Type::And_Gate: + rewrite_and_gate (g, dst, src, id1, id2); + break; + case Gate_Type::XOr_Gate: + rewrite_xor_gate (g, dst, src); + break; + case Gate_Type::ITE_Gate: + rewrite_ite_gate (g, dst, src); + break; + default: + assert (false); + break; + } + assert (internal->unsat || lrat_chain.empty ()); + return !internal->unsat; +} + +bool Closure::rewrite_gates (int dst, int src, LRAT_ID id1, LRAT_ID id2) { + const auto &occs = goccs (src); + for (auto g : occs) { + assert (lrat_chain.empty ()); + if (!rewrite_gate (g, dst, src, id1, id2)) + return false; + else if (!g->garbage && gate_contains (g, dst)) + goccs (dst).push_back (g); + } + goccs (src).clear (); + +#ifndef NDEBUG + for (const auto &occs : gtab) { + for (auto g : occs) { + assert (g); + assert (g->garbage || !gate_contains (g, src)); + } + } +#endif + assert (lrat_chain.empty ()); + return true; +} + +bool Closure::rewriting_lhs (Gate *g, int dst) { + if (dst != g->lhs && dst != -g->lhs) + return false; + mark_garbage (g); + return true; +} + +// update to produce proofs +void Closure::rewrite_xor_gate (Gate *g, int dst, int src) { + if (skip_xor_gate (g)) + return; + if (rewriting_lhs (g, dst)) + return; + if (!gate_contains (g, src)) + return; + LOG (g, "rewriting (%d -> %d)", src, dst); + check_xor_gate_implied (g); + GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); + size_t j = 0, dst_count = 0; + bool original_dst_negated = (dst < 0); + dst = abs (dst); + unsigned negate = original_dst_negated; + const size_t size = g->rhs.size (); + for (size_t i = 0; i < size; ++i) { + int lit = g->rhs[i]; + assert (lit > 0); + if (lit == src) + lit = dst; + const signed char v = internal->val (lit); + if (v > 0) { + negate ^= true; + } + if (v) + continue; + if (lit == dst) + dst_count++; + LOG ("keeping value %d", lit); + g->rhs[j++] = lit; + } + if (negate) { + LOG ("flipping LHS %d", g->lhs); + g->lhs = -g->lhs; + } + assert (dst_count <= 2); + if (dst_count == 2) { + LOG ("destination found twice, removing"); + size_t k = 0; + for (size_t i = 0; i < j; ++i) { + const int lit = g->rhs[i]; + if (lit != dst) + g->rhs[k++] = g->rhs[i]; + } + assert (k == j - 2); + g->rhs.resize (k); + g->shrunken = true; + 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 { + assert (j == size); + sort_literals_by_var (g->rhs); + } + + assert (clause.empty ()); + // LRAT for add_xor_shrinking_proof_chain + // this should be unnecessary... + // TODO check if really unnecessary + if (dst_count > 1) + add_xor_shrinking_proof_chain (g, dst); + assert (internal->clause.size () <= 1); + update_xor_gate (g, git); + + if (!g->garbage && !internal->unsat && original_dst_negated && + dst_count == 1) { + connect_goccs (g, dst); + } + + check_xor_gate_implied (g); + // TODO stats +} + +// update to produce proofs +void Closure::simplify_xor_gate (Gate *g) { + LOG (g, "simplifying"); + if (skip_xor_gate (g)) + return; + check_xor_gate_implied (g); + unsigned negate = 0; + GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); + const size_t size = g->rhs.size (); + size_t j = 0; + for (size_t i = 0; i < size; ++i) { + int lit = g->rhs[i]; + assert (lit > 0); + const signed char v = internal->val (lit); + if (v > 0) + negate ^= 1; + if (!v) { + g->rhs[j++] = lit; + } + } + if (negate) { + LOG ("flipping LHS literal %d", (g->lhs)); + g->lhs = -(g->lhs); + } + if (j != size) { + LOG ("shrunken gate"); + g->shrunken = true; + g->rhs.resize (j); + assert (is_sorted (begin (g->rhs), end (g->rhs), + sort_literals_by_var_smaller (internal))); + g->hash = hash_lits (nonces, g->rhs); + } else { + assert (g->hash == hash_lits (nonces, g->rhs)); + } + + check_xor_gate_implied (g); + assert (clause.empty ()); + update_xor_gate (g, git); + LOG (g, "simplified"); + check_xor_gate_implied (g); + internal->stats.congruence.simplified++; + internal->stats.congruence.simplified_xors++; +} + +/*------------------------------------------------------------------------*/ +// propagation of clauses and simplification +void Closure::schedule_literal (int lit) { + const int idx = abs (lit); + if (scheduled[idx]) + return; + scheduled[idx] = true; + schedule.push (lit); + assert (lit != find_representative (lit)); + LOG ("scheduled literal %d", lit); +} + +bool Closure::propagate_unit (int lit) { + LOG ("propagation of congruence unit %d", lit); + if (internal->lrat) + lazy_propagated (lit) = true; + return simplify_gates (lit) && simplify_gates (-lit); +} + +bool Closure::propagate_units () { + while (units != + internal->trail + .size ()) { // units are added during propagation, so reloading + LOG ("propagating %d over gates", internal->trail[units]); + if (!propagate_unit (internal->trail[units++])) + return false; + } + return true; +} + +// The replacement has to be done eagerly, not lazily to make sure that the +// gates are in normalized form. Otherwise, some merges might be missed. +bool Closure::propagate_equivalence (int lit) { + if (internal->val (lit)) + return true; + LOG ("propagating literal %d", lit); + import_lazy_and_find_eager_representative_and_compress_both (lit); + const int repr = find_eager_representative_and_compress (lit); + const LRAT_ID id1 = find_eager_representative_lrat (lit); + const LRAT_ID id2 = find_eager_representative_lrat (-lit); + assert (lrat_chain.empty ()); + return rewrite_gates (repr, lit, id1, id2) && + rewrite_gates (-repr, -lit, id2, id1); +} + +size_t Closure::propagate_units_and_equivalences () { + START (congruencemerge); + size_t propagated = 0; + LOG ("propagating at least %zd units", schedule.size ()); + assert (lrat_chain.empty ()); + while (propagate_units () && !schedule.empty ()) { + assert (!internal->unsat); + assert (lrat_chain.empty ()); + ++propagated; + int lit = schedule.front (); + schedule.pop (); + scheduled[abs (lit)] = false; + if (!propagate_equivalence (lit)) + break; + } + + assert (internal->unsat || schedule.empty ()); + assert (internal->unsat || lrat_chain.empty ()); + + LOG ("propagated %zu congruence units", units); + LOG ("propagated %zu congruence equivalences", propagated); + +#ifndef NDEBUG + if (!internal->unsat) { + for (const auto &occs : gtab) { + for (auto g : occs) { + if (g->garbage) + continue; + 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! + // assert (g->tag != Gate_Type::ITE_Gate || (g->rhs.size() == 3 + // && g->rhs[1] != -g->lhs && g->rhs[2] != -g->lhs)); + // assert (table.count(g) == 1); + for (auto lit : g->rhs) { + assert (!internal->val (lit)); + assert (representative (lit) == lit); + } + } + } + for (Gate *g : table) { + if (g->garbage) + continue; + if (g->tag == Gate_Type::And_Gate) { + // assert (find_and_lits(g->arity, g->rhs)); + } + } + } +#endif + STOP (congruencemerge); + return propagated; +} + +std::string string_of_gate (Gate_Type t) { + switch (t) { + case Gate_Type::And_Gate: + return "And"; + case Gate_Type::XOr_Gate: + return "XOr"; + case Gate_Type::ITE_Gate: + return "ITE"; + default: + return "buggy"; + } +} + +void Closure::reset_closure () { + scheduled.clear (); + for (Gate *g : table) { + assert (g->indexed); + LOG (g, "deleting"); + if (!g->garbage) + delete g; + } + table.clear (); + + for (auto &occ : gtab) { + occ.clear (); + } + gtab.clear (); + + for (auto gate : garbage) + delete gate; + garbage.clear (); + + if (internal->lrat) { + assert (internal->proof); + for (auto c : extra_clauses) { + assert (!c->garbage); + internal->proof->delete_clause (c); + delete c; + } + extra_clauses.clear (); + } else { + assert (extra_clauses.empty ()); + } +} + +void Closure::reset_extraction () { + full_watching = true; + if (!internal->unsat && !internal->propagate ()) { + internal->learn_empty_clause (); + } + +#if 0 + // remove delete watched clauses from the watch list + for (auto v : internal->vars) { + for (auto sgn = -1; sgn <= 1; sgn += 2) { + const int lit = v * sgn; + auto &watchers = internal->watches (lit); + const size_t size = watchers.size (); + size_t j = 0; + for (size_t i = 0; i != size; ++i) { + const auto w = watchers[i]; + watchers[j] = watchers[i]; + if (!w.clause->garbage) + ++j; + } + watchers.resize(j); + } + } + // watch the remaining non-watched clauses + for (auto c : new_unwatched_binary_clauses) + internal->watch_clause (c); + new_unwatched_binary_clauses.clear(); + for (auto c : internal->clauses) { + if (c->garbage) + continue; + if (c->size != 2) + internal->watch_clause (c); + } +#else // simpler implementation + new_unwatched_binary_clauses.clear (); + internal->clear_watches (); + internal->connect_watches (); +#endif +} + +void Closure::forward_subsume_matching_clauses () { + START (congruencematching); + reset_closure (); + std::vector matchable; + matchable.resize (internal->max_var + 1); + size_t count_matchable = 0; + + for (auto idx : internal->vars) { + if (!internal->flags (idx).active ()) + continue; + const int lit = idx; + const int repr = find_representative (lit); + if (lit == repr) + continue; + const int repr_idx = abs (repr); + if (!matchable[idx]) { + LOG ("matchable %d", idx); + matchable[idx] = true; + count_matchable++; + } + + if (!matchable[repr_idx]) { + LOG ("matchable %d", repr_idx); + matchable[repr_idx] = true; + count_matchable++; + } + } + + LOG ("found %.0f%%", + (double) count_matchable / + (double) (internal->max_var ? internal->max_var : 1)); + std::vector candidates; + auto &analyzed = internal->analyzed; + + for (auto *c : internal->clauses) { + if (c->garbage) + continue; + if (c->redundant) + continue; + if (c->size == 2) + continue; + assert (analyzed.empty ()); + bool contains_matchable = false; + for (auto lit : *c) { + const signed char v = internal->val (lit); + if (v < 0) + continue; + if (v > 0) { + LOG (c, "mark satisfied"); + internal->mark_garbage (c); + break; + } + if (!contains_matchable) { + const int idx = abs (lit); + if (matchable[idx]) + contains_matchable = true; + } + + const int repr = find_representative (lit); + assert (!internal->val (repr)); + if (marked (repr)) + continue; + const int not_repr = -repr; + if (marked (not_repr)) { + LOG (c, "matches both %d and %d", (lit), (not_repr)); + internal->mark_garbage (c); + break; + } + marked (repr) = 1; + analyzed.push_back (repr); + } + + for (auto lit : analyzed) + marked (lit) = 0; + analyzed.clear (); + if (c->garbage) + continue; + if (!contains_matchable) { + LOG ("no matching variable"); + continue; + } + LOG (c, "candidate"); + candidates.push_back (c); + } + + rsort (begin (candidates), end (candidates), smaller_clause_size_rank ()); + size_t tried = 0, subsumed = 0; + internal->init_occs (); + for (auto c : candidates) { + assert (c->size != 2); + // TODO if terminated + ++tried; + if (find_subsuming_clause (c)) { + ++subsumed; + } + } + LOG ("[congruence] subsumed %.0f%%", + (double) subsumed / (double) (tried ? tried : 1)); + STOP (congruencematching); +} + +/*------------------------------------------------------------------------*/ +// Candidate clause 'subsumed' is subsumed by 'subsuming'. We need to copy +// the function because 'congruence' is too early to include the version +// from subsume + +void Closure::subsume_clause (Clause *subsuming, Clause *subsumed) { + // assert (!subsuming->redundant); + // assert (!subsumed->redundant); + auto &stats = internal->stats; + stats.subsumed++; + assert (subsuming->size <= subsumed->size); + LOG (subsumed, "subsumed"); + if (subsumed->redundant) + stats.subred++; + else + stats.subirr++; + if (subsumed->redundant || !subsuming->redundant) { + internal->mark_garbage (subsumed); + return; + } + LOG ("turning redundant subsuming clause into irredundant clause"); + subsuming->redundant = false; + if (internal->proof) + internal->proof->strengthen (subsuming->id); + internal->mark_garbage (subsumed); + stats.current.irredundant++; + stats.added.irredundant++; + stats.irrlits += subsuming->size; + assert (stats.current.redundant > 0); + stats.current.redundant--; + assert (stats.added.redundant > 0); + stats.added.redundant--; + // ... and keep 'stats.added.total'. +} + +bool Closure::find_subsuming_clause (Clause *subsumed) { + assert (!subsumed->garbage); + Clause *subsuming = nullptr; + for (auto lit : *subsumed) { + assert (internal->val (lit) <= 0); + const int repr_lit = find_representative (lit); + const signed char repr_val = internal->val (repr_lit); + assert (repr_val <= 0); + if (repr_val < 0) + continue; + if (marked (repr_lit)) + continue; + assert (!marked (-repr_lit)); + marked (repr_lit) = 1; + } + int least_occuring_lit = 0; + size_t count_least_occurring = INT_MAX; + LOG (subsumed, "trying to forward subsume"); + + for (auto lit : *subsumed) { + const int repr_lit = find_representative (lit); + const size_t count = internal->occs (lit).size (); + assert (count <= UINT_MAX); + if (count < count_least_occurring) { + count_least_occurring = count; + least_occuring_lit = repr_lit; + } + for (auto d : internal->occs (lit)) { + assert (!d->garbage); + assert (subsumed != d); + if (!subsumed->redundant && d->redundant) + continue; + for (auto other : *d) { + const signed char v = internal->val (other); + if (v < 0) + continue; + assert (!v); + const int repr_other = find_representative (other); + if (!marked (repr_other)) + goto CONTINUE_WITH_NEXT_CLAUSE; + LOG ("subsuming due to %d -> %d", other, repr_other); + } + subsuming = d; + goto FOUND_SUBSUMING; + + CONTINUE_WITH_NEXT_CLAUSE:; + } + } + assert (least_occuring_lit); + +FOUND_SUBSUMING: + for (auto lit : *subsumed) { + const int repr_lit = find_representative (lit); + const signed char v = internal->val (lit); + if (!v) + marked (repr_lit) = 0; + } + if (subsuming) { + LOG (subsumed, "subsumed"); + LOG (subsuming, "subsuming"); + subsume_clause (subsuming, subsumed); + ++internal->stats.congruence.subsumed; + return true; + } else { + internal->occs (least_occuring_lit).push_back (subsumed); + return false; + } +} + +/*------------------------------------------------------------------------*/ +static bool skip_ite_gate (Gate *g) { + assert (g->tag == Gate_Type::ITE_Gate); + if (g->garbage) + return true; + return false; +} + +void Closure::produce_ite_merge_then_else_reasons ( + Gate *g, int src, int dst, std::vector &reasons_implication, + std::vector &reasons_back) { + assert (!g->garbage); + if (!internal->lrat) + return; + check_correct_ite_flags (g); + // no merge is happening actually + 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); + assert (g->rhs.size () == 3); + assert (src == g->rhs[1] || src == g->rhs[2]); + assert (dst == g->rhs[1] || dst == g->rhs[2]); + const int8_t flag = g->degenerated_ite; + assert (!ite_flags_no_then_clauses (flag)); // e = lhs: already merged + assert (!ite_flags_no_else_clauses (flag)); // t = lhs: already merged + produce_rewritten_clause_lrat (g->pos_lhs_ids, g->lhs, false); + if (ite_flags_neg_cond_lhs (flag)) { + LOG ("degenerated case with lhs = -cond"); + LOG (g->pos_lhs_ids[0].clause, "1:"); + LOG (g->pos_lhs_ids[1].clause, "2:"); + reasons_back.push_back (g->pos_lhs_ids[0].clause->id); + reasons_implication.push_back (g->pos_lhs_ids[1].clause->id); + return; + } + if (ite_flags_cond_lhs (flag)) { + LOG ("degenerated case with lhs = cond"); + assert (g->pos_lhs_ids[0].clause); + assert (g->pos_lhs_ids[3].clause); + reasons_back.push_back (g->pos_lhs_ids[3].clause->id); + reasons_implication.push_back (g->pos_lhs_ids[0].clause->id); + return; + } + reasons_implication.push_back (g->pos_lhs_ids[0].clause->id); + reasons_implication.push_back (g->pos_lhs_ids[2].clause->id); + reasons_back.push_back (g->pos_lhs_ids[1].clause->id); + reasons_back.push_back (g->pos_lhs_ids[3].clause->id); +} + +void Closure::rewrite_ite_gate_update_lrat_reasons (Gate *g, int src, + int dst) { + if (!internal->lrat) + return; + LOG (g, "updating lrat from"); + for (auto &litId : g->pos_lhs_ids) { + assert (litId.clause); + if (litId.current_lit == src) + litId.current_lit = dst; + if (litId.current_lit == -src) + litId.current_lit = -dst; + } + check_ite_lrat_reasons (g, false); +} + +bool Closure::rewrite_ite_gate_to_and ( + Gate *g, int src, int dst, size_t idx1, size_t idx2, + int cond_lit_to_learn_if_degenerated) { + assert (internal->lrat_chain.empty ()); + assert (!g->garbage); + LOG (g, "rewriting to proper AND"); + if (internal->val (g->lhs) > 0) { + { + const int lit = g->rhs[0]; + const char v = internal->val (lit); + if (v > 0) { + } else if (!v) { + if (internal->lrat) { + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[idx1].clause, + Rewrite (), lrat_chain); + } + learn_congruence_unit (cond_lit_to_learn_if_degenerated); + } else { + if (internal->lrat) + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[idx1].clause, + Rewrite (), lrat_chain); + push_lrat_unit (-lit); + internal->learn_empty_clause (); + return true; + } + } + if (!internal->unsat) { + const int lit = g->rhs[1]; + const char v = internal->val (lit); + assert (dst == g->rhs[0] || dst == g->rhs[1] || -dst == g->rhs[0] || + -dst == g->rhs[1]); + const int other = (dst == g->rhs[0] || dst == g->rhs[1]) + ? dst ^ g->rhs[0] ^ g->rhs[1] + : (-dst) ^ g->rhs[0] ^ g->rhs[1]; + if (v > 0) { + // already set by propagation + return true; + } else if (!v) { + if (internal->lrat) { + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[idx2].clause, + Rewrite (), lrat_chain); + } + learn_congruence_unit (other); + } else { + if (internal->lrat) { + push_lrat_unit (cond_lit_to_learn_if_degenerated); + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[idx2].clause, + Rewrite (), lrat_chain); + } + internal->learn_empty_clause (); + return true; + } + } + return true; + } + if (!internal->lrat) + return false; + LOG ("updating flags"); + g->degenerated_and_neg = (g->rhs[1] == -g->lhs || g->rhs[0] == -g->lhs); + g->degenerated_and_pos = (g->rhs[0] == g->lhs || g->rhs[1] == g->lhs); + assert (g->rhs.size () == 3); + assert (g->pos_lhs_ids.size () == 4); + assert (idx1 < g->pos_lhs_ids.size ()); + assert (idx2 < g->pos_lhs_ids.size ()); + int lit = g->pos_lhs_ids[idx2].current_lit, other = g->lhs; + // TODO: remove argument + (void) src; + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs, idx1, + idx2, false); + + if ((idx1 == (size_t) -1 || idx2 == (size_t) -1)) { + // degenerated and gate + return false; + } + + assert (idx1 != (size_t) -1); + assert (idx2 != (size_t) -1); + assert (idx1 < g->pos_lhs_ids.size ()); + assert (idx2 < g->pos_lhs_ids.size ()); + Clause *c = g->pos_lhs_ids[idx1].clause; + assert (c->size == 2); + Clause *d = g->pos_lhs_ids[idx2].clause; + assert (c != d); + assert (c); + assert (d); + g->pos_lhs_ids.erase (std::remove_if (begin (g->pos_lhs_ids), + end (g->pos_lhs_ids), + [d] (const LitClausePair &p) { + return p.clause == d || !p.clause; + }), + end (g->pos_lhs_ids)); + assert (g->pos_lhs_ids.size () == 2); + assert (lit); + assert (other); + assert (lit != dst); + assert (other != dst); + assert (lit != other); + lrat_chain.push_back (c->id); + lrat_chain.push_back (d->id); + Clause *e = learn_binary_tmp_or_full_clause (lit, -other); + assert (e); + + auto long_clause = + std::find_if (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), + [] (LitClausePair l) { return l.clause->size == 3; }); + assert (long_clause != end (g->pos_lhs_ids)); + LOG (long_clause->clause, "new long clause"); + g->neg_lhs_ids.push_back (*long_clause); + g->pos_lhs_ids.erase (long_clause); + + assert (g->pos_lhs_ids.size () == 1); + + (void) maybe_promote_tmp_binary_clause (g->pos_lhs_ids[0].clause); + g->pos_lhs_ids.push_back ({lit, e}); +#ifndef NDEBUG + for (auto litId : g->pos_lhs_ids) { + bool found = false; + assert (litId.clause); + for (auto other : *litId.clause) { + found = (find_eager_representative (other) == litId.current_lit); + if (found) + break; + } + assert (found); + } + for (auto id : g->pos_lhs_ids) { + LOG (id.clause, "clause after rewriting:"); + assert (id.clause->size == 2); + } + +#endif + return false; +} + +void Closure::produce_ite_merge_lhs_then_else_reasons ( + Gate *g, std::vector &reasons_implication, + std::vector &reasons_back, std::vector &reasons_unit, + bool rewritting_then, bool &learn_units) { + + const size_t idx1 = rewritting_then ? 0 : 2; + const size_t idx2 = idx1 + 1; + const size_t other_idx1 = rewritting_then ? 2 : 0; + const size_t other_idx2 = other_idx1 + 1; + const int cond_lit = g->rhs[0]; + const int lit_to_merge = g->rhs[rewritting_then ? 2 : 1]; + const int other_lit = g->rhs[rewritting_then ? 1 : 2]; + const int repr_cond_lit = find_eager_representative (g->rhs[0]); + const int repr_lit_to_merge = find_eager_representative (lit_to_merge); + const int repr_other_lit = find_eager_representative (other_lit); + const int repr_lhs = find_eager_representative(g->lhs); + if (!internal->proof) + return; + + + LOG ("cond: %d, merging %d and rewriting to %d", cond_lit, lit_to_merge, + other_lit); + if (internal->lrat) { + assert (internal->lrat); + assert (g->pos_lhs_ids.size () == 4); + + if (repr_lhs == -repr_other_lit) { + LOG ("special case: %s=%s, checking if other: %s %s", LOGLIT (g->lhs), + LOGLIT (-lit_to_merge), LOGLIT (cond_lit), LOGLIT (other_lit)); + assert (repr_lit_to_merge != -repr_lhs); // should have been rewritten before + + if (rewritting_then && repr_cond_lit == repr_lhs) { + LOG ("t=-lhs/c=lhs"); + learn_units = true; + // is a unit + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[0].clause, + Rewrite (), lrat_chain); + unsimplified.push_back (-cond_lit); + LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); + reasons_unit = {id_unit}; + // don't bother finding out which one is used + reasons_implication.push_back (id_unit); + g->pos_lhs_ids[3].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[3].clause, g->lhs, false); + assert (g->pos_lhs_ids[3].clause); + reasons_implication.push_back (g->pos_lhs_ids[3].clause->id); + unsimplified.clear (); + return; + } + if (!rewritting_then && repr_cond_lit == repr_lhs) { + LOG ("e=-lhs/c=lhs"); + learn_units = true; + // is a unit + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[3].clause, + Rewrite (), lrat_chain); + unsimplified.push_back (cond_lit); + LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); + reasons_unit = {id_unit}; + // don't bother finding out which one is used + reasons_implication.push_back (id_unit); + g->pos_lhs_ids[0].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[0].clause, g->lhs, false); + assert (g->pos_lhs_ids[0].clause); + reasons_implication.push_back (g->pos_lhs_ids[0].clause->id); + unsimplified.clear (); + return; + } + if (!rewritting_then && repr_cond_lit == -repr_lhs) { + LOG ("e=-lhs/c=-lhs"); + learn_units = true; + // TODO: this function does not work to produce units for this case + // c LOG 0 rewriting 4 by 3 in gate[42] (arity: 3) -3 := ITE 3 7 + // ... + // c LOG 0 clause[50] 4 -3 + // c LOG 0 clause[44] 5 3 + // c LOG 0 clause[2] -3 -4 -5 + // the first two are rewriting, but they are not ordered properly + // and we need the '5' clause to come after + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[2].clause, + Rewrite (), lrat_chain); + unsimplified.push_back (cond_lit); + LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); + reasons_unit = {id_unit}; + g->pos_lhs_ids[1].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[1].clause, g->lhs, false); + assert (g->pos_lhs_ids[1].clause); + + // don't bother finding out which one is used + reasons_implication.push_back (id_unit); + reasons_implication.push_back (g->pos_lhs_ids[1].clause->id); + unsimplified.clear (); + return; + } + if (rewritting_then && repr_cond_lit == -repr_lhs) { + LOG ("t=-lhs/c=-lhs"); + learn_units = true; + push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[1].clause, + Rewrite (), lrat_chain); + unsimplified.push_back (-cond_lit); + LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); + reasons_unit = {id_unit}; + g->pos_lhs_ids[2].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[2].clause, g->lhs, false); + assert (g->pos_lhs_ids[2].clause); + + reasons_implication.push_back (id_unit); + reasons_implication.push_back (g->pos_lhs_ids[2].clause->id); + unsimplified.clear (); + return; + } + if (rewritting_then && repr_lit_to_merge == repr_lhs) { + LOG ("t=-lhs/e=lhs from rewriting then"); + learn_units = true; + g->pos_lhs_ids[idx1].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[idx1].clause, g->lhs, false); + g->pos_lhs_ids[idx2].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[idx2].clause, g->lhs, false); + assert (g->pos_lhs_ids[idx1].clause); + assert (g->pos_lhs_ids[idx2].clause); + lrat_chain.push_back (g->pos_lhs_ids[idx1].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[idx2].clause->id); + unsimplified.push_back (-cond_lit); + LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); + reasons_unit = {id_unit}; + unsimplified.clear (); + + return; + } + if (!rewritting_then && repr_lit_to_merge == repr_lhs) { + LOG ("t=-lhs/e=lhs from rewriting else"); + learn_units = true; + g->pos_lhs_ids[idx1].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[idx1].clause, g->lhs, false); + g->pos_lhs_ids[idx2].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[idx2].clause, g->lhs, false); + assert (g->pos_lhs_ids[idx1].clause); + assert (g->pos_lhs_ids[idx2].clause); + lrat_chain.push_back (g->pos_lhs_ids[idx1].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[idx2].clause->id); + unsimplified.push_back (cond_lit); + LRAT_ID id_unit = simplify_and_add_to_proof_chain (unsimplified); + reasons_unit = {id_unit}; + unsimplified.clear (); + return; + } + + if (other_lit == repr_lhs) { + LOG ("TODO FIX ME t=-lhs/e=lhs"); + learn_units = true; + // in the other direction we are merging a literal with itself + g->pos_lhs_ids[idx1].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[idx1].clause, g->lhs, false); + g->pos_lhs_ids[idx2].clause = produce_rewritten_clause_lrat ( + g->pos_lhs_ids[idx2].clause, g->lhs, false); + assert (g->pos_lhs_ids[idx1].clause); + assert (g->pos_lhs_ids[idx2].clause); + reasons_unit.push_back (g->pos_lhs_ids[idx1].clause->id); + reasons_unit.push_back (g->pos_lhs_ids[idx2].clause->id); + return; + } + // if (other_lit == -g->lhs) { + // assert (false); + // } + // if (other_lit == g->lhs) { + // assert (false); + // } + } + + LOG ("normal path"); + produce_rewritten_clause_lrat (g->pos_lhs_ids, g->lhs, false); + assert (g->pos_lhs_ids.size () == 4); + + reasons_unit.push_back (g->pos_lhs_ids[idx1].clause->id); + reasons_unit.push_back (g->pos_lhs_ids[idx2].clause->id); + + // already merged: only unit is important + if (!rewritting_then && repr_lhs == repr_lit_to_merge) { + return; + } + + reasons_implication.push_back (g->pos_lhs_ids[other_idx1].clause->id); + reasons_implication.push_back (g->pos_lhs_ids[idx1].clause->id); + reasons_implication.push_back (g->pos_lhs_ids[idx2].clause->id); + + reasons_back.push_back (g->pos_lhs_ids[other_idx2].clause->id); + reasons_back.push_back (g->pos_lhs_ids[idx1].clause->id); + reasons_back.push_back (g->pos_lhs_ids[idx2].clause->id); + } else { + LOG ("learn extra clauses XXXXXXXXXXXXXXXXXXXXXXXXX"); + const int lhs = g->lhs; + const int cond = g->rhs[0]; + if (rewritting_then) { + unsimplified.push_back (-cond); + unsimplified.push_back (lhs); + simplify_and_add_to_proof_chain (unsimplified); + unsimplified.push_back (-cond); + unsimplified.push_back (-lhs); + simplify_and_add_to_proof_chain (unsimplified); + } else { + unsimplified.push_back (cond); + unsimplified.push_back (-lhs); + simplify_and_add_to_proof_chain (unsimplified); + unsimplified.push_back (cond); + unsimplified.push_back (lhs); + simplify_and_add_to_proof_chain (unsimplified); + } + unsimplified.clear (); + } +} + +void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { + assert (unsimplified.empty ()); + if (skip_ite_gate (g)) + return; + if (!gate_contains (g, src)) + return; + LOG (g, "rewriting %d by %d in", src, dst); + assert (!g->shrunken); + assert (g->rhs.size () == 3); + assert (!internal->lrat || g->pos_lhs_ids.size () == 4); + auto &rhs = g->rhs; + const int lhs = g->lhs; + const int cond = g->rhs[0]; + const int then_lit = g->rhs[1]; + const int else_lit = g->rhs[2]; + const int not_lhs = -(lhs); + const int not_dst = -(dst); + const int not_cond = -(cond); + const int not_then_lit = -(then_lit); + const int not_else_lit = -(else_lit); + Gate_Type new_tag = Gate_Type::And_Gate; + + bool garbage = false; + bool shrink = true; + const auto git = g->indexed ? table.find (g) : end (table); + assert (!g->indexed || git != end (table)); + assert (*git == g); + if (internal->val (cond) && internal->val (then_lit) && + internal->val (else_lit)) { // propagation has set all value anyway + LOG (g, "all values are set"); + assert (internal->val (g->lhs)); + garbage = true; + } else if (internal->val (g->lhs) && internal->val (cond)) { + LOG (g, "all values are set 2"); + assert (internal->val (g->lhs)); + garbage = true; + } + // this code is taken one-to-one from kissat + else if (src == cond) { + if (dst == then_lit) { + // then_lit ? then_lit : else_lit + // then_lit & then_lit | !then_lit & else_lit + // then_lit | !then_lit & else_lit + // then_lit | else_lit + // !(!then_lit & !else_lit) + g->lhs = not_lhs; + rhs[0] = not_then_lit; + rhs[1] = not_else_lit; + if (then_lit == lhs || else_lit == lhs) + garbage = true; + else + garbage = rewrite_ite_gate_to_and (g, src, dst, 1, 3, -dst); + } else if (not_dst == then_lit) { + // !then_lit ? then_lit : else_lit + // !then_lit & then_lit | then_lit & else_lit + // then_lit & else_lit + rhs[0] = else_lit; + assert (rhs[1] == then_lit); + garbage = rewrite_ite_gate_to_and (g, src, dst, 0, 2, -dst); + } else if (dst == else_lit) { + // else_list ? then_lit : else_lit + // else_list & then_lit | !else_list & else_lit + // else_list & then_lit + rhs[0] = else_lit; + assert (rhs[1] == then_lit); + garbage = rewrite_ite_gate_to_and (g, src, dst, 2, 0, dst); + } else if (not_dst == else_lit) { + // !else_list ? then_lit : else_lit + // !else_list & then_lit | else_lit & else_lit + // !else_list & then_lit | else_lit + // then_lit | else_lit + // !(!then_lit & !else_lit) + g->lhs = not_lhs; + rhs[0] = not_then_lit; + rhs[1] = not_else_lit; + if (then_lit == lhs || else_lit == lhs) + garbage = true; + else + garbage = rewrite_ite_gate_to_and (g, src, dst, 3, 1, dst); + } else { + shrink = false; + rhs[0] = dst; + rewrite_ite_gate_update_lrat_reasons (g, src, dst); + } + } else if (src == then_lit) { + if (not_dst == g->lhs) { // TODO not in kissat + rhs[1] = dst; + check_ite_implied (g->lhs, cond, then_lit, else_lit); + std::vector reasons_implication, reasons_back, reasons_unit; + LOG ("%d = %d ?", g->lhs, -g->rhs[0]); + bool learn_units_instead_of_equivalence = false; + produce_ite_merge_lhs_then_else_reasons ( + g, reasons_implication, reasons_back, reasons_unit, true, + learn_units_instead_of_equivalence); + if (learn_units_instead_of_equivalence) { // it is too hard to produce + // LRAT chains + // in this case + + if (internal->lrat) + lrat_chain = reasons_unit; + learn_congruence_unit (-cond, true); + if (-else_lit == lhs) { + if (internal->lrat) + lrat_chain = reasons_implication; + learn_congruence_unit (cond == -lhs ? -else_lit : else_lit, false, true); + } else fully_propagate (); + } else { + if (merge_literals_lrat (g->lhs, else_lit, reasons_implication, + reasons_back)) { + ++internal->stats.congruence.unaries; + ++internal->stats.congruence.unary_ites; + } + if (!internal->unsat) { + if (internal->lrat) + lrat_chain = reasons_unit; + learn_congruence_unit (-cond); + } + } + delete_proof_chain (); + garbage = true; + } else if (dst == cond) { + // cond ? cond : else_lit + // cond & cond | !cond & else_lit + // cond | !cond & else_lit + // cond | else_lit + // !(!cond & !else_lit) + g->lhs = not_lhs; + rhs[0] = not_cond; + rhs[1] = not_else_lit; + if (else_lit == lhs || cond == lhs) + garbage = true; + else + garbage = rewrite_ite_gate_to_and (g, src, dst, 1, 3, -cond); + } else if (not_dst == cond) { + // cond ? !cond : else_lit + // cond & !cond | !cond & else_lit + // !cond & else_lit + rhs[0] = not_cond; + rhs[1] = else_lit; + garbage = rewrite_ite_gate_to_and (g, src, dst, 0, 2, -cond); + } else if (dst == else_lit) { + // cond ? else_lit : else_lit + // else_lit + std::vector reasons_implication, reasons_back; + produce_ite_merge_then_else_reasons (g, src, dst, reasons_implication, + reasons_back); + if (merge_literals_lrat (lhs, else_lit, reasons_implication, + reasons_back)) { + ++internal->stats.congruence.unaries; + ++internal->stats.congruence.unary_ites; + } + delete_proof_chain (); + garbage = true; + } else if (not_dst == else_lit) { + // cond ? !else_lit : else_lit + // cond & !else_lit | !cond & else_lit + // cond ^ else_lit + if (g->lhs == cond) { + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs, + false); + if (internal->lrat) { + assert (g->pos_lhs_ids.size () == 2); + lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); + } + learn_congruence_unit (-else_lit); + garbage = true; + } else { + LOG ("changing to xor"); + new_tag = Gate_Type::XOr_Gate; + assert (rhs[0] == cond); + rhs[1] = else_lit; + assert (!internal->lrat || !g->pos_lhs_ids.empty ()); + { +#ifdef LOGGING + for (auto litId : g->pos_lhs_ids) { + LOG (litId.clause, "%d ->", litId.current_lit); + } +#endif + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs, + true); +#ifdef LOGGING + for (auto litId : g->pos_lhs_ids) { + LOG (litId.clause, "%d ->", litId.current_lit); + } +#endif + } + } + } else { + shrink = false; + rhs[1] = dst; + rewrite_ite_gate_update_lrat_reasons (g, src, dst); + } + } else { + assert (src == else_lit); + if (not_dst == g->lhs) { // TODO not in kissat + rhs[2] = dst; + std::vector reasons_implication, reasons_back, reasons_unit; + bool learn_units_instead_of_equivalence = false; + produce_ite_merge_lhs_then_else_reasons ( + g, reasons_implication, reasons_back, reasons_unit, false, + learn_units_instead_of_equivalence); + if (learn_units_instead_of_equivalence) { // Too hard to produce LRAT + if (internal->lrat) + lrat_chain = reasons_unit; + learn_congruence_unit (cond, true); + if (then_lit != lhs) { + LOG ("special case, learning %d",cond == -lhs ? -then_lit : then_lit); + if (internal->lrat) + lrat_chain = reasons_implication; + learn_congruence_unit (cond == -lhs ? -then_lit : then_lit, false, true); + } else fully_propagate (); + } else { + if (merge_literals_lrat (lhs, then_lit, reasons_implication, + reasons_back)) { + ++internal->stats.congruence.unaries; + ++internal->stats.congruence.unary_ites; + } + if (!internal->unsat) { + if (internal->lrat) + lrat_chain = reasons_unit; + learn_congruence_unit (cond); + } + } + delete_proof_chain (); + garbage = true; + } else if (dst == cond) { + // cond ? then_lit : cond + // cond & then_lit | !cond & cond + // cond & then_lit + assert (rhs[0] == cond); + assert (rhs[1] == then_lit); + garbage = rewrite_ite_gate_to_and (g, src, dst, 2, 0, cond); + } else if (not_dst == cond) { + // cond ? then_lit : !cond + // cond & then_lit | !cond & !cond + // cond & then_lit | !cond + // then_lit | !cond + // !(!then_lit & cond) + g->lhs = not_lhs; + assert (rhs[0] == cond); + rhs[1] = not_then_lit; + if (then_lit == lhs || cond == lhs) + garbage = true; + else + garbage = rewrite_ite_gate_to_and (g, src, dst, 3, 1, cond); + } else if (dst == then_lit) { + // cond ? then_lit : then_lit + // then_lit + std::vector reasons_implication, reasons_back; + produce_ite_merge_then_else_reasons (g, src, dst, reasons_implication, + reasons_back); + if (merge_literals_lrat (lhs, then_lit, reasons_implication, + reasons_back)) { + ++internal->stats.congruence.unaries; + ++internal->stats.congruence.unary_ites; + } + garbage = true; + } else if (not_dst == then_lit) { + // cond ? then_lit : !then_lit + // cond & then_lit | !cond & !then_lit + // !(cond ^ then_lit) + if (lhs == cond) { + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, + false); + if (internal->lrat) { + assert (g->pos_lhs_ids.size () == 2); + lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); + } + learn_congruence_unit (then_lit); + garbage = true; + } else if (not_lhs == cond) { + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, + false); + if (internal->lrat) { + assert (g->pos_lhs_ids.size () == 2); + lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); + } + learn_congruence_unit (-then_lit); + garbage = true; + } else if (not_lhs == then_lit) { + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, + false); + if (internal->lrat) { + assert (g->pos_lhs_ids.size () == 2); + lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); + } + learn_congruence_unit (cond); + garbage = true; + } else { + new_tag = Gate_Type::XOr_Gate; + g->lhs = not_lhs; + assert (rhs[0] == cond); + assert (rhs[1] == then_lit); + assert (rhs[0] != g->lhs); + assert (rhs[1] != g->lhs); + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, + false); + } + } else { + shrink = false; + rhs[2] = dst; + rewrite_ite_gate_update_lrat_reasons (g, src, dst); + } + } + if (!garbage && !internal->unsat) { + assert (new_tag != Gate_Type::ITE_Gate || g->lhs != -rhs[1]); + assert (new_tag != Gate_Type::ITE_Gate || g->lhs != -rhs[2]); + if (shrink) { + if (new_tag == Gate_Type::XOr_Gate) { + bool negate_lhs = false; + if (rhs[0] < 0) { + rhs[0] = -rhs[0]; + negate_lhs = !negate_lhs; + } + if (rhs[1] < 0) { + rhs[1] = -rhs[1]; + negate_lhs = !negate_lhs; + } + if (negate_lhs) + g->lhs = -g->lhs; + } + if (internal->vlit (rhs[0]) > + internal->vlit ( + rhs[1])) { // unlike kissat, we need to do it after negating + std::swap (rhs[0], rhs[1]); + assert (new_tag != Gate_Type::ITE_Gate); + } + assert (internal->vlit (rhs[0]) < internal->vlit (rhs[1])); + assert (!g->shrunken); + g->shrunken = true; + rhs[2] = 0; + g->tag = new_tag; + rhs.resize (2); + assert (rhs[0] != -rhs[1]); + if (new_tag == Gate_Type::XOr_Gate) { + if (rhs[0] == -g->lhs || rhs[1] == -g->lhs) { + LOG (g, "special XOR:"); + const int unit = rhs[0] ^ -g->lhs ^ rhs[1]; + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, + false); + if (internal->lrat) { + assert (g->pos_lhs_ids.size () == 2); + lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); + } + learn_congruence_unit (unit); + garbage = true; + } else if (rhs[0] == g->lhs || rhs[1] == g->lhs) { + LOG (g, "special XOR:"); + const int unit = rhs[0] ^ g->lhs ^ rhs[1]; + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, + false); + if (internal->lrat) { + assert (g->pos_lhs_ids.size () == 2); + lrat_chain.push_back (g->pos_lhs_ids[0].clause->id); + lrat_chain.push_back (g->pos_lhs_ids[1].clause->id); + } + learn_congruence_unit (-unit); + garbage = true; + } else { + int i = 0; + bool negated = false; + for (int j = 0; j < 2; ++i, ++j) { + assert (i <= j); + const int lit = rhs[i] = rhs[j]; + const char v = internal->val (lit); + if (v > 0) { + --i; + negated = !negated; + } else if (v < 0) { + --i; + } + } + assert (i <= 2); + rhs.resize (i); + if (negated) { + g->lhs = -g->lhs; + } + if (i != 2) { + LOG (g, "removed units"); + } + if (!i) + garbage = true; + else if (i == 1) { +#ifdef LOGGING + for (auto litId : g->pos_lhs_ids) { + LOG (litId.clause, "%d ->", litId.current_lit); + } +#endif + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs); + assert (!internal->lrat || g->pos_lhs_ids.size () == 2); + Clause *c1 = nullptr, *c2 = nullptr; + if (internal->lrat) { + assert (g->pos_lhs_ids[0].clause); + bool rhs_as_src_first = + g->pos_lhs_ids[0].clause->literals[0] == g->lhs || + g->pos_lhs_ids[0].clause->literals[1] == g->lhs; + c1 = (rhs_as_src_first ? g->pos_lhs_ids[0].clause + : g->pos_lhs_ids[1].clause); + c2 = (rhs_as_src_first ? g->pos_lhs_ids[1].clause + : g->pos_lhs_ids[0].clause); + c1 = maybe_promote_tmp_binary_clause (c1); + c2 = maybe_promote_tmp_binary_clause (c2); + } else { + maybe_add_binary_clause (-g->lhs, g->rhs[0]); + maybe_add_binary_clause (g->lhs, -g->rhs[0]); + } + merge_literals_equivalence (g->lhs, g->rhs[0], c1, c2); + garbage = true; + } + } + if (!garbage) { + assert (rhs[0] != g->lhs); + assert (rhs[1] != g->lhs); + assert (rhs[0] != -g->lhs); + assert (rhs[1] != -g->lhs); + } + } + + if (!garbage) { + g->hash = hash_lits (nonces, g->rhs); + LOG (g, "rewritten"); + + if (internal->lrat) { + if (new_tag == Gate_Type::XOr_Gate) { +#ifndef NDEBUG + std::for_each (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), + [g] (LitClausePair l) { + assert ((size_t) l.clause->size == + 1 + g->arity ()); + }); +#endif + } else if (new_tag == Gate_Type::And_Gate) { + // we have to get rid of one clause, two become binaries, and + // becomes ternary + +#if defined(LOGGING) || !defined(NDEBUG) + for (auto id : g->pos_lhs_ids) { + LOG (id.clause, "clause after rewriting:"); + assert (id.clause->size == 2); + } +#endif + assert (g->pos_lhs_ids.size () == 2 || + gate_contains (g, g->lhs)); + assert (g->neg_lhs_ids.size () == 1 || + gate_contains (g, g->lhs)); + assert (g->arity () == 2); +#ifndef NDEBUG + std::for_each ( + begin (g->pos_lhs_ids), end (g->pos_lhs_ids), + [] (LitClausePair l) { assert (l.clause->size == 2); }); +#endif + } else { + assert (false); + __builtin_unreachable (); + } + } + Gate *h; + if (new_tag == Gate_Type::And_Gate) { + check_and_gate_implied (g); + h = find_and_lits (rhs); + } else { + assert (new_tag == Gate_Type::XOr_Gate); + check_xor_gate_implied (g); + h = find_xor_gate (g); + } + if (h) { + garbage = true; + if (new_tag == Gate_Type::XOr_Gate) { + std::vector reasons_implication, reasons_back; + add_xor_matching_proof_chain (g, g->lhs, h->pos_lhs_ids, h->lhs, + reasons_implication, + reasons_back); + if (merge_literals_lrat (g->lhs, h->lhs, reasons_implication, + reasons_back)) + ++internal->stats.congruence.xors; + } else { + add_ite_turned_and_binary_clauses (g); + std::vector reasons_implication, reasons_back; + if (internal->lrat) + merge_and_gate_lrat_produce_lrat (g, h, reasons_implication, + reasons_back, false); + if (merge_literals_lrat (g->lhs, h->lhs, reasons_implication, + reasons_back)) + ++internal->stats.congruence.ands; + } + delete_proof_chain (); + } else { + garbage = false; + if (g->indexed) + remove_gate (git); + index_gate (g); + assert (g->arity () == 2); + for (auto lit : g->rhs) + if (lit != dst) + if (lit != cond && lit != then_lit && lit != else_lit) + connect_goccs (g, lit); + if (g->tag == Gate_Type::And_Gate && !internal->lrat) + for (auto lit : g->rhs) + maybe_add_binary_clause (-g->lhs, lit); + } + } + } else { + LOG (g, "rewritten"); + if (internal->lrat) + update_ite_flags (g), check_correct_ite_flags(g); + assert (rhs[0] != rhs[1]); + assert (rhs[0] != rhs[2]); + assert (rhs[1] != rhs[2]); + assert (rhs[0] != -(rhs[1])); + assert (rhs[0] != -(rhs[2])); + assert (rhs[1] != -(rhs[2])); + check_ite_gate_implied (g); + check_ite_lrat_reasons (g, false); + bool negate_lhs; + Gate *h = find_ite_gate (g, negate_lhs); + assert (lhs == g->lhs); + assert (not_lhs == -(g->lhs)); + if (negate_lhs) + g->lhs = -lhs; + check_ite_lrat_reasons (g); + if (internal->lrat) + check_correct_ite_flags (g); + if (h) { + garbage = true; + check_ite_gate_implied (g); + check_ite_lrat_reasons (g, false); + check_ite_gate_implied (h); + check_ite_lrat_reasons (h, false); + int normalized_lhs = negate_lhs ? not_lhs : lhs; + std::vector extra_reasons_lit, extra_reasons_ulit; + add_ite_matching_proof_chain (g, h, normalized_lhs, h->lhs, + extra_reasons_lit, + extra_reasons_ulit); + if (merge_literals_lrat (normalized_lhs, h->lhs, extra_reasons_lit, + extra_reasons_ulit)) + ++internal->stats.congruence.ites; + delete_proof_chain (); + assert (internal->unsat || chain.empty ()); + } else { + garbage = false; + if (g->indexed) + remove_gate (git); + LOG (g, "normalized"); + g->hash = hash_lits (nonces, g->rhs); + index_gate (g); + assert (g->arity () == 3); + for (auto lit : g->rhs) + if (lit != dst) + if (lit != cond && lit != then_lit && lit != else_lit) + connect_goccs (g, lit); + } + } + } + if (garbage && !internal->unsat) + mark_garbage (g); + + assert (chain.empty ()); +} + +void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, + size_t idx1, + size_t idx2) { + if (!internal->lrat) + return; + // TODO + if (internal->val (lit) > 0) + return; + assert (internal->lrat); + assert (g); + assert (idx1 < g->pos_lhs_ids.size ()); + assert (idx2 < g->pos_lhs_ids.size ()); + assert (g->pos_lhs_ids.size () == 4); + + assert (idx1 != idx2); + Clause *c = g->pos_lhs_ids[idx1].clause; + Clause *d = g->pos_lhs_ids[idx2].clause; + + if (g->lhs == -g->rhs[0]) { + LOG ("special case of LHS=-cond where only one clause in LRAT is needed is needed"); + size_t idx = (internal->val (g->rhs[1]) < 0 ? idx2 : idx1); + c = produce_rewritten_clause_lrat (g->pos_lhs_ids[idx].clause, g->lhs, false, false); + assert (c); + // not possible to do this in a single lrat chain + push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, + Rewrite (), g->lhs); + assert (d); + return; + } + if (g->lhs == g->rhs[0]) { + LOG ("special case of LHS=cond where only one clause in LRAT is needed is needed"); + size_t idx = (internal->val (g->rhs[1]) > 0 ? idx2 : idx1); + c = produce_rewritten_clause_lrat (g->pos_lhs_ids[idx].clause, g->lhs, false, false); + assert (c); + // not possible to do this in a single lrat chain + push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, + Rewrite (), g->lhs); + assert (d); + return; + } + + c = produce_rewritten_clause_lrat (c, g->lhs, true); + if (c) { + lrat_chain.push_back (c->id); + d = produce_rewritten_clause_lrat (d, g->lhs, true); + if (d) + lrat_chain.push_back (d->id); + } else if (!c) { + push_id_and_rewriting_lrat_unit (d, Rewrite (), lrat_chain, true, + Rewrite (), g->lhs); + assert (d); + lrat_chain.push_back (d->id); + } +} + +// TODO merge +void Closure::merge_and_gate_lrat_produce_lrat ( + Gate *g, Gate *h, std::vector &reasons_lrat_src, + std::vector &reasons_lrat_usrc, bool remove_units) { + assert (internal->lrat); + assert (g->tag == Gate_Type::And_Gate); + assert (h->tag == Gate_Type::And_Gate); + assert (g->neg_lhs_ids.size () <= 1); + update_and_gate_build_lrat_chain (g, h, reasons_lrat_src, + reasons_lrat_usrc, remove_units); +} + +// odd copy of rewrite_ite_gate_lrat_and +bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, + int removed_lit) { + assert (internal->lrat_chain.empty ()); + assert (g->rhs.size () == 3); +#ifdef LOGGING + for (auto litId : g->pos_lhs_ids) { + LOG (litId.clause, "%d ->", litId.current_lit); + } +#endif + if (g->lhs == -g->rhs[0] || g->lhs == -g->rhs[1]) { + if (internal->lrat) { + Clause *c = g->pos_lhs_ids[idx1].clause; + push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain); + assert (!lrat_chain.empty ()); + } + learn_congruence_unit (-g->lhs); + return true; + } + if (!internal->lrat) + return false; + g->degenerated_and_neg = (g->degenerated_and_neg || g->rhs[1] == -g->lhs || g->rhs[0] == -g->lhs); + g->degenerated_and_pos = (g->degenerated_and_pos || g->rhs[0] == g->lhs || g->rhs[1] == g->lhs); + + assert (g->pos_lhs_ids.size () == 4); + assert (idx1 < g->pos_lhs_ids.size ()); + assert (idx2 < g->pos_lhs_ids.size ()); + int lit = g->pos_lhs_ids[idx2].current_lit, other = g->lhs; + size_t new_idx1 = idx1; + size_t new_idx2 = idx2; + produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs, new_idx1, + new_idx2); + + if (g->pos_lhs_ids.size () == 1) { + LOG (g, "degenerated AND gate"); + const int replacement_lit = (g->rhs[1] == g->lhs ? g->rhs[0] : g->rhs[1]); + for (auto &litId : g->pos_lhs_ids) { + assert (litId.clause); + LOG (litId.clause, "%d ->", litId.current_lit); + if (litId.current_lit == removed_lit) + litId.current_lit = -replacement_lit; + if (litId.current_lit == -removed_lit) + litId.current_lit = replacement_lit; + LOG (litId.clause, "%d ->", litId.current_lit); + // TODO we need a replacement index + assert (std::find (begin (*litId.clause), end (*litId.clause), litId.current_lit) != + end (*litId.clause)); + assert (std::find (begin (g->rhs), end (g->rhs), litId.current_lit) != + end (g->rhs)); + } + return false; + } + assert (new_idx1 < g->pos_lhs_ids.size ()); + assert (new_idx2 < g->pos_lhs_ids.size ()); + Clause *c = g->pos_lhs_ids[new_idx1].clause; + assert (c->size == 2); + assert (new_idx1 != new_idx2); + Clause *d = g->pos_lhs_ids[new_idx2].clause; + assert (c != d); + assert (c); + assert (d); + g->pos_lhs_ids.erase (std::remove_if (begin (g->pos_lhs_ids), + end (g->pos_lhs_ids), + [d] (const LitClausePair &p) { + return p.clause == d; + }), + end (g->pos_lhs_ids)); + assert (g->pos_lhs_ids.size () == 2); + assert (lit); + assert (other); + assert (lit != other); + lrat_chain.push_back (c->id); + lrat_chain.push_back (d->id); + Clause *e = add_tmp_binary_clause (lit, -other); + + auto long_clause = + std::find_if (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), + [] (LitClausePair l) { return l.clause->size == 3; }); + assert (long_clause != end (g->pos_lhs_ids)); + LOG (long_clause->clause, "new long clause"); + g->neg_lhs_ids.push_back (*long_clause); + g->pos_lhs_ids.erase (long_clause); + assert (g->pos_lhs_ids.size () == 1); + g->pos_lhs_ids.push_back ({lit, e}); + + const int first_lit = g->rhs[0]; + const int second_lit = g->rhs[1]; + for (auto &litId : g->pos_lhs_ids) { + assert (litId.clause); + LOG (litId.clause, "%s ->", LOGLIT (litId.current_lit)); + if (internal->val (litId.current_lit)) { + assert (litId.clause->size == 2); + int replacement_lit = 0; + for (int i = 0; i < 2; ++i) { + if (litId.clause->literals[i] == first_lit) { + replacement_lit = first_lit; + break; + } else if (litId.clause->literals[i] == second_lit) { + replacement_lit = second_lit; + break; + } + } + assert (replacement_lit); + + litId.current_lit = replacement_lit; + } else if (litId.current_lit == removed_lit) + litId.current_lit = -g->rhs[0]; + else if (litId.current_lit == -removed_lit) + litId.current_lit = g->rhs[0]; + LOG (litId.clause, "%d ->", litId.current_lit); + // TODO we need a replacement index + assert (std::find (begin (g->rhs), end (g->rhs), litId.current_lit) != + end (g->rhs)); + assert (std::find (begin (*litId.clause), end (*litId.clause), + litId.current_lit) != end (*litId.clause)); + } + return false; +} + +void Closure::merge_ite_gate_same_then_else_lrat ( + std::vector &clauses, + std::vector &reasons_implication, + std::vector &reasons_back) { + assert (clauses.size () == 4); + produce_rewritten_clause_lrat_and_clean (clauses); + assert (clauses.size () == 4); + auto then_imp = clauses[0]; + auto neg_then_imp = clauses[1]; + auto else_imp = clauses[2]; + auto neg_else_imp = clauses[3]; + reasons_implication.push_back (then_imp.clause->id); + reasons_implication.push_back (else_imp.clause->id); + reasons_back.push_back (neg_then_imp.clause->id); + reasons_back.push_back (neg_else_imp.clause->id); +} + +void Closure::simplify_ite_gate_then_else_set ( + Gate *g, std::vector &reasons_implication, + std::vector &reasons_back, size_t idx1, size_t idx2) { + assert (idx1 < g->pos_lhs_ids.size ()); + assert (idx2 < g->pos_lhs_ids.size ()); + Clause *c = g->pos_lhs_ids[idx1].clause; + Clause *d = g->pos_lhs_ids[idx2].clause; + push_id_and_rewriting_lrat_unit (c, Rewrite (), reasons_back, true, + Rewrite (), g->lhs); + push_id_and_rewriting_lrat_unit (d, Rewrite (), reasons_implication, true, + Rewrite (), g->lhs); + LOG (reasons_back, "LRAT"); + LOG (reasons_implication, "LRAT"); + // c = produce_rewritten_clause_lrat (c, g->lhs, false); + // assert (c); + // d = produce_rewritten_clause_lrat (d, g->lhs, false); + // assert (d); + // const int cond = g->rhs[0]; + // assert (internal->val (cond)); + // reasons_implication.push_back(internal->unit_id (internal->val (cond) > + // 0 ? cond : -cond)); reasons_implication.push_back(c->id); + // reasons_back.push_back(internal->unit_id (internal->val (cond) > 0 ? + // cond : -cond)); reasons_back.push_back(d->id); +} + +void Closure::simplify_ite_gate_condition_set ( + Gate *g, std::vector &reasons_lrat, + std::vector &reasons_back_lrat, size_t idx1, size_t idx2) { + assert (internal->lrat); + Clause *c = g->pos_lhs_ids[idx1].clause; + Clause *d = g->pos_lhs_ids[idx2].clause; +#if defined(LOGGING) || !defined(NDEBUG) + const int cond = g->rhs[0]; + assert (internal->val (cond)); + LOG ("cond = %d", cond); +#endif +#ifdef LOGGING + for (auto litid : g->pos_lhs_ids) + LOG (litid.clause, "clause in gate:"); +#endif + push_id_and_rewriting_lrat_unit (c, Rewrite (), reasons_lrat, true, + Rewrite (), -g->lhs); + push_id_and_rewriting_lrat_unit (d, Rewrite (), reasons_back_lrat, true, + Rewrite (), g->lhs); +} + +void Closure::simplify_ite_gate (Gate *g) { + if (skip_ite_gate (g)) + return; + LOG (g, "simplifying"); + assert (g->arity () == 3); + bool garbage = true; + int lhs = g->lhs; + auto &rhs = g->rhs; + const int cond = rhs[0]; + const int then_lit = rhs[1]; + const int else_lit = rhs[2]; + const signed char v_cond = internal->val (cond); + const signed char v_else = internal->val (else_lit); + const signed char v_then = internal->val (then_lit); + std::vector reasons_lrat, reasons_back_lrat; + if (v_cond && v_else && v_then) { // propagation has set all value anyway + LOG (g, "all values are set"); + assert (internal->val (g->lhs)); + garbage = true; + } else if (v_cond > 0) { + if (internal->lrat) + simplify_ite_gate_condition_set (g, reasons_lrat, reasons_back_lrat, + 0, 1); + if (merge_literals_lrat (lhs, then_lit, reasons_lrat, + reasons_back_lrat)) { + ++internal->stats.congruence.unary_ites; + ++internal->stats.congruence.unaries; + } + } else if (v_cond < 0) { + if (internal->lrat) + simplify_ite_gate_condition_set (g, reasons_lrat, reasons_back_lrat, + 2, 3); + if (merge_literals_lrat (lhs, else_lit, reasons_lrat, + reasons_back_lrat)) { + ++internal->stats.congruence.unary_ites; + ++internal->stats.congruence.unaries; + } + } else { + LOG ("then %d: %d; else %d: %d", then_lit, v_then, else_lit, v_else); + std::vector extra_reasons, extra_reasons_back; + assert (v_then || v_else); + if (v_then > 0 && v_else > 0) { + simplify_ite_gate_produce_unit_lrat (g, lhs, 1, 3); + learn_congruence_unit (lhs); + } else if (v_then < 0 && v_else < 0) { + simplify_ite_gate_produce_unit_lrat (g, -lhs, 0, 2); + learn_congruence_unit (-lhs); + } else if (v_then > 0 && v_else < 0) { + if (internal->lrat) + simplify_ite_gate_then_else_set (g, extra_reasons, + extra_reasons_back, 1, 2); + if (merge_literals_lrat (lhs, cond, extra_reasons, + extra_reasons_back)) { + ++internal->stats.congruence.unary_ites; + ++internal->stats.congruence.unaries; + } + } else if (v_then < 0 && v_else > 0) { + if (internal->lrat) + simplify_ite_gate_then_else_set (g, extra_reasons, + extra_reasons_back, 0, 3); + if (merge_literals_lrat (lhs, -cond, extra_reasons_back, + extra_reasons)) { + ++internal->stats.congruence.unary_ites; + ++internal->stats.congruence.unaries; + } + } else { + assert (!!v_then + !!v_else == 1); + auto git = g->indexed ? table.find (g) : end (table); + assert (!g->indexed || git != end (table)); + if (v_then > 0) { + g->lhs = -lhs; + rhs[0] = -cond; + rhs[1] = -else_lit; + simplify_ite_gate_to_and (g, 1, 3, then_lit); + } else if (v_then < 0) { + rhs[0] = -cond; + rhs[1] = else_lit; + simplify_ite_gate_to_and (g, 0, 2, -then_lit); + } else if (v_else > 0) { + g->lhs = -lhs; + rhs[0] = -then_lit; + rhs[1] = cond; + simplify_ite_gate_to_and (g, 3, 1, else_lit); + } else { + assert (v_else < 0); + rhs[0] = then_lit; + rhs[1] = cond; + simplify_ite_gate_to_and (g, 2, 0, -else_lit); + } + if (internal->unsat) + return; + if (internal->vlit (rhs[0]) > internal->vlit (rhs[1])) + std::swap (rhs[0], rhs[1]); + g->shrunken = true; + g->tag = Gate_Type::And_Gate; + rhs.resize (2); + 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) { + assert (garbage); + std::vector reasons_lrat, reasons_lrat_back; + if (internal->lrat) + merge_and_gate_lrat_produce_lrat (g, h, reasons_lrat, + reasons_lrat_back, false); + if (merge_literals_lrat (g->lhs, h->lhs, reasons_lrat, + reasons_lrat_back)) { + ++internal->stats.congruence.ites; + } + } else { + remove_gate (git); + index_gate (g); + garbage = false; + g->hash = hash_lits (nonces, g->rhs); + for (auto lit : rhs) + if (lit != cond && lit != then_lit && lit != else_lit) { + connect_goccs (g, lit); + } + + if (rhs[0] == -g->lhs || rhs[1] == -g->lhs) + simplify_and_gate ( + g); // TODO Kissat does not do that, but it has also no + // checks to verify that it cannot happen... + } + } + } + if (garbage && !internal->unsat) + mark_garbage (g); + ++internal->stats.congruence.simplified; + ++internal->stats.congruence.simplified_ites; +} + +void Closure::add_ite_matching_proof_chain ( + Gate *g, Gate *h, int lhs1, int lhs2, std::vector &reasons1, + std::vector &reasons2) { + check_ite_lrat_reasons (g); + check_ite_lrat_reasons (h, false); + assert (g->lhs == lhs1); + 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"); + assert (unsimplified.empty ()); + assert (chain.empty ()); + if (internal->lrat) + check_correct_ite_flags (g); + const auto &rhs = g->rhs; + const int8_t flags_g = g->degenerated_ite; + const int8_t flags_h = h->degenerated_ite; + const int cond = rhs[0]; + LRAT_ID g_then_id = 0, g_neg_then_id = 0, g_neg_else_id = 0; + LRAT_ID h_then_id = 0, h_neg_then_id = 0, h_else_id = 0; + LRAT_ID g_else_id = 0, h_neg_else_id = 0; + const bool degenerated_g_then = ite_flags_no_then_clauses (flags_g); + const bool degenerated_g_else = ite_flags_no_else_clauses (flags_g); + const bool degenerated_h_then = ite_flags_no_then_clauses (flags_h); + const bool degenerated_h_else = ite_flags_no_else_clauses (flags_h); + + const bool degenerated_g_cond = ite_flags_cond_lhs (flags_g); + const bool degenerated_h_cond = ite_flags_cond_lhs (flags_h); + assert (!(degenerated_g_cond && degenerated_h_cond)); + + const bool degenerated_g_not_cond = ite_flags_neg_cond_lhs (flags_g); + const bool degenerated_h_not_cond = ite_flags_neg_cond_lhs (flags_h); + assert (!(degenerated_g_not_cond && degenerated_h_not_cond)); + + if (internal->lrat) { + // the code can produce tautologies in the case that: + // a = (c ? a : e) + // b = (c ? a : e) + // (no clauses with (then) index a/-a) + // but also for + // a = (a ? t : e) + // b = (a ? t : e) + // (no clauses with (then) index -a and (else) index e) + // and same for + // a = (c ? t : a) + // b = (c ? t : a) + // (no clauses with (then) index a and (else) index -e) + LOG (g, "get ids from"); + assert (g->pos_lhs_ids.size () == 4); + auto &g_then_clause = g->pos_lhs_ids[0].clause; + g_then_clause = + g_then_clause ? produce_rewritten_clause_lrat (g_then_clause, g->lhs, false) : nullptr; + if (g_then_clause) + g_then_id = g_then_clause->id; + + auto &g_neg_then_clause = g->pos_lhs_ids[1].clause; + g_neg_then_clause = + g_neg_then_clause ? produce_rewritten_clause_lrat (g_neg_then_clause, g->lhs, false) : nullptr; + if (g_neg_then_clause) + g_neg_then_id = g_neg_then_clause->id; + + auto &g_else_clause = g->pos_lhs_ids[2].clause; + g_else_clause = + g_else_clause ? produce_rewritten_clause_lrat (g_else_clause, g->lhs, false) : g_else_clause; + if (g_else_clause) + g_else_id = g_else_clause->id; + + auto &g_neg_else_clause = g->pos_lhs_ids[3].clause; + g_neg_else_clause = + g_neg_else_clause ? produce_rewritten_clause_lrat (g_neg_else_clause, g->lhs, false) : nullptr; + if (g_neg_else_clause) + g_neg_else_id = g_neg_else_clause->id; + + LOG (h, "now clauses from"); + assert (h->pos_lhs_ids.size () == 4); + auto &h_then_clause = h->pos_lhs_ids[0].clause; + h_then_clause = + h_then_clause ? produce_rewritten_clause_lrat (h_then_clause, h->lhs, false) : nullptr; + if (h_then_clause) + h_then_id = h_then_clause->id; + + auto &h_neg_then_clause = h->pos_lhs_ids[1].clause; + h_neg_then_clause = + h_neg_then_clause ? produce_rewritten_clause_lrat (h_neg_then_clause, h->lhs, false) : nullptr; + if (h_neg_then_clause) + h_neg_then_id = h_neg_then_clause->id; + + auto &h_else_clause = h->pos_lhs_ids[2].clause; + h_else_clause = + h_else_clause ? produce_rewritten_clause_lrat (h_else_clause, h->lhs, false) : nullptr; + if (h_else_clause) + h_else_id = h_else_clause->id; + + auto &h_neg_else_clause = h->pos_lhs_ids[3].clause; + h_neg_else_clause = + h_neg_else_clause ? produce_rewritten_clause_lrat (h_neg_else_clause, h->lhs, false) : nullptr; + if (h_neg_else_clause) + h_neg_else_id = h_neg_else_clause->id; + + } + + if (degenerated_g_cond) { + LOG ("special case: cond = lhs, g degenerated"); + unsimplified.push_back (-lhs1); + unsimplified.push_back (lhs2); + LRAT_ID id1 = 0; + if (internal->lrat) { + lrat_chain.push_back (g_then_id); + lrat_chain.push_back (h_neg_then_id); + } + id1 = simplify_and_add_to_proof_chain (unsimplified); + + unsimplified.clear (); + unsimplified.push_back (lhs1); + unsimplified.push_back (-lhs2); + LRAT_ID id2 = 0; + if (internal->lrat) { + lrat_chain.push_back (g_neg_else_id); + lrat_chain.push_back (h_else_id); + } + id2 = simplify_and_add_to_proof_chain (unsimplified); + + assert (!internal->lrat || id1); + assert (!internal->lrat || id2); + reasons1.push_back (id1); + reasons2.push_back (id2); + unsimplified.clear (); + return; + } + + if (degenerated_h_cond) { + LOG ("special case: cond = lhs, h degenerated"); + unsimplified.push_back (lhs1); // potentially lhs1 == lhs2 + unsimplified.push_back (-lhs2); + LRAT_ID id1 = 0; + if (internal->lrat) { + lrat_chain.push_back (h_then_id); + lrat_chain.push_back (g_neg_then_id); + } + id1 = simplify_and_add_to_proof_chain (unsimplified); + + unsimplified.clear (); + unsimplified.push_back (-lhs1); + unsimplified.push_back (lhs2); + LRAT_ID id2 = 0; + if (internal->lrat) { + lrat_chain.push_back (h_neg_else_id); + lrat_chain.push_back (g_else_id); + } + id2 = simplify_and_add_to_proof_chain (unsimplified); + + assert (!internal->lrat || id1); + assert (!internal->lrat || id2); + reasons2.push_back (id1); + reasons1.push_back (id2); + unsimplified.clear (); + return; + } + + if (degenerated_g_not_cond) { + LOG ("special case: cond = -lhs, g degenerated"); + unsimplified.push_back (lhs1); + unsimplified.push_back (-lhs2); + LRAT_ID id1 = 0; + if (internal->lrat) { + assert (g_neg_then_id && h_then_id && g_else_id && h_neg_else_id); + lrat_chain.push_back (g_neg_then_id); + lrat_chain.push_back (h_then_id); + } + id1 = simplify_and_add_to_proof_chain (unsimplified); + + unsimplified.clear (); + unsimplified.push_back (-lhs1); + unsimplified.push_back (lhs2); + LRAT_ID id2 = -1; + + if (internal->lrat) { + lrat_chain.push_back (g_else_id); + lrat_chain.push_back (h_neg_else_id); + } + id2 = simplify_and_add_to_proof_chain (unsimplified); + assert (!internal->lrat || id1); + assert (!internal->lrat || id2); + reasons2.push_back (id1); + reasons1.push_back (id2); + unsimplified.clear (); + return; + } + + if (degenerated_h_not_cond) { + LOG ("special case: cond = -lhs, h degenerated"); + unsimplified.push_back (lhs1); + unsimplified.push_back (-lhs2); + LRAT_ID id1 = 0; + if (internal->lrat) { + assert (g_neg_then_id && h_then_id && g_else_id && h_neg_else_id); + lrat_chain.push_back (h_neg_then_id); + lrat_chain.push_back (g_then_id); + } + id1 = simplify_and_add_to_proof_chain (unsimplified); + + unsimplified.clear (); + unsimplified.push_back (-lhs1); + unsimplified.push_back (lhs2); + LRAT_ID id2 = -1; + + if (internal->lrat) { + lrat_chain.push_back (h_else_id); + lrat_chain.push_back (g_neg_else_id); + } + id2 = simplify_and_add_to_proof_chain (unsimplified); + assert (!internal->lrat || id1); + assert (!internal->lrat || id2); + reasons2.push_back (id1); + reasons1.push_back (id2); + unsimplified.clear (); + return; + } + + LOG ("normal path"); + assert (!internal->lrat || degenerated_g_then || + (g_then_id && g_neg_then_id)); + assert (!internal->lrat || degenerated_g_else || + (g_else_id && g_neg_else_id)); + assert (!internal->lrat || degenerated_h_then || + (h_then_id && h_neg_then_id)); + assert (!internal->lrat || degenerated_h_else || + (h_else_id && h_neg_else_id)); + assert (!internal->lrat || g_then_id || h_neg_then_id); + assert (!internal->lrat || g_neg_then_id || h_then_id); + unsimplified.push_back (-lhs1); + unsimplified.push_back (lhs2); + unsimplified.push_back (-cond); + LRAT_ID id1 = 0; + if (degenerated_g_then || degenerated_h_then) { + id1 = degenerated_g_then ? h_neg_then_id : g_then_id; + } else { + if (internal->lrat) { + lrat_chain.push_back (g_then_id); + lrat_chain.push_back (h_neg_then_id); + } + id1 = simplify_and_add_to_proof_chain (unsimplified); + } + unsimplified.pop_back (); + unsimplified.push_back (cond); + + LRAT_ID id2 = 0; + if (degenerated_g_else || degenerated_h_else) { + id2 = degenerated_g_else ? h_neg_else_id : g_else_id; + } else { + if (internal->lrat) { + lrat_chain.push_back (h_neg_else_id); + lrat_chain.push_back (g_else_id); + } + id2 = simplify_and_add_to_proof_chain (unsimplified); + } + unsimplified.pop_back (); + + unsimplified.clear (); + unsimplified.push_back (lhs1); + unsimplified.push_back (-lhs2); + unsimplified.push_back (-cond); + assert (lrat_chain.empty ()); + + LRAT_ID id3 = 0; + if (degenerated_g_then || degenerated_h_then) { + id3 = degenerated_g_then ? h_then_id : g_neg_then_id; + } else { + if (internal->lrat) { + // lrat_chain.push_back (g_then_id); + // lrat_chain.push_back (h_neg_then_id); + lrat_chain.push_back (g_neg_then_id); + lrat_chain.push_back (h_then_id); + } + id3 = simplify_and_add_to_proof_chain (unsimplified); + } + unsimplified.pop_back (); + unsimplified.push_back (cond); + assert (lrat_chain.empty ()); + + LRAT_ID id4 = 0; + if (degenerated_g_else || degenerated_h_else) { + id4 = degenerated_g_else ? h_else_id : g_neg_else_id; + } else { + if (internal->lrat) { + lrat_chain.push_back (g_neg_else_id); + lrat_chain.push_back (h_else_id); + } + id4 = simplify_and_add_to_proof_chain (unsimplified); + } + unsimplified.pop_back (); + + if (internal->lrat) { + assert (!internal->lrat || (id1 && id2 && id3 && id4)); + reasons1.push_back (id1), reasons1.push_back (id2); + reasons2.push_back (id3), reasons2.push_back (id4); + } + + unsimplified.clear (); + + LOG ("finished ITE matching proof chain"); +} + +Gate *Closure::new_ite_gate (int lhs, int cond, int then_lit, int else_lit, + std::vector &&clauses) { + assert (chain.empty ()); + if (else_lit == -then_lit) { + if (then_lit < 0) + LOG ("skipping ternary XOR %d := %d ^ %d", lhs, cond, -then_lit); + else + LOG ("skipping ternary XOR %d := %d ^ %d", -lhs, cond, then_lit); + return nullptr; + } + if (else_lit == then_lit) { + LOG ("found trivial ITE gate %d := %d ? %d : %d", (lhs), (cond), + (then_lit), (else_lit)); + std::vector reasons_implication, reasons_back; + if (internal->lrat) { + merge_ite_gate_same_then_else_lrat (clauses, reasons_implication, + reasons_back); + } + if (merge_literals_lrat (lhs, then_lit, reasons_implication, + reasons_back)) + ++internal->stats.congruence.trivial_ite; + return 0; + } + + rhs.clear (); + rhs.push_back (cond); + rhs.push_back (then_lit); + rhs.push_back (else_lit); + LOG ("ITE gate %d = %d ? %d : %d", lhs, cond, then_lit, else_lit); + + bool negate_lhs = false; + Gate *g = new Gate; + g->lhs = lhs; + g->tag = Gate_Type::ITE_Gate; + g->rhs = {rhs}; + g->pos_lhs_ids = clauses; +#ifdef LOGGING + g->id = -1; +#endif + Gate *h = find_ite_gate (g, negate_lhs); + if (negate_lhs) + lhs = -lhs; + g->lhs = lhs; + check_ite_gate_implied (g); + check_ite_lrat_reasons ( + g, false); // due to merges done before during AND gate detection! + + if (h) { + check_ite_gate_implied (h); + std::vector extra_reasons_lit, extra_reasons_ulit; + add_ite_matching_proof_chain (h, g, h->lhs, lhs, extra_reasons_lit, + extra_reasons_ulit); + if (merge_literals_lrat (h, g, h->lhs, lhs, extra_reasons_lit, + extra_reasons_ulit)) { + ++internal->stats.congruence.ites; + LOG ("found merged literals"); + } + delete_proof_chain (); + delete g; + return h; + } else { + // do not sort clauses here obviously! + // sort (begin (g->rhs), end (g->rhs)); + g->garbage = false; + g->indexed = true; + g->shrunken = false; + g->hash = hash_lits (nonces, g->rhs); + table.insert (g); + ++internal->stats.congruence.gates; +#ifdef LOGGING + g->id = fresh_id++; +#endif + LOG (g, "creating new"); + if (internal->lrat) + update_ite_flags (g); + check_ite_gate_implied (g); + for (auto lit : g->rhs) { + connect_goccs (g, lit); + } + } + check_ite_lrat_reasons (g); + return g; +} + +void check_ite_lits_normalized (const std::vector &lits) { + assert (lits[0] > 0); + assert (lits[1] > 0); + assert (lits[0] != lits[1]); + assert (lits[0] != lits[2]); + assert (lits[1] != lits[2]); + assert (lits[0] != -lits[1]); + assert (lits[0] != -lits[2]); + assert (lits[1] != -lits[2]); +#ifdef NDEBUG + (void) lits; +#endif +} + +void Closure::check_ite_implied (int lhs, int cond, int then_lit, + int else_lit) { + if (internal->lrat) + return; + check_ternary (cond, -else_lit, lhs); + check_ternary (cond, else_lit, -lhs); + check_ternary (-cond, -then_lit, lhs); + check_ternary (-cond, then_lit, -lhs); +} + +void Closure::check_contained_module_rewriting (Clause *c, int lit, + bool normalized, + int except) { +#ifndef NDEBUG + if (lit == except) // happens for degenerated cases + except = 0; + const int normalize_lit = + (lit == except ? except : find_eager_representative (lit)); + assert (!normalized || lit == -except || normalize_lit == lit); + bool found = false; + LOG (c, "looking for (normalized) %d in ", lit); + for (auto other : *c) { + const int normalize_other = + (other == except ? except : find_eager_representative (other)); + LOG ("%d -> %d ", other, normalize_other); + assert (!normalized || other == -except || normalize_other == other); + if (normalize_other == normalize_lit) { + found = true; + break; + } + } + assert (found); +#else + (void) c, (void) lit, (void) normalized, (void) except; +#endif +} + +void Closure::check_ite_lrat_reasons (Gate *g, bool normalized) { +#ifndef NDEBUG + assert (g->tag == Gate_Type::ITE_Gate); + if (!internal->lrat) + return; + assert (g->rhs.size () == 3); + assert (is_tautological_ite_gate (g) || g->pos_lhs_ids.size () == 4); + assert (g->neg_lhs_ids.empty ()); + assert (g->pos_lhs_ids.size () == 4); +#else + (void) g, (void) normalized; +#endif +} + +void Closure::check_ite_gate_implied (Gate *g) { + assert (g->tag == Gate_Type::ITE_Gate); + if (internal->lrat) + return; + check_ite_implied (g->lhs, g->rhs[0], g->rhs[1], g->rhs[2]); +} + +void Closure::init_ite_gate_extraction ( + std::vector &candidates) { + std::vector ternary; + glargecounts.resize (2 * internal->vsize, 0); + for (auto c : internal->clauses) { + if (c->garbage) + continue; + if (c->redundant) + continue; + if (c->size < 3) + continue; + unsigned size = 0; + + assert (!c->garbage); + for (auto lit : *c) { + const signed char v = internal->val (lit); + if (v < 0) + continue; + if (v > 0) { + LOG (c, "deleting as satisfied due to %d", lit); + internal->mark_garbage (c); + goto CONTINUE_COUNTING_NEXT_CLAUSE; + } + if (size == 3) + goto CONTINUE_COUNTING_NEXT_CLAUSE; + size++; + } + if (size < 3) + continue; + assert (size == 3); + ternary.push_back (c); + LOG (c, "counting original ITE gate base"); + for (auto lit : *c) { + if (!internal->val (lit)) + ++largecount (lit); + } + CONTINUE_COUNTING_NEXT_CLAUSE:; + } + + for (auto c : ternary) { + assert (!c->garbage); + assert (!c->redundant); + unsigned positive = 0, negative = 0, twice = 0; + for (auto lit : *c) { + if (internal->val (lit)) + continue; + const int count_not_lit = largecount (-lit); + if (!count_not_lit) + goto CONTINUE_WITH_NEXT_TERNARY_CLAUSE; + const unsigned count_lit = largecount (lit); + assert (count_lit); + if (count_lit > 1 && count_not_lit > 1) + ++twice; + if (lit < 0) + ++negative; + else + ++positive; + } + if (twice < 2) + goto CONTINUE_WITH_NEXT_TERNARY_CLAUSE; + assert (c->size != 2); + for (auto lit : *c) + internal->occs (lit).push_back (c); + if (positive && negative) + candidates.push_back (c); + CONTINUE_WITH_NEXT_TERNARY_CLAUSE:; + } + + ternary.clear (); +} + +void Closure::reset_ite_gate_extraction () { + condbin[0].clear (); + condbin[1].clear (); + condeq[0].clear (); + condeq[1].clear (); + glargecounts.clear (); + internal->clear_occs (); +} + +void Closure::copy_conditional_equivalences (int lit, + lit_implications &condbin) { + assert (condbin.empty ()); + for (auto c : internal->occs (lit)) { + assert (c->size != 2); + int first = 0, second = 0; + for (auto other : *c) { + if (internal->val (other)) + continue; + if (other == lit) + continue; + if (!first) + first = other; + else { + assert (!second); + second = other; + } + } + assert (first), assert (second); + lit_implication p (first, second, c); + + if (internal->vlit (first) < internal->vlit (second)) { + assert (p.first == first); + assert (p.second == second); + } else { + assert (internal->vlit (second) < internal->vlit (first)); + p.swap (); + assert (p.first == second); + assert (p.second == first); + } + LOG (c, "literal %d condition binary clause %d %d", lit, first, second); + condbin.push_back (p); + } +} + +bool less_litpair (lit_equivalence p, lit_equivalence q) { + const int a = p.first; + const int b = q.first; + if (a < b) + return true; + if (b > a) + return false; + const int c = p.second; + const int d = q.second; + return (c < d); +} +struct litpair_rank { + CaDiCaL::Internal *internal; + litpair_rank (Internal *i) : internal (i) {} + typedef uint64_t Type; + Type operator() (const lit_implication &a) const { + uint64_t lita = internal->vlit (a.first); + uint64_t litb = internal->vlit (a.second); + return (lita << 32) + litb; + } +}; + +struct litpair_smaller { + CaDiCaL::Internal *internal; + litpair_smaller (Internal *i) : internal (i) {} + bool operator() (const lit_implication &a, + const lit_implication &b) const { + const auto s = litpair_rank (internal) (a); + const auto t = litpair_rank (internal) (b); + return s < t; + } +}; + +lit_implications::const_iterator +Closure::find_lit_implication_second_literal ( + int lit, lit_implications::const_iterator begin, + lit_implications::const_iterator end) { + LOG ("searching for %d in", lit); + for (auto it = begin; it != end; ++it) + LOG ("%d [%d]", it->first, it->second); + lit_implications::const_iterator found = std::lower_bound ( + begin, end, lit_implication{lit, lit}, + [] (const lit_implication &a, const lit_implication &b) { + return a.second < b.second; + }); +#ifndef NDEBUG + auto found2 = std::binary_search ( + begin, end, lit_implication{lit, lit}, + [] (const lit_implication &a, const lit_implication &b) { + return a.second < b.second; + }); +#endif + + if (found < end && found->second == lit) { + assert (found2 == (found < end)); + return found; + } + return end; +} + +void Closure::search_condeq (int lit, int pos_lit, + lit_implications::const_iterator pos_begin, + lit_implications::const_iterator pos_end, + int neg_lit, + lit_implications::const_iterator neg_begin, + lit_implications::const_iterator neg_end, + lit_equivalences &condeq) { + assert (neg_lit == -pos_lit); + assert (pos_begin < pos_end); + assert (neg_begin < neg_end); + assert (pos_begin->first == pos_lit); + assert (neg_begin->first == neg_lit); + assert (pos_end <= neg_begin || neg_end <= pos_begin); + for (lit_implications::const_iterator p = pos_begin; p != pos_end; p++) { + const int other = p->second; + const int not_other = -other; + const lit_implications::const_iterator other_clause = + find_lit_implication_second_literal (not_other, neg_begin, neg_end); + assert (std::find (begin (*p->clause), end (*p->clause), lit) != + end (*p->clause)); + if (other_clause != neg_end) { + assert (std::find (begin (*other_clause->clause), + end (*other_clause->clause), + neg_lit) != end (*other_clause->clause)); + assert (std::find (begin (*p->clause), end (*p->clause), other) != + end (*p->clause)); + lit_equivalence equivalence (neg_lit, other_clause->clause, other, + p->clause); + if (pos_lit > 0) { + equivalence.negate_both (); + } + if (internal->lrat) + equivalence.check_invariant (); + LOG ("found conditional %d equivalence %d = %d", lit, + equivalence.first, equivalence.second); + assert (equivalence.first > 0); + assert (internal->vlit (equivalence.first) < + internal->vlit (equivalence.second)); + check_ternary (lit, neg_lit, -other); + check_ternary (lit, -neg_lit, other); + condeq.push_back (equivalence); + if (equivalence.second < 0) { + lit_equivalence inverse_equivalence = + equivalence.swap ().negate_both (); + condeq.push_back (inverse_equivalence); + if (internal->lrat) + inverse_equivalence.check_invariant (); + } else { + lit_equivalence inverse_equivalence = equivalence.swap (); + condeq.push_back (inverse_equivalence); + if (internal->lrat) + inverse_equivalence.check_invariant (); + } + } + } +#ifndef LOGGING + (void) lit; +#endif +} + +void Closure::extract_condeq_pairs (int lit, lit_implications &condbin, + lit_equivalences &condeq) { + const lit_implications::const_iterator begin = condbin.cbegin (); + const lit_implications::const_iterator end = condbin.cend (); + lit_implications::const_iterator pos_begin = begin; + int next_lit = 0; + +#ifdef LOGGING + for (const auto &pair : condbin) + LOG ("unsorted conditional %d equivalence %d = %d", lit, pair.first, + pair.second); +#endif + LOG ("searching for first positive literal for lit %d", lit); + for (;;) { + if (pos_begin == end) + return; + next_lit = pos_begin->first; + LOG ("checking %d", next_lit); + if (next_lit > 0) + break; + pos_begin++; + } + + for (;;) { + assert (pos_begin != end); + assert (next_lit == pos_begin->first); + assert (next_lit > 0); + const int pos_lit = next_lit; + lit_implications::const_iterator pos_end = pos_begin + 1; + LOG ("searching for first other literal after finding lit %d", + next_lit); + for (;;) { + if (pos_end == end) + return; + next_lit = pos_end->first; + if (next_lit != pos_lit) + break; + pos_end++; + } + assert (pos_end != end); + assert (next_lit == pos_end->first); + const int neg_lit = -pos_lit; + if (next_lit != neg_lit) { + if (next_lit < 0) { + pos_begin = pos_end + 1; + LOG ("next_lit %d < 0", next_lit); + for (;;) { + if (pos_begin == end) + return; + next_lit = pos_begin->first; + if (next_lit > 0) + break; + pos_begin++; + } + } else + pos_begin = pos_end; + continue; + } + const lit_implications::const_iterator neg_begin = pos_end; + lit_implications::const_iterator neg_end = neg_begin + 1; + while (neg_end != end) { + next_lit = neg_end->first; + if (next_lit != neg_lit) + break; + neg_end++; + } +#ifdef LOGGING + for (lit_implications::const_iterator p = pos_begin; p != pos_end; p++) + LOG ("conditional %d binary clause %d %d with positive %d", (lit), + (p->first), (p->second), (pos_lit)); + for (lit_implications::const_iterator p = neg_begin; p != neg_end; p++) + LOG ("conditional %d binary clause %d %d with negative %d", (lit), + (p->first), (p->second), (neg_lit)); +#endif + const size_t pos_size = pos_end - pos_begin; + const size_t neg_size = neg_end - neg_begin; + if (pos_size <= neg_size) { + LOG ("searching negation of %zu conditional binary clauses " + "with positive %d in %zu conditional binary clauses with %d", + pos_size, (pos_lit), neg_size, (neg_lit)); + search_condeq (lit, pos_lit, pos_begin, pos_end, neg_lit, neg_begin, + neg_end, condeq); + } else { + LOG ("searching negation of %zu conditional binary clauses " + "with negative %d in %zu conditional binary clauses with %d", + neg_size, (neg_lit), pos_size, (pos_lit)); + search_condeq (lit, neg_lit, neg_begin, neg_end, pos_lit, pos_begin, + pos_end, condeq); + } + if (neg_end == end) + return; + assert (next_lit == neg_end->first); + if (next_lit < 0) { + pos_begin = neg_end + 1; + for (;;) { + if (pos_begin == end) + return; + next_lit = pos_begin->first; + if (next_lit > 0) + break; + pos_begin++; + } + } else + pos_begin = neg_end; + } +} + +void Closure::find_conditional_equivalences (int lit, + lit_implications &condbin, + lit_equivalences &condeq) { + assert (condbin.empty ()); + assert (condeq.empty ()); + assert (internal->occs (lit).size () > 1); + copy_conditional_equivalences (lit, condbin); + MSORT (internal->opts.radixsortlim, begin (condbin), end (condbin), + litpair_rank (this->internal), litpair_smaller (this->internal)); + + extract_condeq_pairs (lit, condbin, condeq); + MSORT (internal->opts.radixsortlim, begin (condbin), end (condbin), + litpair_rank (this->internal), litpair_smaller (this->internal)); + +#ifdef LOGGING + for (auto pair : condeq) + LOG ("sorted conditional %d equivalence %d = %d", lit, pair.first, + pair.second); + LOG ("found %zu conditional %d equivalences", condeq.size (), lit); + +#endif +} + +void Closure::merge_condeq (int cond, lit_equivalences &condeq, + lit_equivalences ¬_condeq) { + LOG ("merging cond for literal %d", cond); + auto q = begin (not_condeq); + const auto end_not_condeq = end (not_condeq); + for (auto p : condeq) { + const int lhs = p.first; + const int then_lit = p.second; + if (internal->lrat) + p.check_invariant (); + assert (lhs > 0); + while (q != end_not_condeq && q->first < lhs) + ++q; + while (q != end_not_condeq && q->first == lhs) { + LOG ("looking when %d at p= %d %d", cond, p.first, p.second); + LOG ("looking when %d at %d %d", -cond, q->first, q->second); + lit_equivalence not_cond_pair = *q++; + const int else_lit = not_cond_pair.second; + std::vector clauses; + if (internal->lrat) { + // The then/else literal is the second of the pair, hence the swap + // of the reasons + assert (p.first_clause && p.second_clause); + assert (not_cond_pair.first_clause && not_cond_pair.second_clause); + LOG (p.second_clause, "pairing %d", then_lit); + LOG (p.first_clause, "pairing %d", -then_lit); + LOG (not_cond_pair.second_clause, "pairing %d", else_lit); + LOG (not_cond_pair.first_clause, "pairing %d", -else_lit); + clauses.push_back (LitClausePair (then_lit, p.second_clause)); + clauses.push_back (LitClausePair (-then_lit, p.first_clause)); + clauses.push_back ( + LitClausePair (else_lit, not_cond_pair.second_clause)); + clauses.push_back ( + LitClausePair (-else_lit, not_cond_pair.first_clause)); + if (internal->lrat) + not_cond_pair.check_invariant (); + } + new_ite_gate (lhs, cond, then_lit, else_lit, std::move (clauses)); + if (internal->unsat) + return; + } + } +} + +void Closure::extract_ite_gates_of_literal (int lit) { + LOG ("search for ITE for literal %d ", lit); + find_conditional_equivalences (lit, condbin[0], condeq[0]); + if (!condeq[0].empty ()) { + find_conditional_equivalences (-lit, condbin[1], condeq[1]); + if (!condeq[1].empty ()) { + if (lit < 0) + merge_condeq (-lit, condeq[0], condeq[1]); + else + merge_condeq (lit, condeq[1], condeq[0]); + } + } + + condbin[0].clear (); + condbin[1].clear (); + condeq[0].clear (); + condeq[1].clear (); +} + +void Closure::extract_ite_gates_of_variable (int idx) { + const int lit = idx; + const int not_lit = -idx; + + auto lit_watches = internal->occs (lit); + auto not_lit_watches = internal->occs (not_lit); + const size_t size_lit_watches = lit_watches.size (); + const size_t size_not_lit_watches = not_lit_watches.size (); + if (size_lit_watches <= size_not_lit_watches) { + if (size_lit_watches > 1) + extract_ite_gates_of_literal (lit); + } else { + if (size_not_lit_watches > 1) + extract_ite_gates_of_literal (not_lit); + } +} + +void Closure::extract_ite_gates () { + assert (!full_watching); + if (!internal->opts.congruenceite) + return; + START (extractites); + std::vector candidates; + + init_ite_gate_extraction (candidates); + + for (auto idx : internal->vars) { + if (internal->flags (idx).active ()) { + extract_ite_gates_of_variable (idx); + if (internal->unsat) + break; + } + } + // Kissat has an alternative version MERGE_CONDITIONAL_EQUIVALENCES + reset_ite_gate_extraction (); + STOP (extractites); +} + +/*------------------------------------------------------------------------*/ +void Closure::extract_gates () { + START (extract); + extract_and_gates (); + assert (internal->unsat || lrat_chain.empty ()); + assert (internal->unsat || chain.empty ()); + if (internal->unsat || internal->terminated_asynchronously ()) { + STOP (extract); + return; + } + extract_xor_gates (); + assert (internal->unsat || lrat_chain.empty ()); + assert (internal->unsat || chain.empty ()); + + if (internal->unsat || internal->terminated_asynchronously ()) { + STOP (extract); + return; + } + extract_ite_gates (); + STOP (extract); +} + +/*------------------------------------------------------------------------*/ +// top level function to extract gate +bool Internal::extract_gates () { + if (unsat) + return false; + if (!opts.congruence) + return false; + if (level) + backtrack (); + if (!propagate ()) { + learn_empty_clause (); + return false; + } + if (congruence_delay.bumpreasons.limit) { + LOG ("delaying congruence %" PRId64 " more times", + congruence_delay.bumpreasons.limit); + congruence_delay.bumpreasons.limit--; + return false; + } + + // to remove false literals from clauses + // It makes the technique stronger as long clauses + // can become binary / ternary + // garbage_collection (); + + const int64_t old = stats.congruence.congruent; + const int old_merged = stats.congruence.congruent; + + // congruencebinary is already doing it (and more actually) + if (!internal->opts.congruencebinaries) { + const bool dedup = opts.deduplicate; + opts.deduplicate = true; + mark_duplicated_binary_clauses_as_garbage (); + opts.deduplicate = dedup; + } + ++stats.congruence.rounds; + clear_watches (); + // connect_binary_watches (); + + START_SIMPLIFIER (congruence, CONGRUENCE); + Closure closure (this); + + closure.init_closure (); + assert (unsat || closure.chain.empty ()); + assert (unsat || lrat_chain.empty ()); + closure.extract_binaries (); + assert (unsat || closure.chain.empty ()); + assert (unsat || lrat_chain.empty ()); + closure.extract_gates (); + assert (unsat || closure.chain.empty ()); + assert (unsat || lrat_chain.empty ()); + internal->clear_watches (); + internal->connect_watches (); + closure.reset_extraction (); + + if (!unsat) { + closure.find_units (); + assert (unsat || closure.chain.empty ()); + assert (unsat || lrat_chain.empty ()); + if (!internal->unsat) { + closure.find_equivalences (); + assert (unsat || closure.chain.empty ()); + assert (unsat || lrat_chain.empty ()); + + if (!unsat) { + const int propagated = closure.propagate_units_and_equivalences (); + assert (unsat || closure.chain.empty ()); + if (!unsat && propagated) + closure.forward_subsume_matching_clauses (); + } + } + } + + closure.reset_closure (); + internal->clear_watches (); + internal->connect_watches (); + if (!internal->unsat) { + propagated2 = propagated = 0; + propagate (); + } + assert (closure.new_unwatched_binary_clauses.empty ()); + internal->reset_occs (); + internal->reset_noccs (); + assert (!internal->occurring ()); + assert (lrat_chain.empty ()); + + const int64_t new_merged = stats.congruence.congruent; + + phase ("congruence-phase", stats.congruence.rounds, "merged %ld literals", + new_merged - old_merged); + if (!unsat && !internal->propagate ()) + unsat = true; + + STOP_SIMPLIFIER (congruence, CONGRUENCE); + report ('c', !opts.reportall && !(stats.congruence.congruent - old)); +#ifndef 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 ()) + assert (!w.clause->garbage); + if (w.clause->garbage) + continue; + ++watched; + LOG (w.clause, "watched"); + } + } + } + LOG ("and now the clauses:"); + size_t nb_clauses = 0; + for (auto c : clauses) { + if (c->garbage) + continue; + LOG (c, "watched"); + ++nb_clauses; + } + assert (watched == nb_clauses * 2); +#endif + assert (!internal->occurring ()); + + if (new_merged == old_merged) { + congruence_delay.bumpreasons.interval++; + } else { + congruence_delay.bumpreasons.interval /= 2; + } + + LOG ("delay congruence internal %" PRId64, + congruence_delay.bumpreasons.interval); + congruence_delay.bumpreasons.limit = + congruence_delay.bumpreasons.interval; + + return new_merged != old_merged; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/congruence.hpp b/src/sat/cadical/congruence.hpp new file mode 100644 index 000000000..5686238d8 --- /dev/null +++ b/src/sat/cadical/congruence.hpp @@ -0,0 +1,714 @@ +#ifndef _congruenc_hpp_INCLUDED +#define _congruenc_hpp_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clause.hpp" +#include "inttypes.hpp" +#include "util.hpp" +#include "watch.hpp" + +namespace CaDiCaL { + +typedef int64_t LRAT_ID; + +// This implements the algorithm algorithm from SAT 2024. +// +// The idea is to: +// 0. handle binary clauses +// 1. detect gates and merge gates with same inputs ('lazy') +// 2. eagerly replace the equivalent literals and merge gates with same +// inputs +// 3. forward subsume +// +// In step 0 the normalization is fully lazy but we do not care about a +// normal form. Therefore we actually eagerly merge literals. +// +// In step 2 there is a subtility: we only replace with the equivalence +// chain as far as we propagated so far. This is the eager part. For LRAT we +// produce the equivalence up to the point we have propagated, no the full +// chain. This is important for merging literals. To merge literals we use +// union-find but we only compress paths when rewriting the literal, not +// before. The compression was not considered important in Kissat, but we do +// it aggressively as a mirror of the equivalences we have generated. +// +// We have two structures for merging: +// - the lazy ones contains alls merges, with functions like +// find_representative +// +// - the eager version that gets the merges one by one, with functions +// like find_eager_representatives +// +// The two structures are nicely separated and we only working on one of +// them except for: +// +// 1. When propagating one equivalence, we first important the +// equivalence from the lazy to the eager version, producing the full +// chain. +// +// 2. When merging the literals, we merge the literals given by the lazy +// structure, then we merge their representative in the eager version, +// updating only the lazy structure. We do not update the eager version. +// +// An important point: We cannot use internal->lrat_chain and +// internal->clause because in most places we can interrupt the +// transformation to learn a new clause representing an equivalence. +// However, we can only have 2 layers so we use this->lrat_chain and +// internal->lrat_chain when we really produce the proof. +struct Internal; + +#define LD_MAX_ARITY 26 +#define MAX_ARITY ((1 << LD_MAX_ARITY) - 1) + +enum class Gate_Type { And_Gate, XOr_Gate, ITE_Gate }; + +// Wrapper when we are looking for implication in if-then-else gates +struct lit_implication { + int first; + int second; + Clause *clause; + lit_implication (int f, int s, Clause *_id) + : first (f), second (s), clause (_id) {} + lit_implication (int f, int s) : first (f), second (s), clause (0) {} + lit_implication () : first (0), second (0), clause (nullptr) {} + void swap () { std::swap (first, second); } +}; + +// Wrapper when we are looking for equivalence for if-then-else-gate. They +// are produced by merging implication +struct lit_equivalence { + int first; + int second; + Clause *first_clause; + Clause *second_clause; + void check_invariant () { + assert (second_clause); + assert (first_clause); + assert (std::find (begin (*first_clause), end (*first_clause), first) != + end (*first_clause)); + assert (std::find (begin (*second_clause), end (*second_clause), + second) != end (*second_clause)); + assert (std::find (begin (*first_clause), end (*first_clause), + -second) != end (*first_clause)); + assert (std::find (begin (*second_clause), end (*second_clause), + -first) != end (*second_clause)); + } + lit_equivalence (int f, Clause *f_id, int s, Clause *s_id) + : first (f), second (s), first_clause (f_id), second_clause (s_id) {} + lit_equivalence (int f, int s) + : first (f), second (s), first_clause (nullptr), + second_clause (nullptr) {} + lit_equivalence () + : first (0), second (0), first_clause (nullptr), + second_clause (nullptr) {} + lit_equivalence swap () { + std::swap (first, second); + std::swap (first_clause, second_clause); + return *this; + } + lit_equivalence negate_both () { + first = -first; + second = -second; + std::swap (first_clause, second_clause); + return *this; + } +}; + +typedef std::vector lit_implications; +typedef std::vector lit_equivalences; + +std::string string_of_gate (Gate_Type t); + +struct LitClausePair { + int current_lit; // current literal from the gate + Clause *clause; + LitClausePair (int lit, Clause *cl) : current_lit (lit), clause (cl) {} + LitClausePair () : current_lit (0), clause (nullptr) {} +}; +struct LitIdPair { + int lit; // current literal from the gate + LRAT_ID id; + LitIdPair (int l, LRAT_ID i) : lit (l), id (i) {} + LitIdPair () : lit (0), id (0) {} +}; + +/*------------------------------------------------------------------------*/ + +// Sorting the scheduled clauses is way faster if we compute and save the +// clause size in the schedule to avoid pointer access to clauses during +// sorting. This slightly increases the schedule size though. + +struct ClauseSize { + size_t size; + Clause *clause; + ClauseSize (int s, Clause *c) : size (s), clause (c) {} + ClauseSize (Clause *c): size (c->size), clause (c) {} + ClauseSize () {} +}; + +struct smaller_clause_size_rank { + typedef size_t Type; + Type operator() (const ClauseSize &a) { return a.size; } +}; + +/*------------------------------------------------------------------------*/ +// There are many special cases for ITE gates and we have to keep track of +// them as it is a gate property (rewriting might not make it obvious +// anymore). +// a = (a ? t : e) results in no -t and no +e gate (a --> a = t == (-a v -a v t) & (-a v a v -t)) +// a = (-a ? t : e) results in no +t and no -e gate +// a = (c ? a : e) results in no t gate (none of them) +// a = (c ? t : a) results in no e gate (none of them) + +enum Special_ITE_GATE { + NORMAL = 0, + NO_PLUS_THEN = (1 << 0), + NO_NEG_THEN = (1 << 1), + NO_THEN = NO_PLUS_THEN + NO_NEG_THEN, + NO_PLUS_ELSE = (1 << 2), + NO_NEG_ELSE = (1 << 3), + NO_ELSE = NO_PLUS_ELSE + NO_NEG_ELSE, + COND_LHS = NO_NEG_THEN + NO_PLUS_ELSE, + UCOND_LHS = NO_PLUS_THEN + NO_NEG_ELSE, +}; + +inline bool ite_flags_no_then_clauses (int8_t flag) { + return (flag & NO_THEN) == NO_THEN; +} + +inline bool ite_flags_no_else_clauses (int8_t flag) { + return (flag & NO_ELSE) == NO_ELSE; +} + +inline bool ite_flags_neg_cond_lhs (int8_t flag) { + return (flag & UCOND_LHS) == UCOND_LHS; +} + +inline bool ite_flags_cond_lhs (int8_t flag) { + return (flag & COND_LHS) == COND_LHS; +} + +/*------------------------------------------------------------------------*/ + +// The core structure of this algorithm: the gate. It is composed of a +// left-hand side and an array of right-hand side. +// +// There are a few tags to help remembering the status of the gate (like +// deleted) +// +// To keep track of the proof we use two extra arrays: +// - `neg_lhs_ids' contains the long clause for AND gates. Otherwise, it is +// empty. TODO: change to std::option as it contains at most one element +// - `pos_lhs_ids' contains all the remaining gates. +// +// We keep the reasons with an index. This index depends on the gates: + +// - AND-Gates and ITE-Gates: the index is the literal from the RHS +// +// - XOR-Gates: if you order the clauses by the order of the literals, +// each literal is either positive (bit '1') or negative (bit '0'). This +// gives a number that we can use. +// +// TODO Florian: I do not think that you have to changed anything, look at +// the 'Look at this first' in the CPP file. +// +// Important for the proofs: the LHS is not updated. +// +// TODO: we currently use a vector for the rhs, but we could also use FMA +// and inline the structure to avoid any indirection. +// +// One warning for degenerated gate: it is a monotone property on the +// defining clauses, but not on the LHS/RHS as the LHS is not rewritten: +// take 4 = AND 3 4 (degenerated with only the clause -4 3) with a rewriting +// 4 -> 1 (unchanged clause) and later 1 -> 3 (unchanged clause) but you do +// not know anymore from the gate that it is degenerated +struct Gate { +#ifdef LOGGING + uint64_t id; +#endif + int lhs; + Gate_Type tag; + bool garbage : 1; + bool indexed : 1; + bool marked : 1; + bool shrunken : 1; + size_t hash; // TODO remove this field (the C++ implementation is caching + // it anyway) + vector pos_lhs_ids; + vector neg_lhs_ids; + bool degenerated_and_neg = false; // LRAT only relevant for AND Gates, neg lhs in RHS + bool degenerated_and_pos = false; // LRAT only relevant for AND Gates, pos lhs in RHS + int8_t degenerated_ite = Special_ITE_GATE::NORMAL; + vector rhs; + + size_t arity () const { return rhs.size (); } + + bool operator== (Gate const &lhs) { + return tag == lhs.tag && hash == lhs.hash && rhs == lhs.rhs; + } +}; + +typedef vector GOccs; + +struct GateEqualTo { + bool operator() (const Gate *const lhs, const Gate *const rhs) const { + return lhs->rhs == rhs->rhs && lhs->tag == rhs->tag; + } +}; + +struct CompactBinary { + Clause *clause; + LRAT_ID id; + int lit1, lit2; + CompactBinary (Clause *c, LRAT_ID i, int l1, int l2) + : clause (c), id (i), lit1 (l1), lit2 (l2) {} + CompactBinary () : clause (nullptr), id (0), lit1 (0), lit2 (0) {} +}; + +struct Hash { + Hash (std::array &ncs) : nonces (ncs) {} + std::array &nonces; + size_t operator() (const Gate *const g) const; +}; + +struct Rewrite { + int src, dst; + LRAT_ID id1; + LRAT_ID id2; + + Rewrite (int _src, int _dst, LRAT_ID _id1, LRAT_ID _id2) + : src (_src), dst (_dst), id1 (_id1), id2 (_id2) {} + Rewrite () : src (0), dst (0), id1 (0), id2 (0) {} +}; + +struct Closure { + + Closure (Internal *i); + + Internal *const internal; + vector extra_clauses; + vector binaries; + std::vector> offsetsize; + bool full_watching = false; + std::array nonces; + typedef unordered_set GatesTable; + + vector scheduled; + vector marks; + vector mu1_ids, mu2_ids, + mu4_ids; // remember the ids and the literal. 2 and 4 are + // only used for lrat proofs, but we need 1 to + // promote binary clauses to irredundant + + vector lits; // result of definitions + vector rhs; // stack for storing RHS + vector unsimplified; // stack for storing unsimplified version (XOR, + // ITEs) for DRAT proof + vector chain; // store clauses to be able to delete them properly + vector clause; // storing partial clauses + vector + glargecounts; // count for large clauses to complement internal->noccs + vector gnew_largecounts; // count for large clauses to + // complement internal->noccs + GatesTable table; + std::array condbin; + std::array condeq; + + std::vector new_unwatched_binary_clauses; + // LRAT proofs + vector resolvent_analyzed; + mutable vector lrat_chain; // storing LRAT chain + +#ifdef LOGGING + uint64_t fresh_id; +#endif + + uint64_t &new_largecounts (int lit); + uint64_t &largecounts (int lit); + + void unmark_all (); + vector representant; // union-find + vector eager_representant; // union-find + vector representant_id; // lrat version of union-find + vector eager_representant_id; // lrat version of union-find + int &representative (int lit); + int representative (int lit) const; + LRAT_ID &representative_id (int lit); + LRAT_ID representative_id (int lit) const; + int &eager_representative (int lit); + int eager_representative (int lit) const; + LRAT_ID &eager_representative_id (int lit); + LRAT_ID eager_representative_id (int lit) const; + std::vector lazy_propagated_idx; + char &lazy_propagated (int lit); + + int find_lrat_representative_with_marks (int lit); + // representative in the union-find structure in the lazy equivalences + int find_representative (int lit); + // find the representative and produce the binary clause representing the + // normalization from the literal to the result. + int find_representative_and_compress (int, bool update_eager = true); + // find the lazy representative for the `lit' and `-lit' + void find_representative_and_compress_both (int); + // find the eager representative + int find_eager_representative (int); + + // compreses the path from lit to the representative with a new clause if + // needed. Save internal->lrat_chain to avoid any issue. + int find_eager_representative_and_compress (int); + // Import the path from the literal and its negation to the representative + // in the lazy graph to the eager part, producing the binary clauses. + void import_lazy_and_find_eager_representative_and_compress_both ( + int); // generates clauses for -lit and lit + + // returns the ID of the LRAT clause for the normalization from the + // literal lit to its argument, assuming that the representative was + // already compressed. + LRAT_ID find_representative_lrat (int lit); + // returns the ID of the LRAT clause for the eager normalization from the + // literal lit to its argument assuming that the representative was + // already compressed. + LRAT_ID find_eager_representative_lrat (int lit); + + // Writes the LRAT chain required for the eager normalization to + // `lrat_chain`. + void produce_eager_representative_lrat (int lit); + // Writes the LRAT chain required for the lazy normalization to + // `lrat_chain`. + void produce_representative_lrat (int lit); + + // learns a binary clause if not unit + Clause *maybe_add_binary_clause (int a, int b); + // add binary clause + Clause *add_binary_clause (int a, int b); + // add tmp clause + Clause *add_tmp_binary_clause (int a, int b); + // add clause taking core of tmp or full + Clause *learn_binary_tmp_or_full_clause (int a, int b); + + // promotes a clause from redundant to irredundant. We do this for all + // clauses involved in gates to make sure that we produce correct result. + void promote_clause (Clause *); + + // Merge functions. We actually need different several versions for LRAT + // in order to simplify the proof production. + // + // When merging binary clauses, we can simply produce the LRAT chain by + // (1) using the two binary clauses and (2) the reason clause from the + // literals to the representatives. + // + // The same approach does not work for merging gates because the + // representative might be also a representative of another literal + // (because of eager rewriting), requiring to resolve more than once on + // the same literal. An example of this are the two gates 4=-2&7 and + // 6=-2&1, the rewriting 7=1 and the equivalence 4=1. The simple road of + // merging 6 and 4 (requires resolving away 1) + adding the rewrite 4 to 1 + // (requires adding 1) does not work. + // + // Therefore, we actually go for the more regular road and produce two + // equivalence: the merge from the LHS, followed by the actual equivalence + // (by combining it with the rewrite). In DRAT this is less important + // because the checker finds a chain and is less restricted than our LRAT + // chain. + bool merge_literals_equivalence (int lit, int other, Clause *c1, + Clause *c2); + bool merge_literals_lrat (Gate *g, Gate *h, int lit, int other, + const std::vector & = {}, + const std::vector & = {}); + bool merge_literals_lrat (int lit, int other, + const std::vector & = {}, + const std::vector & = {}); + + // proof production + vector lrat_chain_and_gate; + void push_lrat_id (const Clause *const c, int lit); + void push_lrat_unit (int lit); + + // pushes the clause with the reasons to rewrite clause + // unless: + // - the rewriting is not necessary (resolvent_marked == 1) + // - it is overwritten by one of the arguments + void push_id_and_rewriting_lrat_unit (Clause *c, Rewrite rewrite1, + std::vector &chain, + bool = true, + Rewrite rewrite2 = Rewrite (), + int execept_lhs = 0, + int except_lhs2 = 0); + void push_id_and_rewriting_lrat_full (Clause *c, Rewrite rewrite1, + std::vector &chain, + bool = true, + Rewrite rewrite2 = Rewrite (), + int execept_lhs = 0, + int except_lhs2 = 0); + // TODO: does nothing except pushing on the stack, remove! + void push_id_on_chain (std::vector &chain, Clause *c); + // TODO: does nothing except pushing on the stack, remove! + void push_id_on_chain (std::vector &chain, + const std::vector &c); + // TODO: does nothing except pushing on the stack, remove! + void push_id_on_chain (std::vector &chain, Rewrite rewrite, int); + void update_and_gate_build_lrat_chain ( + Gate *g, Gate *h, std::vector &extra_reasons_lit, + std::vector &extra_reasons_ulit, bool remove_units = true); + void update_and_gate_unit_build_lrat_chain ( + Gate *g, int src, LRAT_ID id1, LRAT_ID id2, int dst, + std::vector &extra_reasons_lit, + std::vector &extra_reasons_ulit); + // occs + vector gtab; + GOccs &goccs (int lit); + void connect_goccs (Gate *g, int lit); + vector garbage; + void mark_garbage (Gate *); + // remove the gate from the table + bool remove_gate (Gate *); + bool remove_gate (GatesTable::iterator git); + void index_gate (Gate *); + + // second counter for size, complements noccs + uint64_t &largecount (int lit); + + // simplification + bool skip_and_gate (Gate *g); + bool skip_xor_gate (Gate *g); + void update_and_gate (Gate *g, GatesTable::iterator, int src, int dst, + LRAT_ID id1, LRAT_ID id2, int falsified = 0, + int clashing = 0); + void update_xor_gate (Gate *g, GatesTable::iterator); + void shrink_and_gate (Gate *g, int falsified = 0, int clashing = 0); + bool simplify_gate (Gate *g); + void simplify_and_gate (Gate *g); + void simplify_ite_gate (Gate *g); + Clause *simplify_xor_clause (int lhs, Clause *); + void simplify_xor_gate (Gate *g); + bool simplify_gates (int lit); + void simplify_and_sort_xor_lrat_clauses (const vector &, + vector &, int, + int except2 = 0, bool flip = 0); + void simplify_unit_xor_lrat_clauses (const vector &, int); + + // rewriting + bool rewriting_lhs (Gate *g, int dst); + bool rewrite_gates (int dst, int src, LRAT_ID id1, LRAT_ID id2); + bool rewrite_gate (Gate *g, int dst, int src, LRAT_ID id1, LRAT_ID id2); + void rewrite_xor_gate (Gate *g, int dst, int src); + void rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, + LRAT_ID id2); + void rewrite_ite_gate (Gate *g, int dst, int src); + + size_t units; // next trail position to propagate + bool propagate_unit (int lit); + bool propagate_units (); + size_t propagate_units_and_equivalences (); + bool propagate_equivalence (int lit); + + // gates + void init_closure (); + void reset_closure (); + void reset_extraction (); + void reset_and_gate_extraction (); + void extract_and_gates (Closure &); + void extract_gates (); + void extract_and_gates_with_base_clause (Clause *c); + void init_and_gate_extraction (); + Gate *find_first_and_gate (Clause *base_clause, int lhs); + Gate *find_remaining_and_gate (Clause *base_clause, int lhs); + void extract_and_gates (); + + Gate *find_and_lits (const vector &rhs, Gate *except = nullptr); + // rhs is sorted, so passing by copy + Gate *find_gate_lits (const vector &rhs, Gate_Type typ, + Gate *except = nullptr); + Gate *find_xor_lits (const vector &rhs); + // not const to normalize negations, also fixes the order of the LRAT + Gate *find_ite_gate (Gate *, bool &); + Gate *find_xor_gate (Gate *); + + void reset_xor_gate_extraction (); + void init_xor_gate_extraction (std::vector &candidates); + LRAT_ID check_and_add_to_proof_chain (vector &clause); + void add_xor_matching_proof_chain (Gate *g, int lhs1, + const vector &, + int lhs2, vector &, + vector &); + void add_xor_shrinking_proof_chain (Gate *g, int src); + void extract_xor_gates (); + void extract_xor_gates_with_base_clause (Clause *c); + Clause *find_large_xor_side_clause (std::vector &lits); + + void merge_condeq (int cond, lit_equivalences &condeq, + lit_equivalences ¬_condeq); + void find_conditional_equivalences (int lit, lit_implications &condbin, + lit_equivalences &condeq); + void copy_conditional_equivalences (int lit, lit_implications &condbin); + void check_ite_implied (int lhs, int cond, int then_lit, int else_lit); + void check_ite_gate_implied (Gate *g); + void check_and_gate_implied (Gate *g); + void check_ite_lrat_reasons (Gate *g, bool = false); + void check_contained_module_rewriting (Clause *c, int lit, bool, + int except); + void delete_proof_chain (); + + // ite gate extraction + void extract_ite_gates_of_literal (int); + void extract_ite_gates_of_variable (int idx); + void extract_condeq_pairs (int lit, lit_implications &condbin, + lit_equivalences &condeq); + void init_ite_gate_extraction (std::vector &candidates); + lit_implications::const_iterator find_lit_implication_second_literal ( + int lit, lit_implications::const_iterator begin, + lit_implications::const_iterator end); + void search_condeq (int lit, int pos_lit, + lit_implications::const_iterator pos_begin, + lit_implications::const_iterator pos_end, int neg_lit, + lit_implications::const_iterator neg_begin, + lit_implications::const_iterator neg_end, + lit_equivalences &condeq); + void reset_ite_gate_extraction (); + void extract_ite_gates (); + + void forward_subsume_matching_clauses (); + + void extract_congruence (); + + void add_ite_matching_proof_chain (Gate *g, Gate *h, int lhs1, int lhs2, + std::vector &reasons1, + std::vector &reasons2); + void add_ite_turned_and_binary_clauses (Gate *g); + Gate *new_and_gate (Clause *, int); + Gate *new_ite_gate (int lhs, int cond, int then_lit, int else_lit, + std::vector &&clauses); + Gate *new_xor_gate (const vector &, int); + // check + void check_xor_gate_implied (Gate const *const); + void check_ternary (int a, int b, int c); + void check_binary_implied (int a, int b); + void check_implied (); + + // learn units. You can delay units if you want to learn several at once before + // propagation. Otherwise, propagate! If you need propagation even if nothing is set, use the + // second parameter. + // + // The function can also learn the empty clause if the unit is already set. Do not add the unit in + // the chain! + bool learn_congruence_unit (int unit, bool = false, bool = false); + bool fully_propagate (); + void learn_congruence_unit_falsifies_lrat_chain (Gate *g, int src, + int dst, + int clashing, + int falsified, int unit); + void learn_congruence_unit_when_lhs_set (Gate *g, int src, LRAT_ID id1, + LRAT_ID id2, int dst); + + void find_units (); + void find_equivalences (); + void subsume_clause (Clause *subsuming, Clause *subsumed); + bool find_subsuming_clause (Clause *c); + void produce_rewritten_clause_lrat_and_clean (vector &, + int execept_lhs = 0, + bool = true); + // rewrite the clause using eager rewriting and rew1 and rew2, except for + // 2 literals Usage: + // - the except are used to ignore LHS of gates that have not and should + // not be rewritten. + // - TODO: except_lhs2 should never be used actually + // - the Rewrite are for additional rewrite to allow for lazy rewrites + // to be taken into account without being added to the eager rewriting + // (yet) + Clause *produce_rewritten_clause_lrat (Clause *c, int execept_lhs = 0, + bool remove_units = true, bool = true); + void produce_rewritten_clause_lrat (vector &, + int execept_lhs = 0, + bool = true); + void compute_rewritten_clause_lrat_simple (Clause *c, int except); + // variant where we update the indices after removing the tautologies and + // remove the tautological clauses + void produce_rewritten_clause_lrat_and_clean ( + std::vector &litIds, int except_lhs, + size_t &old_position1, size_t &old_position2, + bool remove_units = true); + // binary extraction and ternary strengthening + void extract_binaries (); + bool find_binary (int, int) const; + + Clause *new_tmp_clause (std::vector &clause); + Clause *maybe_promote_tmp_binary_clause (Clause *); + void check_not_tmp_binary_clause (Clause *c); + Clause *new_clause (); + // + void sort_literals_by_var (vector &rhs); + void sort_literals_by_var_except (vector &rhs, int, int except2 = 0); + + // schedule + queue schedule; + void schedule_literal (int lit); + void add_clause_to_chain (std::vector, LRAT_ID); + // proof. If delete_id is non-zero, then delete the clause instead of + // learning it + LRAT_ID simplify_and_add_to_proof_chain (vector &unsimplified, + LRAT_ID delete_id = 0); + + // we define our own wrapper as cadical has otherwise a non-compatible + // marking system + signed char &marked (int lit); + void set_mu1_reason (int lit, Clause *c); + void set_mu2_reason (int lit, Clause *c); + void set_mu4_reason (int lit, Clause *c); + LitClausePair marked_mu1 (int lit); + LitClausePair marked_mu2 (int lit); + LitClausePair marked_mu4 (int lit); + + // XOR + uint32_t number_from_xor_reason_reversed (const std::vector &rhs); + uint32_t number_from_xor_reason (const std::vector &rhs, int, + int except2 = 0, bool flip = 0); + void gate_sort_lrat_reasons (std::vector &, int, + int except2 = 0, bool flip = 0); + void gate_sort_lrat_reasons (LitClausePair &, int, int except2 = 0, + bool flip = 0); + + bool rewrite_ite_gate_to_and (Gate *g, int dst, int src, size_t c, + size_t d, int cond_lit_to_learn_if_degenerated); + void produce_ite_merge_then_else_reasons ( + Gate *g, int dst, int src, std::vector &reasons_implication, + std::vector &reasons_back); + void produce_ite_merge_lhs_then_else_reasons ( + Gate *g, std::vector &reasons_implication, + std::vector &reasons_back, + std::vector &reasons_unit, bool, bool &); + void rewrite_ite_gate_update_lrat_reasons (Gate *g, int src, int dst); + void simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, size_t idx1, + size_t idx2); + void merge_and_gate_lrat_produce_lrat ( + Gate *g, Gate *h, std::vector &reasons_lrat, + std::vector &reasons_lrat_back, bool remove_units = true); + // first index is a binary clause after unit propagation and the second + // has length 3 + bool simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, + int removed); + void + merge_ite_gate_same_then_else_lrat (std::vector &clauses, + std::vector &reasons_implication, + std::vector &reasons_back); + void simplify_ite_gate_then_else_set ( + Gate *g, std::vector &reasons_implication, + std::vector &reasons_back, size_t idx1, size_t idx2); + + void simplify_ite_gate_condition_set ( + Gate *g, std::vector &reasons_lrat, + std::vector &reasons_back_lrat, size_t idx1, size_t idx2); + bool normalize_ite_lits_gate (Gate *rhs); +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/constrain.cpp b/src/sat/cadical/constrain.cpp new file mode 100644 index 000000000..e7f7b7ef9 --- /dev/null +++ b/src/sat/cadical/constrain.cpp @@ -0,0 +1,64 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +void Internal::constrain (int lit) { + if (lit) + constraint.push_back (lit); + else { + if (level) + backtrack (); + LOG (constraint, "shrinking constraint"); + bool satisfied_constraint = false; + const vector::const_iterator end = constraint.end (); + vector::iterator i = constraint.begin (); + for (vector::const_iterator j = i; j != end; j++) { + int tmp = marked (*j); + if (tmp > 0) { + LOG ("removing duplicated literal %d from constraint", *j); + } else if (tmp < 0) { + LOG ("tautological since both %d and %d occur in constraint", -*j, + *j); + satisfied_constraint = true; + break; + } else { + tmp = val (*j); + if (tmp < 0) { + LOG ("removing falsified literal %d from constraint clause", *j); + } else if (tmp > 0) { + LOG ("satisfied constraint with literal %d", *j); + satisfied_constraint = true; + break; + } else { + *i++ = *j; + mark (*j); + } + } + } + constraint.resize (i - constraint.begin ()); + for (const auto &lit : constraint) + unmark (lit); + if (satisfied_constraint) + constraint.clear (); + else if (constraint.empty ()) { + unsat_constraint = true; + if (!conflict_id) + marked_failed = false; // allow to trigger failing () + } else + for (const auto lit : constraint) + freeze (lit); + } +} + +bool Internal::failed_constraint () { return unsat_constraint; } + +void Internal::reset_constraint () { + for (auto lit : constraint) + melt (lit); + LOG ("cleared %zd constraint literals", constraint.size ()); + constraint.clear (); + unsat_constraint = false; + marked_failed = true; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/contract.cpp b/src/sat/cadical/contract.cpp new file mode 100644 index 000000000..8547d0306 --- /dev/null +++ b/src/sat/cadical/contract.cpp @@ -0,0 +1,27 @@ +#ifndef NCONTRACTS + +#include "internal.hpp" + +namespace CaDiCaL { + +void fatal_message_start (); + +// See comments in 'contract.hpp'. Ugly hack we keep for now. + +void require_solver_pointer_to_be_non_zero (const void *ptr, + const char *function_name, + const char *file_name) { + if (ptr) + return; + fatal_message_start (); + fprintf (stderr, + "invalid API usage of '%s' in '%s': " + "solver 'this' pointer zero (not initialized)\n", + function_name, file_name); + fflush (stderr); + abort (); +} + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/contract.hpp b/src/sat/cadical/contract.hpp new file mode 100644 index 000000000..3e98867c0 --- /dev/null +++ b/src/sat/cadical/contract.hpp @@ -0,0 +1,136 @@ +#ifndef _contract_hpp_INCLUDED +#define _contract_hpp_INCLUDED + +/*------------------------------------------------------------------------*/ +#ifndef NCONTRACTS +/*------------------------------------------------------------------------*/ + +// If the user violates API contracts while calling functions declared in +// 'cadical.hpp' and implemented in 'solver.cpp' then an error is reported. +// Currently we also force aborting the program. In the future it might be +// better to allow the user to provide a call back function, which then can +// for instance throw a C++ exception or execute a 'longjmp' in 'C' etc. + +#define CONTRACT_VIOLATED(...) \ + do { \ + fatal_message_start (); \ + fprintf (stderr, \ + "invalid API usage of '%s' in '%s': ", __PRETTY_FUNCTION__, \ + __FILE__); \ + fprintf (stderr, __VA_ARGS__); \ + fputc ('\n', stderr); \ + fflush (stderr); \ + abort (); \ + } while (0) + +/*------------------------------------------------------------------------*/ + +namespace CaDiCaL { + +// It would be much easier to just write 'REQUIRE (this, "not initialized")' +// which however produces warnings due to the '-Wnonnull' check. Note, that +// 'this' is always assumed to be non zero in modern C++. Much worse, if we +// use instead 'this != 0' or something similar like 'this != nullptr' then +// optimization silently removes this check ('gcc-7.4.0' at least) even +// though of course a zero pointer might be used as 'this' if the user did +// not initialize it. The only solution I found is to disable optimization +// for this check. It does not seem to be necessary for 'clang++' though +// ('clang++-6.0.0' at least). The alternative is to not check that the +// user forgot to initialize the solver pointer, but as long this works we +// keep this ugly hack. It also forces the function not to be inlined. +// The actual code I is in 'contract.cpp'. +// +void require_solver_pointer_to_be_non_zero (const void *ptr, + const char *function_name, + const char *file_name); +#define REQUIRE_NON_ZERO_THIS() \ + do { \ + require_solver_pointer_to_be_non_zero (this, __PRETTY_FUNCTION__, \ + __FILE__); \ + } while (0) + +} // namespace CaDiCaL + +/*------------------------------------------------------------------------*/ + +// These are common shortcuts for 'Solver' API contracts (requirements). + +#define REQUIRE(COND, ...) \ + do { \ + if ((COND)) \ + break; \ + CONTRACT_VIOLATED (__VA_ARGS__); \ + } while (0) + +#define REQUIRE_INITIALIZED() \ + do { \ + REQUIRE_NON_ZERO_THIS (); \ + REQUIRE (external, "external solver not initialized"); \ + REQUIRE (internal, "internal solver not initialized"); \ + } while (0) + +#define REQUIRE_VALID_STATE() \ + do { \ + REQUIRE_INITIALIZED (); \ + REQUIRE (this->state () & VALID, "solver in invalid state"); \ + } while (0) + +#define REQUIRE_READY_STATE() \ + do { \ + REQUIRE_VALID_STATE (); \ + REQUIRE (state () != ADDING, \ + "clause incomplete (terminating zero not added)"); \ + } while (0) + +#define REQUIRE_VALID_OR_SOLVING_STATE() \ + do { \ + REQUIRE_INITIALIZED (); \ + REQUIRE (this->state () & (VALID | SOLVING), \ + "solver neither in valid nor solving state"); \ + } while (0) + +#define REQUIRE_VALID_LIT(LIT) \ + do { \ + REQUIRE ((int) (LIT) && ((int) (LIT)) != INT_MIN, \ + "invalid literal '%d'", (int) (LIT)); \ + REQUIRE (external->is_valid_input ((int) (LIT)), \ + "extension variable %d defined by the solver", (int) (LIT)); \ + } while (0) + +#define REQUIRE_STEADY_STATE() \ + do { \ + REQUIRE_INITIALIZED (); \ + REQUIRE (this->state () & STEADY, "solver is not in steady state"); \ + } while (0) + +/*------------------------------------------------------------------------*/ +#else // NCONTRACTS +/*------------------------------------------------------------------------*/ + +#define REQUIRE(...) \ + do { \ + } while (0) +#define REQUIRE_INITIALIZED() \ + do { \ + } while (0) +#define REQUIRE_VALID_STATE() \ + do { \ + } while (0) +#define REQUIRE_READY_STATE() \ + do { \ + } while (0) +#define REQUIRE_VALID_OR_SOLVING_STATE() \ + do { \ + } while (0) +#define REQUIRE_VALID_LIT(...) \ + do { \ + } while (0) +#define REQUIRE_STEADY_STATE() \ + do { \ + } while (0) + +/*------------------------------------------------------------------------*/ +#endif +/*------------------------------------------------------------------------*/ + +#endif diff --git a/src/sat/cadical/cover.cpp b/src/sat/cadical/cover.cpp new file mode 100644 index 000000000..bd8781630 --- /dev/null +++ b/src/sat/cadical/cover.cpp @@ -0,0 +1,704 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Covered clause elimination (CCE) is described in our short LPAR-10 paper +// and later in more detail in our JAIR'15 article. Actually implement +// the asymmetric version which adds asymmetric literals too but still call +// it 'CCE' in the following (and not 'ACCE'). This implementation provides +// a simplified and cleaner version of the one implemented before in +// Lingeling. We still follow quite closely the original description in the +// literature, which is based on asymmetric literal addition (ALA) and +// covered literal addition (CLA). Both can be seen as kind of propagation, +// where the literals in the original and then extended clause are assigned +// to false, and the literals on the trail (actually we use our own 'added' +// stack for that) make up the extended clause. The ALA steps can be +// implemented by simple propagation (copied from 'propagate.cpp') using +// watches, while the CLA steps need full occurrence lists to determine the +// resolution candidate clauses. The CCE is successful if a conflict is +// found during ALA steps or if during a CLA step all resolution candidates +// of a literal on the trail are satisfied (the extended clause is blocked). + +struct Coveror { + std::vector added; // acts as trail + std::vector extend; // extension stack for witness + std::vector covered; // clause literals or added through CLA + std::vector intersection; // of literals in resolution candidates + + size_t alas, clas; // actual number of ALAs and CLAs + + struct { + size_t added, covered; + } next; // propagate next ... + + Coveror () : alas (0), clas (0) {} +}; + +/*------------------------------------------------------------------------*/ + +// Push on the extension stack a clause made up of the given literal, the +// original clause (initially copied to 'covered') and all the added covered +// literals so far. The given literal will act as blocking literal for that +// clause, if CCE is successful. Only in this case, this private extension +// stack is copied to the actual extension stack of the solver. Note, that +// even though all 'added' clauses correspond to the extended clause, we +// only need to save the original and added covered literals. + +inline void Internal::cover_push_extension (int lit, Coveror &coveror) { + coveror.extend.push_back (0); + coveror.extend.push_back (lit); // blocking literal comes first + bool found = false; + for (const auto &other : coveror.covered) + if (lit == other) + assert (!found), found = true; + else + coveror.extend.push_back (other); + assert (found); + (void) found; +} + +// Successful covered literal addition (CLA) step. + +inline void Internal::covered_literal_addition (int lit, Coveror &coveror) { + require_mode (COVER); + assert (level == 1); + cover_push_extension (lit, coveror); + for (const auto &other : coveror.intersection) { + LOG ("covered literal addition %d", other); + assert (!vals[other]), assert (!vals[-other]); + set_val (other, -1); + coveror.covered.push_back (other); + coveror.added.push_back (other); + coveror.clas++; + } + coveror.next.covered = 0; +} + +// Successful asymmetric literal addition (ALA) step. + +inline void Internal::asymmetric_literal_addition (int lit, + Coveror &coveror) { + require_mode (COVER); + assert (level == 1); + LOG ("initial asymmetric literal addition %d", lit); + assert (!vals[lit]), assert (!vals[-lit]); + set_val (lit, -1); + coveror.added.push_back (lit); + coveror.alas++; + coveror.next.covered = 0; +} + +/*------------------------------------------------------------------------*/ + +// In essence copied and adapted from 'propagate' in 'propagate.cpp'. Since +// this function is also a hot-spot here in 'cover' we specialize it in the +// same spirit as 'probe_propagate' and 'vivify_propagate'. Please refer to +// the detailed comments for 'propagate' in 'propagate.cpp' for details. + +bool Internal::cover_propagate_asymmetric (int lit, Clause *ignore, + Coveror &coveror) { + require_mode (COVER); + stats.propagations.cover++; + assert (val (lit) < 0); + bool subsumed = false; + LOG ("asymmetric literal propagation of %d", lit); + Watches &ws = watches (lit); + const const_watch_iterator eow = ws.end (); + watch_iterator j = ws.begin (); + const_watch_iterator i = j; + while (!subsumed && i != eow) { + const Watch w = *j++ = *i++; + if (w.clause == ignore) + continue; // costly but necessary here ... + const signed char b = val (w.blit); + if (b > 0) + continue; + if (w.clause->garbage) + j--; + else if (w.binary ()) { + if (b < 0) { + LOG (w.clause, "found subsuming"); + subsumed = true; + } else + asymmetric_literal_addition (-w.blit, coveror); + } else { + literal_iterator lits = w.clause->begin (); + const int other = lits[0] ^ lits[1] ^ lit; + lits[0] = other, lits[1] = lit; + const signed char u = val (other); + if (u > 0) + j[-1].blit = other; + else { + const int size = w.clause->size; + const const_literal_iterator end = lits + size; + const literal_iterator middle = lits + w.clause->pos; + literal_iterator k = middle; + signed char v = -1; + int r = 0; + while (k != end && (v = val (r = *k)) < 0) + k++; + if (v < 0) { + k = lits + 2; + assert (w.clause->pos <= size); + while (k != middle && (v = val (r = *k)) < 0) + k++; + } + w.clause->pos = k - lits; + assert (lits + 2 <= k), assert (k <= w.clause->end ()); + if (v > 0) + j[-1].blit = r; + else if (!v) { + LOG (w.clause, "unwatch %d in", lit); + lits[1] = r; + *k = lit; + watch_literal (r, lit, w.clause); + j--; + } else if (!u) { + assert (v < 0); + asymmetric_literal_addition (-other, coveror); + } else { + assert (u < 0), assert (v < 0); + LOG (w.clause, "found subsuming"); + subsumed = true; + break; + } + } + } + } + if (j != i) { + while (i != eow) + *j++ = *i++; + ws.resize (j - ws.begin ()); + } + return subsumed; +} + +// Covered literal addition (which needs full occurrence lists). The +// function returns 'true' if the extended clause is blocked on 'lit.' + +bool Internal::cover_propagate_covered (int lit, Coveror &coveror) { + require_mode (COVER); + + assert (val (lit) < 0); + if (frozen (lit)) { + LOG ("no covered propagation on frozen literal %d", lit); + return false; + } + + stats.propagations.cover++; + + LOG ("covered propagation of %d", lit); + assert (coveror.intersection.empty ()); + + Occs &os = occs (-lit); + const auto end = os.end (); + bool first = true; + + // Compute the intersection of the literals in all the clauses with + // '-lit'. If all these clauses are double satisfied then we know that + // the extended clauses (in 'added') is blocked. All literals in the + // intersection can be added as covered literal. As soon the intersection + // becomes empty (during traversal of clauses with '-lit') we abort. + + for (auto i = os.begin (); i != end; i++) { + + Clause *c = *i; + if (c->garbage) + continue; + + // First check whether clause is 'blocked', i.e., is double satisfied. + + bool blocked = false; + for (const auto &other : *c) { + if (other == -lit) + continue; + const signed char tmp = val (other); + if (tmp < 0) + continue; + if (tmp > 0) { + blocked = true; + break; + } + } + if (blocked) { // ... if 'c' is double satisfied. + LOG (c, "blocked"); + continue; // with next clause with '-lit'. + } + + if (first) { + + // Copy and mark literals of first clause. + + for (const auto &other : *c) { + if (other == -lit) + continue; + const signed char tmp = val (other); + if (tmp < 0) + continue; + assert (!tmp); + coveror.intersection.push_back (other); + mark (other); + } + + first = false; + + } else { + + // Unmark all literals in current clause. + + for (const auto &other : *c) { + if (other == -lit) + continue; + signed char tmp = val (other); + if (tmp < 0) + continue; + assert (!tmp); + tmp = marked (other); + if (tmp > 0) + unmark (other); + } + + // Then remove from intersection all marked literals. + + const auto end = coveror.intersection.end (); + auto j = coveror.intersection.begin (); + for (auto k = j; k != end; k++) { + const int other = *j++ = *k; + const int tmp = marked (other); + assert (tmp >= 0); + if (tmp) + j--, unmark (other); // remove marked and unmark it + else + mark (other); // keep unmarked and mark it + } + const size_t new_size = j - coveror.intersection.begin (); + coveror.intersection.resize (new_size); + + if (!coveror.intersection.empty ()) + continue; + + // No covered literal addition candidates in the intersection left! + // Move this clause triggering early abort to the beginning. + // This is a common move to front strategy to minimize effort. + + auto begin = os.begin (); + while (i != begin) { + auto prev = i - 1; + *i = *prev; + i = prev; + } + *begin = c; + + break; // early abort ... + } + } + + bool res = false; + if (first) { + LOG ("all resolution candidates with %d blocked", -lit); + assert (coveror.intersection.empty ()); + cover_push_extension (lit, coveror); + res = true; + } else if (coveror.intersection.empty ()) { + LOG ("empty intersection of resolution candidate literals"); + } else { + LOG (coveror.intersection, + "non-empty intersection of resolution candidate literals"); + covered_literal_addition (lit, coveror); + unmark (coveror.intersection); + coveror.intersection.clear (); + coveror.next.covered = 0; // Restart covering. + } + + unmark (coveror.intersection); + coveror.intersection.clear (); + + return res; +} + +/*------------------------------------------------------------------------*/ + +bool Internal::cover_clause (Clause *c, Coveror &coveror) { + + require_mode (COVER); + assert (!c->garbage); + + LOG (c, "trying covered clauses elimination on"); + bool satisfied = false; + for (const auto &lit : *c) + if (val (lit) > 0) + satisfied = true; + + if (satisfied) { + LOG (c, "clause already satisfied"); + mark_garbage (c); + return false; + } + + assert (coveror.added.empty ()); + assert (coveror.extend.empty ()); + assert (coveror.covered.empty ()); + + assert (!level); + level = 1; + LOG ("assuming literals of candidate clause"); + for (const auto &lit : *c) { + if (val (lit)) + continue; + asymmetric_literal_addition (lit, coveror); + coveror.covered.push_back (lit); + } + + bool tautological = false; + + coveror.next.added = coveror.next.covered = 0; + + while (!tautological) { + if (coveror.next.added < coveror.added.size ()) { + const int lit = coveror.added[coveror.next.added++]; + tautological = cover_propagate_asymmetric (lit, c, coveror); + } else if (coveror.next.covered < coveror.covered.size ()) { + const int lit = coveror.covered[coveror.next.covered++]; + tautological = cover_propagate_covered (lit, coveror); + } else + break; + } + + if (tautological) { + if (coveror.extend.empty ()) { + stats.cover.asymmetric++; + stats.cover.total++; + LOG (c, "asymmetric tautological"); + } else { + stats.cover.blocked++; + stats.cover.total++; + // Only copy extension stack if successful. + int prev = INT_MIN; + bool already_pushed = false; + int64_t last_id = 0; + LOG (c, "covered tautological"); + assert (clause.empty ()); + LOG (coveror.extend, "extension = "); + for (const auto &other : coveror.extend) { + if (!prev) { + // are we finishing a clause? + if (already_pushed) { + // add missing literals that are not needed for covering + // but avoid RAT proofs + for (auto i = 0, j = 0; i < c->size; ++i, ++j) { + const int lit = c->literals[i]; + if (j >= (int) coveror.covered.size () || + c->literals[i] != coveror.covered[j]) { + --j; + LOG ("adding lit %d not needed for ATA", lit); + clause.push_back (lit); + external->push_clause_literal_on_extension_stack (lit); + } + } + } + if (proof && already_pushed) { + if (lrat) + lrat_chain.push_back (c->id); + LOG ("LEARNING clause with id %" PRId64, last_id); + proof->add_derived_clause (last_id, false, clause, lrat_chain); + proof->weaken_plus (last_id, clause); + lrat_chain.clear (); + } + last_id = ++clause_id; + external->push_zero_on_extension_stack (); + external->push_witness_literal_on_extension_stack (other); + external->push_zero_on_extension_stack (); + external->push_id_on_extension_stack (last_id); + external->push_zero_on_extension_stack (); + clause.clear (); + already_pushed = true; + } + if (other) { + external->push_clause_literal_on_extension_stack (other); + clause.push_back (other); + LOG (clause, "current clause is"); + } + prev = other; + } + + if (proof) { + // add missing literals that are not needed for covering + // but avoid RAT proofs + for (auto i = 0, j = 0; i < c->size; ++i, ++j) { + const int lit = c->literals[i]; + if (j >= (int) coveror.covered.size () || + c->literals[i] != coveror.covered[j]) { + --j; + LOG ("adding lit %d not needed for ATA", lit); + clause.push_back (lit); + external->push_clause_literal_on_extension_stack (lit); + } + } + if (lrat) + lrat_chain.push_back (c->id); + proof->add_derived_clause (last_id, false, clause, lrat_chain); + proof->weaken_plus (last_id, clause); + lrat_chain.clear (); + } + clause.clear (); + + mark_garbage (c); + } + } + + // Backtrack and 'unassign' all literals. + + assert (level == 1); + for (const auto &lit : coveror.added) + set_val (lit, 0); + level = 0; + + coveror.covered.clear (); + coveror.extend.clear (); + coveror.added.clear (); + + return tautological; +} + +/*------------------------------------------------------------------------*/ + +// Not yet tried and larger clauses are tried first. + +struct clause_covered_or_smaller { + bool operator() (const Clause *a, const Clause *b) { + if (a->covered && !b->covered) + return true; + if (!a->covered && b->covered) + return false; + return a->size < b->size; + } +}; + +int64_t Internal::cover_round () { + + if (unsat) + return 0; + + init_watches (); + connect_watches (true); // irredundant watches only is enough + + int64_t delta = stats.propagations.search; + delta *= 1e-3 * opts.covereffort; + if (delta < opts.covermineff) + delta = opts.covermineff; + if (delta > opts.covermaxeff) + delta = opts.covermaxeff; + delta = max (delta, ((int64_t) 2) * active ()); + + PHASE ("cover", stats.cover.count, + "covered clause elimination limit of %" PRId64 " propagations", + delta); + + int64_t limit = stats.propagations.cover + delta; + + init_occs (); + + vector schedule; + Coveror coveror; + + // First connect all clauses and find all not yet tried clauses. + // +#ifndef QUIET + int64_t untried = 0; +#endif + // + for (auto c : clauses) { + assert (!c->frozen); + if (c->garbage) + continue; + if (c->redundant) + continue; + bool satisfied = false, allfrozen = true; + for (const auto &lit : *c) + if (val (lit) > 0) { + satisfied = true; + break; + } else if (allfrozen && !frozen (lit)) + allfrozen = false; + if (satisfied) { + mark_garbage (c); + continue; + } + if (allfrozen) { + c->frozen = true; + continue; + } + for (const auto &lit : *c) + occs (lit).push_back (c); + if (c->size < opts.coverminclslim) + continue; + if (c->size > opts.covermaxclslim) + continue; + if (c->covered) + continue; + schedule.push_back (c); +#ifndef QUIET + untried++; +#endif + } + + if (schedule.empty ()) { + + PHASE ("cover", stats.cover.count, "no previously untried clause left"); + + for (auto c : clauses) { + if (c->garbage) + continue; + if (c->redundant) + continue; + if (c->frozen) { + c->frozen = false; + continue; + } + if (c->size < opts.coverminclslim) + continue; + if (c->size > opts.covermaxclslim) + continue; + assert (c->covered); + c->covered = false; + schedule.push_back (c); + } + } else { // Mix of tried and not tried clauses .... + + for (auto c : clauses) { + if (c->garbage) + continue; + if (c->redundant) + continue; + if (c->frozen) { + c->frozen = false; + continue; + } + if (c->size < opts.coverminclslim) + continue; + if (c->size > opts.covermaxclslim) + continue; + if (!c->covered) + continue; + schedule.push_back (c); + } + } + + stable_sort (schedule.begin (), schedule.end (), + clause_covered_or_smaller ()); + +#ifndef QUIET + const size_t scheduled = schedule.size (); + PHASE ("cover", stats.cover.count, + "scheduled %zd clauses %.0f%% with %" PRId64 " untried %.0f%%", + scheduled, percent (scheduled, stats.current.irredundant), untried, + percent (untried, scheduled)); +#endif + + // Heuristically it should be beneficial to intersect with smaller clauses + // first, since then the chances are higher that the intersection of + // resolution candidates becomes emptier earlier. + + for (auto lit : lits) { + if (!active (lit)) + continue; + Occs &os = occs (lit); + stable_sort (os.begin (), os.end (), clause_smaller_size ()); + } + + // This is the main loop of trying to do CCE of candidate clauses. + // + int64_t covered = 0; + // + while (!terminated_asynchronously () && !schedule.empty () && + stats.propagations.cover < limit) { + Clause *c = schedule.back (); + schedule.pop_back (); + c->covered = true; + if (cover_clause (c, coveror)) + covered++; + } + +#ifndef QUIET + const size_t remain = schedule.size (); + const size_t tried = scheduled - remain; + PHASE ("cover", stats.cover.count, + "eliminated %" PRId64 " covered clauses out of %zd tried %.0f%%", + covered, tried, percent (covered, tried)); + if (remain) + PHASE ("cover", stats.cover.count, + "remaining %zu clauses %.0f%% untried", remain, + percent (remain, scheduled)); + else + PHASE ("cover", stats.cover.count, "all scheduled clauses tried"); +#endif + reset_occs (); + reset_watches (); + + return covered; +} + +/*------------------------------------------------------------------------*/ + +bool Internal::cover () { + + if (!opts.cover) + return false; + if (unsat) + return false; + if (terminated_asynchronously ()) + return false; + if (!stats.current.irredundant) + return false; + + // TODO: Our current algorithm for producing the necessary clauses on the + // reconstruction stack for extending the witness requires a covered + // literal addition step which (empirically) conflicts with flushing + // during restoring clauses (see 'regr00{48,51}.trace') even though + // flushing during restore is disabled by default (as is covered clause + // elimination). The consequence of combining these two options + // ('opts.cover' and 'opts.restoreflush') can thus produce incorrect + // witness reconstruction and thus invalid witnesses. This is quite + // infrequent (one out of half billion mobical test cases) but as the two + // regression traces show, does happen. Thus we disable the combination. + // + if (opts.restoreflush) + return false; + + START_SIMPLIFIER (cover, COVER); + + stats.cover.count++; + + // During variable elimination unit clauses can be generated which need to + // be propagated properly over redundant clauses too. Since variable + // elimination avoids to have occurrence lists and watches at the same + // time this propagation is delayed until the end of variable elimination. + // Since we want to interleave CCE with it, we have to propagate here. + // Otherwise this triggers inconsistencies. + // + if (propagated < trail.size ()) { + init_watches (); + connect_watches (); // need to propagated over all clauses! + LOG ("elimination produced %zd units", + (size_t) (trail.size () - propagated)); + if (!propagate ()) { + LOG ("propagating units before covered clause elimination " + "results in empty clause"); + learn_empty_clause (); + assert (unsat); + } + reset_watches (); + } + assert (unsat || propagated == trail.size ()); + + int64_t covered = cover_round (); + + STOP_SIMPLIFIER (cover, COVER); + report ('c', !opts.reportall && !covered); + + return covered; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/cover.hpp b/src/sat/cadical/cover.hpp new file mode 100644 index 000000000..67e1bea77 --- /dev/null +++ b/src/sat/cadical/cover.hpp @@ -0,0 +1,34 @@ +#ifndef _cover_hpp_INCLUDED +#define _cover_hpp_INCLUDED + +/*------------------------------------------------------------------------*/ + +// This header only provides the 'COVER' macro for testing. It is unrelated +// to 'cover.cpp' which implements covered clause elimination (CCE), but we +// wanted to use the name base name in both cases. More explanation on CCE +// is provided in 'cover.cpp'. + +/*------------------------------------------------------------------------*/ + +// Coverage goal, used similar to 'assert' (but with flipped condition) and +// also included even if 'NDEBUG' is defined (in optimizing compilation). +// +// This should in essence not be used in production code. +// +// There seems to be no problem overloading the name 'COVER' of this macro +// with the constant 'COVER' of 'Internal::Mode' (surprisingly). + +#define COVER(COND) \ + do { \ + if (!(COND)) \ + break; \ + fprintf (stderr, \ + "%scadical%s: %s:%d: %s: Coverage goal %s`%s'%s reached.\n", \ + terr.bold_code (), terr.normal_code (), __FUNCTION__, \ + __LINE__, __FILE__, terr.green_code (), #COND, \ + terr.normal_code ()); \ + fflush (stderr); \ + abort (); \ + } while (0) + +#endif diff --git a/src/sat/cadical/decide.cpp b/src/sat/cadical/decide.cpp new file mode 100644 index 000000000..ca56ccdd7 --- /dev/null +++ b/src/sat/cadical/decide.cpp @@ -0,0 +1,252 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +// This function determines the next decision variable on the queue, without +// actually removing it from the decision queue, e.g., calling it multiple +// times without any assignment will return the same result. This is of +// course used below in 'decide' but also in 'reuse_trail' to determine the +// largest decision level to backtrack to during 'restart' without changing +// the assigned variables (if 'opts.restartreusetrail' is non-zero). + +int Internal::next_decision_variable_on_queue () { + int64_t searched = 0; + int res = queue.unassigned; + while (val (res)) + res = link (res).prev, searched++; + if (searched) { + stats.searched += searched; + update_queue_unassigned (res); + } + LOG ("next queue decision variable %d bumped %" PRId64 "", res, + bumped (res)); + return res; +} + +// This function determines the best decision with respect to score. +// +int Internal::next_decision_variable_with_best_score () { + int res = 0; + for (;;) { + res = scores.front (); + if (!val (res)) + break; + (void) scores.pop_front (); + } + LOG ("next decision variable %d with score %g", res, score (res)); + return res; +} + +int Internal::next_decision_variable () { + if (use_scores ()) + return next_decision_variable_with_best_score (); + else + return next_decision_variable_on_queue (); +} + +/*------------------------------------------------------------------------*/ + +// Implements phase saving as well using a target phase during +// stabilization unless decision phase is forced to the initial value +// of a phase is forced through the 'phase' option. + +int Internal::decide_phase (int idx, bool target) { + const int initial_phase = opts.phase ? 1 : -1; + int phase = 0; + if (force_saved_phase) + phase = phases.saved[idx]; + if (!phase) + phase = phases.forced[idx]; // swapped with opts.forcephase case! + if (!phase && opts.forcephase) + phase = initial_phase; + if (!phase && target) + phase = phases.target[idx]; + if (!phase) + phase = phases.saved[idx]; + + // The following should not be necessary and in some version we had even + // a hard 'COVER' assertion here to check for this. Unfortunately it + // triggered for some users and we could not get to the root cause of + // 'phase' still not being set here. The logic for phase and target + // saving is pretty complex, particularly in combination with local + // search, and to avoid running in such an issue in the future again, we + // now use this 'defensive' code here, even though such defensive code is + // considered bad programming practice. + // + if (!phase) + phase = initial_phase; + + return phase * idx; +} + +// The likely phase of an variable used in 'collect' for optimizing +// co-location of clauses likely accessed together during search. + +int Internal::likely_phase (int idx) { return decide_phase (idx, false); } + +/*------------------------------------------------------------------------*/ + +// adds new level to control and trail +// +void Internal::new_trail_level (int lit) { + level++; + control.push_back (Level (lit, trail.size ())); +} + +/*------------------------------------------------------------------------*/ + +bool Internal::satisfied () { + if ((size_t) level < assumptions.size () + (!!constraint.size ())) + return false; + if (num_assigned < (size_t) max_var) + return false; + assert (num_assigned == (size_t) max_var); + if (propagated < trail.size ()) + return false; + size_t assigned = num_assigned; + return (assigned == (size_t) max_var); +} + +bool Internal::better_decision (int lit, int other) { + int lit_idx = abs (lit); + int other_idx = abs (other); + if (stable) + return stab[lit_idx] > stab[other_idx]; + else + return btab[lit_idx] > btab[other_idx]; +} + +// Search for the next decision and assign it to the saved phase. Requires +// that not all variables are assigned. + +int Internal::decide () { + assert (!satisfied ()); + START (decide); + int res = 0; + if ((size_t) level < assumptions.size ()) { + const int lit = assumptions[level]; + assert (assumed (lit)); + const signed char tmp = val (lit); + if (tmp < 0) { + LOG ("assumption %d falsified", lit); + res = 20; + } else if (tmp > 0) { + LOG ("assumption %d already satisfied", lit); + new_trail_level (0); + LOG ("added pseudo decision level"); + notify_decision (); + } else { + LOG ("deciding assumption %d", lit); + search_assume_decision (lit); + } + } else if ((size_t) level == assumptions.size () && constraint.size ()) { + + int satisfied_lit = 0; // The literal satisfying the constrain. + int unassigned_lit = 0; // Highest score unassigned literal. + int previous_lit = 0; // Move satisfied literals to the front. + + const size_t size_constraint = constraint.size (); + +#ifndef 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; + } + + 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 NDEBUG + for (auto lit : constraint) + sum -= lit; + assert (!sum); // Checksum of literal should not change! +#endif + + } else { + + int decision = ask_decision (); + if ((size_t) level < assumptions.size () || + ((size_t) level == assumptions.size () && constraint.size ())) { + // Forced backtrack below pseudo decision levels. + // So one of the two branches above will handle it. + STOP (decide); + res = decide (); // STARTS and STOPS profiling + START (decide); + } else { + stats.decisions++; + if (!decision) { + int idx = next_decision_variable (); + const bool target = (opts.target > 1 || (stable && opts.target)); + decision = decide_phase (idx, target); + } + search_assume_decision (decision); + } + } + if (res) + marked_failed = false; + STOP (decide); + return res; +} +} // namespace CaDiCaL diff --git a/src/sat/cadical/decompose.cpp b/src/sat/cadical/decompose.cpp new file mode 100644 index 000000000..7e44ab742 --- /dev/null +++ b/src/sat/cadical/decompose.cpp @@ -0,0 +1,733 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +void Internal::decompose_analyze_binary_chain (DFS *dfs, int from) { + if (!lrat) + return; + LOG ("binary chain starting at %d", from); + DFS &from_dfs = dfs[vlit (from)]; + Clause *reason = from_dfs.parent; + if (!reason) + return; + assert (reason->size == 2); + mini_chain.push_back (reason->id); + int other = reason->literals[0]; + other = other == from ? -reason->literals[1] : -other; + Flags &f = flags (other); + if (f.seen) + return; + f.seen = true; + analyzed.push_back (other); + decompose_analyze_binary_chain (dfs, other); +} + +vector Internal::decompose_analyze_binary_clauses (DFS *dfs, + int from) { + vector result; + LOG ("binary chain starting at %d", from); + DFS &from_dfs = dfs[vlit (from)]; + Clause *reason = from_dfs.parent; + while (reason) { + result.push_back (reason); + assert (reason->size == 2); + int other = reason->literals[0]; + other = other == from ? -reason->literals[1] : -other; + Flags &f = flags (other); + if (f.seen) + break; + f.seen = true; + analyzed.push_back (other); + from = other; + DFS &from_dfs = dfs[vlit (from)]; + reason = from_dfs.parent; + } + return result; +} + +void Internal::decompose_conflicting_scc_lrat (DFS *dfs, vector &scc) { + if (!lrat) + return; + assert (lrat_chain.empty ()); + assert (mini_chain.empty ()); + for (auto &lit : scc) { + Flags &f = flags (lit); + if (f.seen) + return; + f.seen = true; + analyzed.push_back (lit); + decompose_analyze_binary_chain (dfs, lit); + for (auto p = mini_chain.rbegin (); p != mini_chain.rend (); p++) { + lrat_chain.push_back (*p); + } + mini_chain.clear (); + } + clear_analyzed_literals (); +} + +void Internal::build_lrat_for_clause ( + const vector> &dfs_chains, bool invert) { + assert (lrat); + LOG ("building chain for not subsumed clause"); + assert (lrat_chain.empty ()); + assert (sign_marked.empty ()); + // build chain for each replaced literal + for (const auto lit : clause) { + auto other = lit; + if (val (other) > 0) { + if (marked_decomposed (other)) + continue; + mark_decomposed (other); + int64_t id = unit_id (other); + lrat_chain.push_back (id); + continue; + } + assert (mini_chain.empty ()); + for (auto p : dfs_chains[vlit (other)]) { + if (marked_decomposed (other)) + continue; + mark_decomposed (other); + int implied = p->literals[0]; + implied = implied == other ? -p->literals[1] : -implied; + LOG ("ADDED %d -> %d (%" PRId64 ")", implied, other, p->id); + other = implied; + mini_chain.push_back (p->id); + if (val (implied) <= 0) + continue; + if (marked_decomposed (implied)) + break; + mark_decomposed (implied); + int64_t id = unit_id (implied); + mini_chain.push_back (id); + break; + } + if (invert) + for (auto p = mini_chain.rbegin (); p != mini_chain.rend (); p++) + lrat_chain.push_back (*p); + else + for (auto p = mini_chain.begin (); p != mini_chain.end (); p++) + lrat_chain.push_back (*p); + mini_chain.clear (); + } + clear_sign_marked_literals (); + LOG (lrat_chain, "lrat_chain:"); +} + +void Internal::clear_sign_marked_literals () { + LOG ("clearing %zd marked literals", sign_marked.size ()); + for (const auto &lit : sign_marked) { + // assert (marked_signed (lit)); violated on purpose in factor + unmark_decomposed (lit); + } + sign_marked.clear (); +} + +// This performs one round of Tarjan's algorithm, e.g., equivalent literal +// detection and substitution, on the whole formula. We might want to +// repeat it since its application might produce new binary clauses or +// units. Such units might even result in an empty clause. + +bool Internal::decompose_round () { + + if (!opts.decompose) + return false; + if (unsat) + return false; + if (terminated_asynchronously ()) + return false; + + assert (!level); + + START_SIMPLIFIER (decompose, DECOMP); + + stats.decompositions++; + + const size_t size_dfs = 2 * (1 + (size_t) max_var); + DFS *dfs = new DFS[size_dfs]; + DeferDeleteArray dfs_delete (dfs); + int *reprs = new int[size_dfs]; + DeferDeleteArray reprs_delete (reprs); + clear_n (reprs, size_dfs); + vector> dfs_chains; + dfs_chains.resize (size_dfs); + if (lrat) { + for (size_t i = 0; i > size_dfs; i++) { + vector empty; + dfs_chains[i] = empty; + } + } + + int substituted = 0; +#ifndef QUIET + int non_trivial_sccs = 0; + int before = active (); +#endif + unsigned dfs_idx = 0; + + vector work; // depth first search working stack + vector scc; // collects members of one SCC + + // The binary implication graph might have disconnected components and + // thus we have in general to start several depth first searches. + + for (auto root_idx : vars) { + if (unsat) + break; + if (!active (root_idx)) + continue; + for (int root_sign = -1; !unsat && root_sign <= 1; root_sign += 2) { + int root = root_sign * root_idx; + if (dfs[vlit (root)].min == TRAVERSED) + continue; // skip traversed + LOG ("new dfs search starting at root %d", root); + assert (work.empty ()); + assert (scc.empty ()); + work.push_back (root); + while (!unsat && !work.empty ()) { + int parent = work.back (); + DFS &parent_dfs = dfs[vlit (parent)]; + if (parent_dfs.min == TRAVERSED) { // skip traversed + assert (reprs[vlit (parent)]); + work.pop_back (); + } else { + assert (!reprs[vlit (parent)]); + + // Go over all implied literals, thus need to iterate over all + // binary watched clauses with the negation of 'parent'. + + Watches &ws = watches (-parent); + + // Two cases: Either the node has never been visited before, i.e., + // it's depth first search index is zero, then perform the + // 'pre-fix' work before visiting it's children. Otherwise all + // it's children and nodes reachable from those children have been + // visited and their minimum reachable depth first search index + // has been computed. This second case is the 'post-fix' work. + + if (parent_dfs.idx) { // post-fix + + work.pop_back (); // 'parent' done + + // Get the minimum reachable depth first search index reachable + // from the children of 'parent'. + + unsigned new_min = parent_dfs.min; + + for (const auto &w : ws) { + if (!w.binary ()) + continue; + const int child = w.blit; + if (!active (child)) + continue; + DFS &child_dfs = dfs[vlit (child)]; + if (new_min > child_dfs.min) + new_min = child_dfs.min; + } + + LOG ("post-fix work dfs search %d index %u reaches minimum %u", + parent, parent_dfs.idx, new_min); + + if (parent_dfs.idx == new_min) { // entry to SCC + + // All nodes on the 'scc' stack after and including 'parent' + // are in the same SCC. Their representative is computed as + // the smallest literal (index-wise) in the SCC. If the SCC + // contains both a literal and its negation, then the formula + // becomes unsatisfiable. + + if (lrat) { + assert (analyzed.empty ()); + int other, first = 0; + bool conflicting = false; + size_t j = scc.size (); + do { + assert (j > 0); + other = scc[--j]; + if (!first || vlit (other) < vlit (first)) + first = other; + Flags &f = flags (other); + if (other == -parent) { + conflicting = true; // conflicting scc + } + if (f.seen) { + continue; // also conflicting scc + } + f.seen = true; + analyzed.push_back (other); + } while (other != parent); + + assert (!conflicting || first > 0); + vector to_justify; + if (conflicting) { + LOG ("conflicting scc simulating up at %d", parent); + to_justify.push_back (-parent); + } else + to_justify.push_back (first); + while (!to_justify.empty ()) { + const int next = to_justify.back (); + to_justify.pop_back (); + Watches &next_ws = watches (-next); + for (const auto &w : next_ws) { + if (!w.binary ()) + continue; + const int child = w.blit; + if (!active (child)) + continue; + if (!flags (child).seen) + continue; + DFS &child_dfs = dfs[vlit (child)]; + if (child_dfs.parent) + continue; + child_dfs.parent = w.clause; + to_justify.push_back (child); + } + } + + clear_analyzed_literals (); + } + + int other, repr = parent; +#ifndef QUIET + int size = 0; +#endif + assert (!scc.empty ()); + size_t j = scc.size (); + do { + assert (j > 0); + other = scc[--j]; + if (other == -parent) { + LOG ("both %d and %d in one SCC", parent, -parent); + if (lrat) { + Flags &f = flags (-parent); + f.seen = true; + analyzed.push_back (-parent); + decompose_analyze_binary_chain (dfs, parent); + for (auto p : mini_chain) + lrat_chain.push_back (p); + mini_chain.clear (); + } + assign_unit (parent); +#ifndef NDEBUG + bool ok = +#endif + propagate (); + assert (!ok); + learn_empty_clause (); + lrat_chain.clear (); + } else { + if (abs (other) < abs (repr)) + repr = other; +#ifndef QUIET + size++; +#endif + } + } while (!unsat && other != parent); + + if (unsat) + break; +#ifndef QUIET + LOG ("SCC of representative %d of size %d", repr, size); +#endif + do { + assert (!scc.empty ()); + other = scc.back (); + scc.pop_back (); + dfs[vlit (other)].min = TRAVERSED; + if (frozen (other)) { + reprs[vlit (other)] = other; + continue; + } + reprs[vlit (other)] = repr; + if (other == repr) + continue; + substituted++; + LOG ("literal %d in SCC of %d", other, repr); + if (!lrat) + continue; + assert (mini_chain.empty ()); + Flags &f = flags (repr); + f.seen = true; + analyzed.push_back (repr); + // no need to reverse dfs_chain because this is handled by + // build_lrat_for_clause. + dfs_chains[vlit (other)] = + decompose_analyze_binary_clauses (dfs, other); + clear_analyzed_literals (); + } while (other != parent); + +#ifndef QUIET + if (size > 1) + non_trivial_sccs++; +#endif + + } else { + + // Current node 'parent' is in a non-trivial SCC but is not + // the entry point of the SCC in this depth first search, so + // keep it on the SCC stack until the entry point is reached. + + parent_dfs.min = new_min; + } + + } else { // pre-fix + + dfs_idx++; + assert (dfs_idx < TRAVERSED); + parent_dfs.idx = parent_dfs.min = dfs_idx; + scc.push_back (parent); + + LOG ("pre-fix work dfs search %d index %u", parent, dfs_idx); + + // Now traverse all the children in the binary implication + // graph but keep 'parent' on the stack for 'post-fix' work. + + for (const auto &w : ws) { + if (!w.binary ()) + continue; + const int child = w.blit; + if (!active (child)) + continue; + DFS &child_dfs = dfs[vlit (child)]; + if (child_dfs.idx) + continue; + work.push_back (child); + } + } + } + } + } + } + + erase_vector (work); + erase_vector (scc); + // delete [] dfs; need to postpone until after changing clauses... + + // Only keep the representatives 'repr' mapping. + + PHASE ("decompose", stats.decompositions, + "%d non-trivial sccs, %d substituted %.2f%%", non_trivial_sccs, + substituted, percent (substituted, before)); + + bool new_unit = false, new_binary_clause = false; + + // Finally, mark substituted literals as such and push the equivalences of + // the substituted literals to their representative on the extension + // stack to fix an assignment during 'extend'. + // It is also necessary to do so for proper IDRUP/LIDRUP/Resolution proofs + + vector decompose_ids; + const size_t size = 2 * (1 + (size_t) max_var); + decompose_ids.resize (size); + + for (auto idx : vars) { + if (!substituted) + break; + if (unsat) + break; + if (!active (idx)) + continue; + int other = reprs[vlit (idx)]; + if (other == idx) + continue; + assert (!flags (other).eliminated ()); + assert (!flags (other).substituted ()); + + LOG ("marking equivalence of %d and %d", idx, other); + assert (clause.empty ()); + assert (lrat_chain.empty ()); + clause.push_back (other); + clause.push_back (-idx); + if (lrat) { + build_lrat_for_clause (dfs_chains); + assert (!lrat_chain.empty ()); + } + + const int64_t id1 = ++clause_id; + if (proof) { + proof->add_derived_clause (id1, false, clause, lrat_chain); + proof->weaken_minus (id1, clause); + } + external->push_binary_clause_on_extension_stack (id1, -idx, other); + + decompose_ids[vlit (-idx)] = id1; + + lrat_chain.clear (); + clause.clear (); + + assert (clause.empty ()); + assert (lrat_chain.empty ()); + clause.push_back (idx); + clause.push_back (-other); + if (lrat) { + build_lrat_for_clause (dfs_chains); + assert (!lrat_chain.empty ()); + } + const int64_t id2 = ++clause_id; + if (proof) { + proof->add_derived_clause (id2, false, clause, lrat_chain); + proof->weaken_minus (id2, clause); + } + external->push_binary_clause_on_extension_stack (id2, idx, -other); + decompose_ids[vlit (idx)] = id2; + + clause.clear (); + lrat_chain.clear (); + } + + vector postponed_garbage; + + // Now go over all clauses and find clause which contain literals that + // should be substituted by their representative. + + size_t clauses_size = clauses.size (); +#ifndef QUIET + size_t garbage = 0, replaced = 0; +#endif + for (size_t i = 0; substituted && !unsat && i < clauses_size; i++) { + Clause *c = clauses[i]; + if (c->garbage) + continue; + int j, size = c->size; + for (j = 0; j < size; j++) { + const int lit = c->literals[j]; + if (reprs[vlit (lit)] != lit) + break; + } + + if (j == size) + continue; + +#ifndef QUIET + replaced++; +#endif + LOG (c, "first substituted literal %d in", substituted); + + // Now copy the result to 'clause'. Substitute literals if they have a + // different representative. Skip duplicates and false literals. If a + // literal occurs in both phases or is assigned to true the clause is + // satisfied and can be marked as garbage. + + assert (clause.empty ()); + assert (lrat_chain.empty ()); + assert (analyzed.empty ()); + bool satisfied = false; + + for (int k = 0; !satisfied && k < size; k++) { + const int lit = c->literals[k]; + signed char tmp = val (lit); + if (tmp > 0) + satisfied = true; + else if (tmp < 0) { + if (!lrat) + continue; + Flags &f = flags (lit); + if (f.seen) + continue; + f.seen = true; + analyzed.push_back (lit); + int64_t id = unit_id (-lit); + lrat_chain.push_back (id); + continue; + } else { + const int other = reprs[vlit (lit)]; + tmp = val (other); + if (tmp < 0) { + if (!lrat) + continue; + Flags &f = flags (other); + if (!f.seen) { + f.seen = true; + analyzed.push_back (other); + int64_t id = unit_id (-other); + lrat_chain.push_back (id); + } + if (other == lit) + continue; + int64_t id = decompose_ids[vlit (-lit)]; + assert (id); + lrat_chain.push_back (id); + continue; + } else if (tmp > 0) + satisfied = true; + else { + tmp = marked (other); + if (tmp < 0) + satisfied = true; + else if (!tmp) { + mark (other); + clause.push_back (other); + } + if (other == lit) + continue; + if (!lrat) + continue; + int64_t id = decompose_ids[vlit (-lit)]; + assert (id); + lrat_chain.push_back (id); + } + } + } + if (lrat) + lrat_chain.push_back (c->id); + clear_analyzed_literals (); + LOG (lrat_chain, "lrat_chain:"); + if (satisfied) { + LOG (c, "satisfied after substitution (postponed)"); + postponed_garbage.push_back (c); +#ifndef QUIET + garbage++; +#endif + } else if (!clause.size ()) { + LOG ("learned empty clause during decompose"); + learn_empty_clause (); + } else if (clause.size () == 1) { + LOG (c, "unit %d after substitution", clause[0]); + assign_unit (clause[0]); + mark_garbage (c); + new_unit = true; +#ifndef QUIET + garbage++; +#endif + } else if (c->literals[0] != clause[0] || c->literals[1] != clause[1]) { + LOG ("need new clause since at least one watched literal changed"); + if (clause.size () == 2) + new_binary_clause = true; + size_t d_clause_idx = clauses.size (); + Clause *d = new_clause_as (c); + assert (clauses[d_clause_idx] == d); + clauses[d_clause_idx] = c; + clauses[i] = d; + mark_garbage (c); +#ifndef QUIET + garbage++; +#endif + } else { + LOG ("simply shrinking clause since watches did not change"); + assert (c->size > 2); + if (!c->redundant) + mark_removed (c); + if (proof) { + proof->add_derived_clause (++clause_id, c->redundant, clause, + lrat_chain); + proof->delete_clause (c); + c->id = clause_id; + } + size_t l; + int *literals = c->literals; + for (l = 2; l < clause.size (); l++) + literals[l] = clause[l]; + int flushed = c->size - (int) l; + if (flushed) { + if (l == 2) + new_binary_clause = true; + LOG ("flushed %d literals", flushed); + (void) shrink_clause (c, l); + } else if (likely_to_be_kept_clause (c)) + mark_added (c); + // we have shrunken c->size to l so even though there is an assertion + // for c->size > 2 at the beginning of this else block, the new size + // can be 2 now. + if (c->size == 2) { // cheaper to update only new binary clauses + assert (new_binary_clause); + update_watch_size (watches (c->literals[0]), c->literals[1], c); + update_watch_size (watches (c->literals[1]), c->literals[0], c); + } + LOG (c, "substituted"); + } + while (!clause.empty ()) { + int lit = clause.back (); + clause.pop_back (); + assert (marked (lit) > 0); + unmark (lit); + } + lrat_chain.clear (); + } + + if (proof) { + for (auto idx : vars) { + if (!substituted) + break; + if (!active (idx)) + continue; + const int64_t id1 = decompose_ids[vlit (-idx)]; + if (!id1) + continue; + int other = reprs[vlit (idx)]; + assert (other != idx); + assert (!flags (other).eliminated ()); + assert (!flags (other).substituted ()); + + clause.push_back (other); + clause.push_back (-idx); + proof->delete_clause (id1, false, clause); + clause.clear (); + + clause.push_back (idx); + clause.push_back (-other); + const int64_t id2 = decompose_ids[vlit (idx)]; + proof->delete_clause (id2, false, clause); + clause.clear (); + } + } + + if (!unsat && !postponed_garbage.empty ()) { + LOG ("now marking %zd postponed garbage clauses", + postponed_garbage.size ()); + for (const auto &c : postponed_garbage) + mark_garbage (c); + } + erase_vector (postponed_garbage); + + PHASE ("decompose", stats.decompositions, + "%zd clauses replaced %.2f%% producing %zd garbage clauses %.2f%%", + replaced, percent (replaced, clauses_size), garbage, + percent (garbage, replaced)); + + erase_vector (scc); + + // Propagate found units. + + if (!unsat && propagated < trail.size () && !propagate ()) { + LOG ("empty clause after propagating units from substitution"); + learn_empty_clause (); + } + + for (auto idx : vars) { + if (!substituted) + break; + if (unsat) + break; + if (!active (idx)) + continue; + int other = reprs[vlit (idx)]; + if (other == idx) + continue; + assert (!flags (other).eliminated ()); + assert (!flags (other).substituted ()); + if (!flags (other).fixed ()) + mark_substituted (idx); + } + + reprs_delete.free (); + dfs_delete.free (); + erase_vector (dfs_chains); + + if (substituted) + flush_all_occs_and_watches (); // particularly the 'blit's + + bool success = + unsat || (substituted > 0 && (new_unit || new_binary_clause)); + report ('d', !opts.reportall && !success); + + STOP_SIMPLIFIER (decompose, DECOMP); + + return success; +} + +void Internal::decompose () { + for (int round = 1; round <= opts.decomposerounds; round++) + if (!decompose_round ()) + break; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/decompose.hpp b/src/sat/cadical/decompose.hpp new file mode 100644 index 000000000..d62b6ba4a --- /dev/null +++ b/src/sat/cadical/decompose.hpp @@ -0,0 +1,23 @@ +#ifndef _decompose_hpp_INCLUDED +#define _decompose_hpp_INCLUDED + +namespace CaDiCaL { + +// This implements Tarjan's algorithm for decomposing the binary implication +// graph intro strongly connected components (SCCs). Literals in one SCC +// are equivalent and we replace them all by the literal with the smallest +// index in the SCC. These variables are marked 'substituted' and will be +// removed from all clauses. Their value will be fixed during 'extend'. + +#define TRAVERSED UINT_MAX // mark completely traversed + +struct DFS { + unsigned idx; // depth first search index + unsigned min; // minimum reachable index + Clause *parent; // for lrat + DFS () : idx (0), min (0), parent (0) {} +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/deduplicate.cpp b/src/sat/cadical/deduplicate.cpp new file mode 100644 index 000000000..08a5d01c6 --- /dev/null +++ b/src/sat/cadical/deduplicate.cpp @@ -0,0 +1,170 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +// Equivalent literal substitution in 'decompose' and shrinking in 'subsume' +// or 'vivify' might produce duplicated binary clauses. They can not be +// found in 'subsume' nor 'vivify' since we explicitly do not consider +// binary clauses as candidates to be shrunken or subsumed. They are +// detected here by a simple scan of watch lists and then marked as garbage. +// This is actually also quite fast. + +// Further it might also be possible that two binary clauses can be resolved +// to produce a unit (we call it 'hyper unary resolution'). For example +// resolving the binary clauses '1 -2' and '1 2' produces the unit '1'. +// This could be found by probing in 'probe' unless '-1' also occurs in a +// binary clause (add the clause '-1 2' to those two clauses) in which case +// '1' as well as '2' both occur positively as well as negatively and none +// of them nor their negation is considered as probe + +void Internal::mark_duplicated_binary_clauses_as_garbage () { + + if (!opts.deduplicate) + return; + if (unsat) + return; + if (terminated_asynchronously ()) + return; + + START_SIMPLIFIER (deduplicate, DEDUP); + stats.deduplications++; + + assert (!level); + assert (watching ()); + + vector stack; // To save marked literals and unmark them later. + + int64_t subsumed = 0; + int64_t units = 0; + + for (auto idx : vars) { + + if (unsat) + break; + if (!active (idx)) + continue; + int unit = 0; + + for (int sign = -1; !unit && sign <= 1; sign += 2) { + + const int lit = sign * idx; // Consider all literals. + + assert (stack.empty ()); + Watches &ws = watches (lit); + + // We are removing references to garbage clause. Thus no 'auto'. + + const const_watch_iterator end = ws.end (); + watch_iterator j = ws.begin (); + const_watch_iterator i; + + for (i = j; !unit && i != end; i++) { + Watch w = *j++ = *i; + if (!w.binary ()) + continue; + int other = w.blit; + const int tmp = marked (other); + Clause *c = w.clause; + + if (tmp > 0) { // Found duplicated binary clause. + + if (c->garbage) { + j--; + continue; + } + LOG (c, "found duplicated"); + + // The previous identical clause 'd' might be redundant and if the + // second clause 'c' is not (so irredundant), then we have to keep + // 'c' instead of 'd', thus we search for it and replace it. + + if (!c->redundant) { + watch_iterator k; + for (k = ws.begin ();; k++) { + assert (k != i); + if (!k->binary ()) + continue; + if (k->blit != other) + continue; + Clause *d = k->clause; + if (d->garbage) + continue; + c = d; + break; + } + *k = w; + } + + LOG (c, "mark garbage duplicated"); + stats.subsumed++; + stats.deduplicated++; + subsumed++; + mark_garbage (c); + j--; + + } else if (tmp < 0) { // Hyper unary resolution. + + LOG ("found %d %d and %d %d which produces unit %d", lit, -other, + lit, other, lit); + unit = lit; + if (lrat) { + // taken from fradical + assert (lrat_chain.empty ()); + lrat_chain.push_back (c->id); + // We've forgotten where the other binary clause is, so go find + // it again + for (watch_iterator k = ws.begin ();; k++) { + assert (k != i); + if (!k->binary ()) + continue; + if (k->blit != -other) + continue; + lrat_chain.push_back (k->clause->id); + break; + } + } + j = ws.begin (); // Flush 'ws'. + units++; + + } else { + if (c->garbage) + continue; + mark (other); + stack.push_back (other); + } + } + + if (j == ws.begin ()) + erase_vector (ws); + else if (j != end) + ws.resize (j - ws.begin ()); // Shrink watchers. + + for (const auto &other : stack) + unmark (other); + + stack.clear (); + } + + // Propagation potentially messes up the watches and thus we can not + // propagate the unit immediately after finding it. Instead we break + // out of both loops and assign and propagate the unit here. + + if (unit) { + + stats.failed++; + stats.hyperunary++; + assign_unit (unit); + // lrat_chain.clear (); done in search_assign + + if (!propagate ()) { + LOG ("empty clause after propagating unit"); + learn_empty_clause (); + } + } + } + STOP_SIMPLIFIER (deduplicate, DEDUP); + + report ('2', !opts.reportall && !(subsumed + units)); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/definition.cpp b/src/sat/cadical/definition.cpp new file mode 100644 index 000000000..1b704b2b9 --- /dev/null +++ b/src/sat/cadical/definition.cpp @@ -0,0 +1,283 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +#define INVALID_LIT UINT_MAX + +// functions below are passed to kitten +// +struct definition_extractor { + Eliminator *eliminator; + Internal *internal; + vector clauses[2]; + int lit; + vector> implicants; + int unit; +}; + +extern "C" { + +// used to extract definitions from kitten +// +static void traverse_definition_core (void *state, unsigned id) { + definition_extractor *extractor = (definition_extractor *) state; + Clause *clause; + const vector &clauses0 = extractor->clauses[0]; + const vector &clauses1 = extractor->clauses[1]; + Eliminator *eliminator = extractor->eliminator; + const size_t size_clauses0 = clauses0.size (); + const size_t size_clauses1 = clauses1.size (); + assert (size_clauses0 <= UINT_MAX); + unsigned sign; + assert (id < size_clauses0 + size_clauses1); + if (id < size_clauses0) { + clause = clauses0[id]; + sign = 1; + } else { + unsigned tmp = id - size_clauses0; +#ifndef NDEBUG + assert (size_clauses1 <= UINT_MAX); + assert (tmp < size_clauses1); +#endif + clause = clauses1[tmp]; + sign = 2; + } + (void) size_clauses1; + clause->gate = true; + eliminator->gates.push_back (clause); +#ifdef LOGGING + Internal *internal = extractor->internal; + LOG (clause, "extracted gate"); +#endif + eliminator->definition_unit |= sign; +} + +// extracts relevant learned clauses from kissat for drat proofs +// +static void traverse_one_sided_core_lemma (void *state, bool learned, + size_t size, + const unsigned *lits) { + if (!learned) + return; + definition_extractor *extractor = (definition_extractor *) state; + Eliminator *eliminator = extractor->eliminator; + Internal *internal = extractor->internal; + Proof *proof = internal->proof; + const int unit = extractor->unit; + vector &proof_clauses = eliminator->proof_clauses; + if (size) { + proof_clause pc; + pc.id = ++(internal->clause_id); + pc.literals.push_back (unit); + const unsigned *end = lits + size; + for (const unsigned *p = lits; p != end; p++) + pc.literals.push_back (internal->citten2lit (*p)); // conversion + proof_clauses.push_back (pc); + assert (proof); + proof->add_derived_clause (pc.id, true, pc.literals, pc.chain); + } else { + internal->assign_unit (unit); + for (const auto &pc : proof_clauses) { + proof->delete_clause (pc.id, true, pc.literals); + } + proof_clauses.clear (); + } +} + +// extract lrat proofs for relevant clauses +// +static void traverse_one_sided_core_lemma_with_lrat ( + void *state, unsigned cid, unsigned id, bool learned, size_t size, + const unsigned *lits, size_t chain_size, const unsigned *chain) { + definition_extractor *extractor = (definition_extractor *) state; + Eliminator *eliminator = extractor->eliminator; + Internal *internal = extractor->internal; + Proof *proof = internal->proof; + const int unit = extractor->unit; + const vector &clauses0 = extractor->clauses[0]; + const vector &clauses1 = extractor->clauses[1]; + vector &proof_clauses = eliminator->proof_clauses; + if (!learned) { // remember clauses for mapping to kitten internal + assert (size); + assert (!chain_size); + proof_clause pc; + pc.cid = cid; + pc.learned = false; + const size_t size_clauses0 = clauses0.size (); + assert (size_clauses0 <= UINT_MAX); + if (id < size_clauses0) { + pc.id = clauses0[id]->id; + } else { + unsigned tmp = id - size_clauses0; +#ifndef NDEBUG + const size_t size_clauses1 = clauses1.size (); + assert (size_clauses1 <= UINT_MAX); + assert (tmp < size_clauses1); +#endif + pc.id = clauses1[tmp]->id; + } + proof_clauses.push_back (pc); + } else { // actually add to proof + assert (chain_size); + if (size) { + proof_clause pc; + pc.id = ++(internal->clause_id); + pc.cid = cid; + pc.learned = true; + pc.literals.push_back (unit); + const unsigned *end = lits + size; + for (const unsigned *p = lits; p != end; p++) + pc.literals.push_back (internal->citten2lit (*p)); // conversion + for (const unsigned *p = chain + chain_size; p != chain; p--) { + int64_t id = 0; + for (const auto &cpc : proof_clauses) { + if (cpc.cid == *(p - 1)) { + id = cpc.id; + break; + } + } + assert (id); + pc.chain.push_back (id); + } + proof_clauses.push_back (pc); + assert (proof); + proof->add_derived_clause (pc.id, true, pc.literals, pc.chain); + } else { // learn unit finish proof + assert (internal->lrat_chain.empty ()); + for (const unsigned *p = chain + chain_size; p != chain; p--) { + int64_t id = 0; + for (const auto &cpc : proof_clauses) { + if (cpc.cid == *(p - 1)) { + id = cpc.id; + break; + } + } + assert (id); + internal->lrat_chain.push_back (id); + } + internal->assign_unit (unit); + assert (internal->lrat_chain.empty ()); + for (const auto &pc : proof_clauses) { + if (pc.learned) + proof->delete_clause (pc.id, true, pc.literals); + } + proof_clauses.clear (); + } + } +} + +} // end extern C + +// Code ported from kissat. Kitten (and kissat) use unsigned representation +// for literals whereas CaDiCaL uses signed representation. Conversion is +// necessary for communication using lit2citten and citten2lit. +// This code is called in elim and kitten is initialized beforehand. +// To avoid confusion all cadical interal definitions with kitten are called +// citten. +// +void Internal::find_definition (Eliminator &eliminator, int lit) { + if (!opts.elimdef) + return; + if (unsat) + return; + if (val (lit)) + return; + if (!eliminator.gates.empty ()) + return; + assert (!val (lit)); + assert (!level); + assert (citten); + const int not_lit = -lit; + definition_extractor extractor; + extractor.lit = lit; + extractor.clauses[0] = occs (lit); + extractor.clauses[1] = occs (not_lit); + extractor.eliminator = &eliminator; + extractor.internal = internal; + citten_clear_track_log_terminate (); + unsigned exported = 0; + for (unsigned sign = 0; sign < 2; sign++) { + const unsigned except = sign ? lit2citten (not_lit) : lit2citten (lit); + for (auto c : extractor.clauses[sign]) { + // to avoid copying the literals of c in their unsigned + // representation we instead implement the translation in kitten + if (!c->garbage) { + LOG (c, "adding to kitten"); + citten_clause_with_id_and_exception (citten, exported, c->size, + c->literals, except); + } + exported++; + } + } + stats.definitions_checked++; + const size_t limit = opts.elimdefticks; + kitten_set_ticks_limit (citten, limit); + int status = kitten_solve (citten); + if (!exported) + goto ABORT; + if (status == 20) { + LOG ("sub-solver result UNSAT shows definition exists"); + uint64_t learned; + unsigned reduced = kitten_compute_clausal_core (citten, &learned); + LOG ("1st sub-solver core of size %u original clauses out of %u", + reduced, exported); + for (int i = 2; i <= opts.elimdefcores; i++) { + kitten_shrink_to_clausal_core (citten); + kitten_shuffle_clauses (citten); + kitten_set_ticks_limit (citten, 10 * limit); + int tmp = kitten_solve (citten); + assert (!tmp || tmp == 20); + if (!tmp) { + LOG ("aborting core extraction"); + goto ABORT; + } +#ifndef NDEBUG + unsigned previous = reduced; +#endif + reduced = kitten_compute_clausal_core (citten, &learned); + LOG ("%d sub-solver core of size %u original clauses out of %u", i, + reduced, exported); + assert (reduced <= previous); +#if not defined(LOGGING) && defined(NDEBUG) + (void) reduced; +#endif + } + stats.definitions_extracted++; + eliminator.gatetype = DEF; + eliminator.definition_unit = 0; + kitten_traverse_core_ids (citten, &extractor, traverse_definition_core); + assert (eliminator.definition_unit); + int unit = 0; + if (eliminator.definition_unit == 2) { + unit = not_lit; + } else if (eliminator.definition_unit == 1) + unit = lit; + + if (unit) { + stats.definition_units++; + VERBOSE (2, "one sided core " + "definition extraction yields " + "failed literal"); + if (proof) { + if (lrat) { + extractor.unit = unit; + kitten_trace_core (citten, &extractor, + traverse_one_sided_core_lemma_with_lrat); + } else { + extractor.unit = unit; + kitten_traverse_core_clauses (citten, &extractor, + traverse_one_sided_core_lemma); + } + } else + assign_unit (unit); + elim_propagate (eliminator, unit); + } + } else { + ABORT: + LOG ("sub-solver failed to show that definition exists"); + } + stats.definition_ticks += kitten_current_ticks (citten); + return; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/delay.hpp b/src/sat/cadical/delay.hpp new file mode 100644 index 000000000..e1e10ec82 --- /dev/null +++ b/src/sat/cadical/delay.hpp @@ -0,0 +1,38 @@ +#ifndef _delay_hpp_INCLUDED +#define _delay_hpp_INCLUDED + +#include +#include + +namespace CaDiCaL { +struct Delay { + unsigned count; + unsigned current; + + Delay () : count (0), current (0) {} + + bool delay () { + if (count) { + --count; + return true; + } else { + return false; + } + } + + void bump_delay () { + current += current < std::numeric_limits::max (); + count = current; + } + + void reduce_delay () { + if (!current) + return; + current /= 2; + count = current; + } +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/drattracer.cpp b/src/sat/cadical/drattracer.cpp new file mode 100644 index 000000000..057c2d141 --- /dev/null +++ b/src/sat/cadical/drattracer.cpp @@ -0,0 +1,153 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +DratTracer::DratTracer (Internal *i, File *f, bool b) + : internal (i), file (f), binary (b) +#ifndef QUIET + , + added (0), deleted (0) +#endif +{ + (void) internal; +} + +void DratTracer::connect_internal (Internal *i) { + internal = i; + file->connect_internal (internal); + LOG ("DRAT TRACER connected to internal"); +} + +DratTracer::~DratTracer () { + LOG ("DRAT TRACER delete"); + delete file; +} + +/*------------------------------------------------------------------------*/ + +inline void DratTracer::put_binary_zero () { + assert (binary); + assert (file); + file->put ((unsigned char) 0); +} + +inline void DratTracer::put_binary_lit (int lit) { + assert (binary); + assert (file); + assert (lit != INT_MIN); + unsigned idx = abs (lit); + assert (idx < (1u << 31)); + unsigned x = 2u * idx + (lit < 0); + unsigned char ch; + while (x & ~0x7f) { + ch = (x & 0x7f) | 0x80; + file->put (ch); + x >>= 7; + } + ch = x; + file->put (ch); +} + +/*------------------------------------------------------------------------*/ + +void DratTracer::drat_add_clause (const vector &clause) { + if (binary) + file->put ('a'); + for (const auto &external_lit : clause) + if (binary) + put_binary_lit (external_lit); + else + file->put (external_lit), file->put (' '); + if (binary) + put_binary_zero (); + else + file->put ("0\n"); +} +void DratTracer::drat_delete_clause (const vector &clause) { + if (binary) + file->put ('d'); + else + file->put ("d "); + for (const auto &external_lit : clause) + if (binary) + put_binary_lit (external_lit); + else + file->put (external_lit), file->put (' '); + if (binary) + put_binary_zero (); + else + file->put ("0\n"); +} + +/*------------------------------------------------------------------------*/ + +void DratTracer::add_derived_clause (int64_t, bool, + const vector &clause, + const vector &) { + if (file->closed ()) + return; + LOG ("DRAT TRACER tracing addition of derived clause"); + drat_add_clause (clause); +#ifndef QUIET + added++; +#endif +} + +void DratTracer::delete_clause (int64_t, bool, const vector &clause) { + if (file->closed ()) + return; + LOG ("DRAT TRACER tracing deletion of clause"); + drat_delete_clause (clause); +#ifndef QUIET + deleted++; +#endif +} + +/*------------------------------------------------------------------------*/ + +bool DratTracer::closed () { return file->closed (); } + +#ifndef QUIET + +void DratTracer::print_statistics () { + uint64_t bytes = file->bytes (); + uint64_t total = added + deleted; + MSG ("DRAT %" PRId64 " added clauses %.2f%%", added, + percent (added, total)); + MSG ("DRAT %" PRId64 " deleted clauses %.2f%%", deleted, + percent (deleted, total)); + MSG ("DRAT %" PRId64 " bytes (%.2f MB)", bytes, + bytes / (double) (1 << 20)); +} + +#endif + +void DratTracer::close (bool print) { + assert (!closed ()); + file->close (); +#ifndef QUIET + if (print) { + MSG ("DRAT proof file '%s' closed", file->name ()); + print_statistics (); + } +#else + (void) print; +#endif +} + +void DratTracer::flush (bool print) { + assert (!closed ()); + file->flush (); +#ifndef QUIET + if (print) { + MSG ("DRAT proof file '%s' flushed", file->name ()); + print_statistics (); + } +#else + (void) print; +#endif +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/drattracer.hpp b/src/sat/cadical/drattracer.hpp new file mode 100644 index 000000000..42e238838 --- /dev/null +++ b/src/sat/cadical/drattracer.hpp @@ -0,0 +1,53 @@ +#ifndef _drattracer_h_INCLUDED +#define _drattracer_h_INCLUDED + +#include "tracer.hpp" + +namespace CaDiCaL { + +class DratTracer : public FileTracer { + + Internal *internal; + File *file; + bool binary; +#ifndef QUIET + int64_t added, deleted; +#endif + void put_binary_zero (); + void put_binary_lit (int external_lit); + + // support DRAT + void drat_add_clause (const vector &); + void drat_delete_clause (const vector &); + +public: + // own and delete 'file' + DratTracer (Internal *, File *file, bool binary); + ~DratTracer (); + + void connect_internal (Internal *i) override; + void begin_proof (int64_t) override {} // skip + + void add_original_clause (int64_t, bool, const vector &, + bool = false) override {} // skip + + void add_derived_clause (int64_t, bool, const vector &, + const vector &) override; + + void delete_clause (int64_t, bool, const vector &) override; + + void finalize_clause (int64_t, const vector &) override {} // skip + + void report_status (int, int64_t) override {} // skip + +#ifndef QUIET + void print_statistics (); +#endif + bool closed () override; + void close (bool) override; + void flush (bool) override; +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/elim.cpp b/src/sat/cadical/elim.cpp new file mode 100644 index 000000000..9a5157436 --- /dev/null +++ b/src/sat/cadical/elim.cpp @@ -0,0 +1,1172 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Implements a variant of bounded variable elimination as originally +// described in our SAT'05 paper introducing 'SATeLite'. This is an +// inprocessing version, i.e., it is interleaved with search and triggers +// subsumption and strengthening, blocked and covered clause elimination +// during elimination rounds. It focuses only those variables which +// occurred in removed irredundant clauses since the last time an +// elimination round was run. By bounding the maximum resolvent size we can +// run each elimination round until completion. See the code of 'elim' for +// how elimination rounds are interleaved with blocked clause elimination +// and subsumption (which in turn also calls vivification and transitive +// reduction of the binary implication graph). + +/*------------------------------------------------------------------------*/ + +inline double Internal::compute_elim_score (unsigned lit) { + assert (1 <= lit), assert (lit <= (unsigned) max_var); + const unsigned uidx = 2 * lit; + const double pos = internal->ntab[uidx]; + const double neg = internal->ntab[uidx + 1]; + if (!pos) + return -neg; + if (!neg) + return -pos; + double sum = 0, prod = 0; + if (opts.elimsum) + sum = opts.elimsum * (pos + neg); + if (opts.elimprod) + prod = opts.elimprod * (pos * neg); + return prod + sum; +} + +/*------------------------------------------------------------------------*/ + +bool elim_more::operator() (unsigned a, unsigned b) { + const auto s = internal->compute_elim_score (a); + const auto t = internal->compute_elim_score (b); + if (s > t) + return true; + if (s < t) + return false; + return a > b; +} + +/*------------------------------------------------------------------------*/ + +// Note that the new fast subsumption algorithm implemented in 'subsume' +// does not distinguish between irredundant and redundant clauses and is +// also run during search to strengthen and remove 'sticky' redundant +// clauses but also irredundant ones. So beside learned units during search +// or as consequence of other preprocessors, these subsumption rounds during +// search can remove (irredundant) clauses (and literals), which in turn +// might make new bounded variable elimination possible. This is tested +// in the 'bool ineliminating ()' guard. + +bool Internal::ineliminating () { + + if (!opts.elim) + return false; + if (!preprocessing && !opts.inprocessing) + return false; + if (preprocessing) + assert (lim.preprocessing); + + // Respect (increasing) conflict limit. + // + if (lim.elim >= stats.conflicts) + return false; + + // Wait until there are new units or new removed variables + // (in removed or shrunken irredundant clauses and thus marked). + // + if (last.elim.fixed < stats.all.fixed) + return true; + if (last.elim.marked < stats.mark.elim) + return true; + + // VERBOSE (3, "elim not scheduled due to fixpoint"); + return false; +} + +/*------------------------------------------------------------------------*/ + +// Update the global elimination schedule after adding or removing a clause. + +void Internal::elim_update_added_clause (Eliminator &eliminator, + Clause *c) { + assert (!c->redundant); + ElimSchedule &schedule = eliminator.schedule; + for (const auto &lit : *c) { + if (!active (lit)) + continue; + occs (lit).push_back (c); + if (frozen (lit)) + continue; + noccs (lit)++; + const int idx = abs (lit); + if (schedule.contains (idx)) + schedule.update (idx); + } +} + +void Internal::elim_update_removed_lit (Eliminator &eliminator, int lit) { + if (!active (lit)) + return; + if (frozen (lit)) + return; + int64_t &score = noccs (lit); + assert (score > 0); + score--; + const int idx = abs (lit); + ElimSchedule &schedule = eliminator.schedule; + if (schedule.contains (idx)) + schedule.update (idx); + else { + LOG ("rescheduling %d for elimination after removing clause", idx); + schedule.push_back (idx); + } +} + +void Internal::elim_update_removed_clause (Eliminator &eliminator, + Clause *c, int except) { + assert (!c->redundant); + for (const auto &lit : *c) { + if (lit == except) + continue; + assert (lit != -except); + elim_update_removed_lit (eliminator, lit); + } +} + +/*------------------------------------------------------------------------*/ + +// Since we do not have watches we have to do our own unit propagation +// during elimination as soon we find a unit clause. This finds new units +// and also marks clauses satisfied by those units as garbage immediately. + +void Internal::elim_propagate (Eliminator &eliminator, int root) { + assert (val (root) > 0); + vector work; + size_t i = 0; + work.push_back (root); + while (i < work.size ()) { + int lit = work[i++]; + LOG ("elimination propagation of %d", lit); + assert (val (lit) > 0); + const Occs &ns = occs (-lit); + for (const auto &c : ns) { + if (c->garbage) + continue; + int unit = 0, satisfied = 0; + for (const auto &other : *c) { + const signed char tmp = val (other); + if (tmp < 0) + continue; + if (tmp > 0) { + satisfied = other; + break; + } + if (unit) + unit = INT_MIN; + else + unit = other; + } + if (satisfied) { + LOG (c, "elimination propagation of %d finds %d satisfied", lit, + satisfied); + elim_update_removed_clause (eliminator, c, satisfied); + mark_garbage (c); + } else if (!unit) { + LOG ("empty clause during elimination propagation of %d", lit); + // need to set conflict = c for lrat + conflict = c; + learn_empty_clause (); + conflict = 0; + break; + } else if (unit != INT_MIN) { + LOG ("new unit %d during elimination propagation of %d", unit, lit); + build_chain_for_units (unit, c, 0); + assign_unit (unit); + work.push_back (unit); + } + } + if (unsat) + break; + const Occs &ps = occs (lit); + for (const auto &c : ps) { + if (c->garbage) + continue; + LOG (c, "elimination propagation of %d produces satisfied", lit); + elim_update_removed_clause (eliminator, c, lit); + mark_garbage (c); + } + } +} + +/*------------------------------------------------------------------------*/ + +// On-the-fly self-subsuming resolution during variable elimination is due +// to HyoJung Han, Fabio Somenzi, SAT'09. Basically while resolving two +// clauses we test the resolvent to be smaller than one of the antecedents. +// If this is the case the pivot can be removed from the antecedent +// on-the-fly and the resolution can be skipped during elimination. + +void Internal::elim_on_the_fly_self_subsumption (Eliminator &eliminator, + Clause *c, int pivot) { + LOG (c, "pivot %d on-the-fly self-subsuming resolution", pivot); + stats.elimotfstr++; + stats.strengthened++; + assert (clause.empty ()); + for (const auto &lit : *c) { + if (lit == pivot) + continue; + const signed char tmp = val (lit); + assert (tmp <= 0); + if (tmp < 0) + continue; + clause.push_back (lit); + } + Clause *r = new_resolved_irredundant_clause (); + elim_update_added_clause (eliminator, r); + clause.clear (); + lrat_chain.clear (); + elim_update_removed_clause (eliminator, c, pivot); + mark_garbage (c); +} + +/*------------------------------------------------------------------------*/ + +// Resolve two clauses on the pivot literal 'pivot', which is assumed to +// occur in opposite phases in 'c' and 'd'. The actual resolvent is stored +// in the temporary global 'clause' if it is not redundant. It is +// considered redundant if one of the clauses is already marked as garbage +// it is root level satisfied, the resolvent is empty, a unit, or produces a +// self-subsuming resolution, which results in the pivot to be removed from +// at least one of the antecedents. + +// Note that current root level assignments are taken into account, i.e., by +// removing root level falsified literals. The function returns true if the +// resolvent is not redundant and for instance has to be taken into account +// during bounded variable elimination. + +// Detected units are immediately assigned and in case the last argument is +// true also propagated eagerly in a elimination specific propagation +// routine, which not only finds units but also updates the schedule. + +// When this function is called during computation of the number of +// non-trivial (or non-satisfied) resolvents we can eagerly propagate units. +// But during actually adding the resolvents this results in problems as we +// found one rare test case '../test/trace/reg0056.trace' (out of billions), +// where the pivot itself was assigned during such a propagation while +// adding resolvents and lead to pushing a clause to the reconstruction +// stack that later flipped the value of the pivot (while all other literals +// in that clause were unit implied too). Not pushing the pivot clauses to +// the reconstruction stack produced a wrong model too. Our fix is to only +// eagerly propagate during computation of the number of resolvents and +// otherwise delay propagation until the end of elimination (which is less +// precise regarding scheduling but very rarely happens). + +bool Internal::resolve_clauses (Eliminator &eliminator, Clause *c, + int pivot, Clause *d, + const bool propagate_eagerly) { + + assert (!c->redundant); + assert (!d->redundant); + + stats.elimres++; + + if (c->garbage || d->garbage) + return false; + if (c->size > d->size) { + pivot = -pivot; + swap (c, d); + } + + assert (!level); + assert (clause.empty ()); + + int satisfied = 0; // Contains this satisfying literal. + int tautological = 0; // Clashing literal if tautological. + + int s = 0; // Actual literals from 'c'. + int t = 0; // Actual literals from 'd'. + + // First determine whether the first antecedent is satisfied, add its + // literals to 'clause' and mark them (except for 'pivot'). + // + for (const auto &lit : *c) { + if (lit == pivot) { + s++; + continue; + } + assert (lit != -pivot); + const signed char tmp = val (lit); + if (tmp > 0) { + satisfied = lit; + break; + } else if (tmp < 0) { + if (!lrat) + continue; + Flags &f = flags (lit); + if (f.seen) + continue; + analyzed.push_back (lit); + f.seen = true; + int64_t id = unit_id (-lit); + lrat_chain.push_back (id); + continue; + } else + mark (lit), clause.push_back (lit), s++; + } + if (satisfied) { + LOG (c, "satisfied by %d antecedent", satisfied); + elim_update_removed_clause (eliminator, c, satisfied); + mark_garbage (c); + clause.clear (); + lrat_chain.clear (); + clear_analyzed_literals (); + unmark (c); + return false; + } + + // Then determine whether the second antecedent is satisfied, add its + // literal to 'clause' and check whether a clashing literal is found, such + // that the resolvent would be tautological. + // + for (const auto &lit : *d) { + if (lit == -pivot) { + t++; + continue; + } + assert (lit != pivot); + signed char tmp = val (lit); + if (tmp > 0) { + satisfied = lit; + break; + } else if (tmp < 0) { + if (!lrat) + continue; + Flags &f = flags (lit); + if (f.seen) + continue; + analyzed.push_back (lit); + f.seen = true; + int64_t id = unit_id (-lit); + lrat_chain.push_back (id); + continue; + } else if ((tmp = marked (lit)) < 0) { + tautological = lit; + break; + } else if (!tmp) + clause.push_back (lit), t++; + else + assert (tmp > 0), t++; + } + + clear_analyzed_literals (); + unmark (c); + const int64_t size = clause.size (); + + if (lrat) { + lrat_chain.push_back (d->id); + lrat_chain.push_back (c->id); + } + + if (satisfied) { + LOG (d, "satisfied by %d antecedent", satisfied); + elim_update_removed_clause (eliminator, d, satisfied); + mark_garbage (d); + clause.clear (); + lrat_chain.clear (); + return false; + } + + LOG (c, "first antecedent"); + LOG (d, "second antecedent"); + + if (tautological) { + clause.clear (); + LOG ("resolvent tautological on %d", tautological); + lrat_chain.clear (); + return false; + } + + if (!size) { + clause.clear (); + LOG ("empty resolvent"); + learn_empty_clause (); // already clears lrat_chain. + return false; + } + + if (size == 1) { + int unit = clause[0]; + LOG ("unit resolvent %d", unit); + clause.clear (); + assign_unit (unit); // already clears lrat_chain. + if (propagate_eagerly) + elim_propagate (eliminator, unit); + return false; + } + + LOG (clause, "resolvent"); + assert (!lrat || !lrat_chain.empty ()); + + // Double self-subsuming resolution. The clauses 'c' and 'd' are + // identical except for the pivot which occurs in different phase. The + // resolvent subsumes both antecedents. + + if (s > size && t > size) { + assert (s == size + 1); + assert (t == size + 1); + clause.clear (); + // LRAT is c + d (+ eventual units) + elim_on_the_fly_self_subsumption (eliminator, c, pivot); + LOG (d, "double pivot %d on-the-fly self-subsuming resolution", -pivot); + stats.elimotfsub++; + stats.subsumed++; + elim_update_removed_clause (eliminator, d, -pivot); + mark_garbage (d); + return false; + } + + // Single self-subsuming resolution: The pivot can be removed from 'c', + // which is implemented by adding a clause which is the same as 'c' but + // with 'pivot' removed and then marking 'c' as garbage. + + if (s > size) { + assert (s == size + 1); + clause.clear (); + // LRAT is c + d (+ eventual units) + elim_on_the_fly_self_subsumption (eliminator, c, pivot); + return false; + } + + // Same single self-subsuming resolution situation, but only for 'd'. + + if (t > size) { + assert (t == size + 1); + clause.clear (); + // LRAT is c + d (+ eventual units) -> same. + elim_on_the_fly_self_subsumption (eliminator, d, -pivot); + return false; + } + if (propagate_eagerly) + lrat_chain.clear (); + return true; +} + +/*------------------------------------------------------------------------*/ + +// Check whether the number of non-tautological resolvents on 'pivot' is +// smaller or equal to the number of clauses with 'pivot' or '-pivot'. This +// is the main criteria of bounded variable elimination. As a side effect +// it flushes garbage clauses with that variable, sorts its occurrence lists +// (smallest clauses first) and also negates pivot if it has more positive +// than negative occurrences. + +bool Internal::elim_resolvents_are_bounded (Eliminator &eliminator, + int pivot) { + const bool substitute = !eliminator.gates.empty (); + const bool resolve_gates = eliminator.definition_unit; + if (substitute) + LOG ("trying to substitute %d", pivot); + + stats.elimtried++; + + assert (!unsat); + assert (active (pivot)); + + const Occs &ps = occs (pivot); + const Occs &ns = occs (-pivot); + const int64_t pos = ps.size (); + const int64_t neg = ns.size (); + if (!pos || !neg) + return lim.elimbound >= 0; + const int64_t bound = pos + neg + lim.elimbound; + + LOG ("checking number resolvents on %d bounded by " + "%" PRId64 " = %" PRId64 " + %" PRId64 " + %" PRId64, + pivot, bound, pos, neg, lim.elimbound); + + // Try all resolutions between a positive occurrence (outer loop) of + // 'pivot' and a negative occurrence of 'pivot' (inner loop) as long the + // bound on non-tautological resolvents is not hit and the size of the + // generated resolvents does not exceed the resolvent clause size limit. + + int64_t resolvents = 0; // Non-tautological resolvents. + + for (const auto &c : ps) { + assert (!c->redundant); + if (c->garbage) + continue; + for (const auto &d : ns) { + assert (!d->redundant); + if (d->garbage) + continue; + if (!resolve_gates && substitute && c->gate == d->gate) + continue; + stats.elimrestried++; + if (resolve_clauses (eliminator, c, pivot, d, true)) { + resolvents++; + int size = clause.size (); + clause.clear (); + LOG ("now at least %" PRId64 + " non-tautological resolvents on pivot %d", + resolvents, pivot); + if (size > opts.elimclslim) { + LOG ("resolvent size %d too big after %" PRId64 + " resolvents on %d", + size, resolvents, pivot); + return false; + } + if (resolvents > bound) { + LOG ("too many non-tautological resolvents on %d", pivot); + return false; + } + } else if (unsat) + return false; + else if (val (pivot)) + return false; + } + } + + LOG ("need %" PRId64 " <= %" PRId64 " non-tautological resolvents", + resolvents, bound); + + return true; +} + +/*------------------------------------------------------------------------*/ +// Add all resolvents on 'pivot' and connect them. + +inline void Internal::elim_add_resolvents (Eliminator &eliminator, + int pivot) { + + const bool substitute = !eliminator.gates.empty (); + const bool resolve_gates = eliminator.definition_unit; + if (substitute) { + LOG ("substituting pivot %d by resolving with %zd gate clauses", pivot, + eliminator.gates.size ()); + stats.elimsubst++; + } + switch (eliminator.gatetype) { + case EQUI: + stats.eliminated_equi++; + break; + case AND: + stats.eliminated_and++; + break; + case ITE: + stats.eliminated_ite++; + break; + case XOR: + stats.eliminated_xor++; + break; + case DEF: + stats.eliminated_def++; + break; + default: + assert (eliminator.gatetype == NO); + } + + LOG ("adding all resolvents on %d", pivot); + + assert (!val (pivot)); + assert (!flags (pivot).eliminated ()); + + const Occs &ps = occs (pivot); + const Occs &ns = occs (-pivot); +#ifdef LOGGING + int64_t resolvents = 0; +#endif + for (auto &c : ps) { + if (unsat) + break; + if (c->garbage) + continue; + for (auto &d : ns) { + if (unsat) + break; + if (d->garbage) + continue; + if (!resolve_gates && substitute && c->gate == d->gate) + continue; + if (!resolve_clauses (eliminator, c, pivot, d, false)) + continue; + assert (!lrat || !lrat_chain.empty ()); + Clause *r = new_resolved_irredundant_clause (); + elim_update_added_clause (eliminator, r); + eliminator.enqueue (r); + lrat_chain.clear (); + clause.clear (); +#ifdef LOGGING + resolvents++; +#endif + } + } + + LOG ("added %" PRId64 " resolvents to eliminate %d", resolvents, pivot); +} + +/*------------------------------------------------------------------------*/ + +// Remove clauses with 'pivot' and '-pivot' by marking them as garbage and +// push them on the extension stack. + +void Internal::mark_eliminated_clauses_as_garbage ( + Eliminator &eliminator, int pivot, bool &deleted_binary_clause) { + assert (!unsat); + + LOG ("marking irredundant clauses with %d as garbage", pivot); + + const int64_t substitute = eliminator.gates.size (); + if (substitute) + LOG ("pushing %" PRId64 " gate clauses on extension stack", substitute); +#ifndef NDEBUG + int64_t pushed = 0; +#endif + Occs &ps = occs (pivot); + for (const auto &c : ps) { + if (c->garbage) + continue; + assert (!c->redundant); + if (!substitute || c->gate) { + if (proof) + proof->weaken_minus (c); + if (c->size == 2) + deleted_binary_clause = true; + external->push_clause_on_extension_stack (c, pivot); +#ifndef NDEBUG + pushed++; +#endif + } + mark_garbage (c); + elim_update_removed_clause (eliminator, c, pivot); + } + erase_occs (ps); + + LOG ("marking irredundant clauses with %d as garbage", -pivot); + + Occs &ns = occs (-pivot); + for (const auto &d : ns) { + if (d->garbage) + continue; + assert (!d->redundant); + if (!substitute || d->gate) { + if (proof) + proof->weaken_minus (d); + if (d->size == 2) + deleted_binary_clause = true; + external->push_clause_on_extension_stack (d, -pivot); +#ifndef NDEBUG + pushed++; +#endif + } + mark_garbage (d); + elim_update_removed_clause (eliminator, d, -pivot); + } + erase_occs (ns); + + if (substitute) + assert (pushed <= substitute); + + // Unfortunately, we can not use the trick by Niklas Soerensson anymore, + // which avoids saving all clauses on the extension stack. This would + // break our new incremental 'restore' logic. +} + +/*------------------------------------------------------------------------*/ + +// Try to eliminate 'pivot' by bounded variable elimination. +void Internal::try_to_eliminate_variable (Eliminator &eliminator, int pivot, + bool &deleted_binary_clause) { + + if (!active (pivot)) + return; + assert (!frozen (pivot)); + + // First flush garbage clauses. + // + int64_t pos = flush_occs (pivot); + int64_t neg = flush_occs (-pivot); + + if (pos > neg) { + pivot = -pivot; + swap (pos, neg); + } + LOG ("pivot %d occurs positively %" PRId64 + " times and negatively %" PRId64 " times", + pivot, pos, neg); + assert (!eliminator.schedule.contains (abs (pivot))); + assert (pos <= neg); + + if (pos && neg > opts.elimocclim) { + LOG ("too many occurrences thus not eliminated %d", pivot); + assert (!eliminator.schedule.contains (abs (pivot))); + return; + } + + LOG ("trying to eliminate %d", pivot); + assert (!flags (pivot).eliminated ()); + + // Sort occurrence lists, such that shorter clauses come first. + Occs &ps = occs (pivot); + stable_sort (ps.begin (), ps.end (), clause_smaller_size ()); + Occs &ns = occs (-pivot); + stable_sort (ns.begin (), ns.end (), clause_smaller_size ()); + + if (pos) + find_gate_clauses (eliminator, pivot); + + if (!unsat && !val (pivot)) { + if (elim_resolvents_are_bounded (eliminator, pivot)) { + LOG ("number of resolvents on %d are bounded", pivot); + elim_add_resolvents (eliminator, pivot); + if (!unsat) + mark_eliminated_clauses_as_garbage (eliminator, pivot, + deleted_binary_clause); + if (active (pivot)) + mark_eliminated (pivot); + } else { + LOG ("too many resolvents on %d so not eliminated", pivot); + } + } + + unmark_gate_clauses (eliminator); + elim_backward_clauses (eliminator); +} + +/*------------------------------------------------------------------------*/ + +void Internal:: + mark_redundant_clauses_with_eliminated_variables_as_garbage () { + for (const auto &c : clauses) { + if (c->garbage || !c->redundant) + continue; + bool clean = true; + for (const auto &lit : *c) { + Flags &f = flags (lit); + if (f.eliminated ()) { + clean = false; + break; + } + if (f.pure ()) { + clean = false; + break; + } + } + if (!clean) + mark_garbage (c); + } +} + +/*------------------------------------------------------------------------*/ + +// This function performs one round of bounded variable elimination and +// returns the number of eliminated variables. The additional result +// 'completed' is true if this elimination round ran to completion (all +// variables have been tried). Otherwise it was asynchronously terminated +// or the resolution limit was hit. + +int Internal::elim_round (bool &completed, bool &deleted_binary_clause) { + + assert (opts.elim); + assert (!unsat); + + START_SIMPLIFIER (elim, ELIM); + stats.elimrounds++; + + int64_t marked_before = last.elim.marked; + last.elim.marked = stats.mark.elim; + assert (!level); + + int64_t resolution_limit; + + if (opts.elimlimited) { + int64_t delta = stats.propagations.search; + delta *= 1e-3 * opts.elimeffort; + if (delta < opts.elimmineff) + delta = opts.elimmineff; + if (delta > opts.elimmaxeff) + delta = opts.elimmaxeff; + delta = max (delta, (int64_t) 2l * active ()); + + PHASE ("elim-round", stats.elimrounds, + "limit of %" PRId64 " resolutions", delta); + + resolution_limit = stats.elimres + delta; + } else { + PHASE ("elim-round", stats.elimrounds, "resolutions unlimited"); + resolution_limit = LONG_MAX; + } + + init_noccs (); + + // First compute the number of occurrences of each literal and at the same + // time mark satisfied clauses and update 'elim' flags of variables in + // clauses with root level assigned literals (both false and true). + // + for (const auto &c : clauses) { + if (c->garbage || c->redundant) + continue; + bool satisfied = false, falsified = false; + for (const auto &lit : *c) { + const signed char tmp = val (lit); + if (tmp > 0) + satisfied = true; + else if (tmp < 0) + falsified = true; + else + assert (active (lit)); + } + if (satisfied) + mark_garbage (c); // forces more precise counts + else { + for (const auto &lit : *c) { + if (!active (lit)) + continue; + if (falsified) + mark_elim (lit); // simulate unit propagation + noccs (lit)++; + } + } + } + + init_occs (); + + Eliminator eliminator (this); + ElimSchedule &schedule = eliminator.schedule; + assert (schedule.empty ()); + + // Now find elimination candidates which occurred in clauses removed since + // the last time we ran bounded variable elimination, which in turned + // triggered their 'elim' bit to be set. + // + for (auto idx : vars) { + if (!active (idx)) + continue; + if (frozen (idx)) + continue; + if (!flags (idx).elim) + continue; + LOG ("scheduling %d for elimination initially", idx); + schedule.push_back (idx); + } + + schedule.shrink (); + +#ifndef QUIET + int64_t scheduled = schedule.size (); +#endif + + PHASE ("elim-round", stats.elimrounds, + "scheduled %" PRId64 " variables %.0f%% for elimination", + scheduled, percent (scheduled, active ())); + + // Connect irredundant clauses. + // + for (const auto &c : clauses) + if (!c->garbage && !c->redundant) + for (const auto &lit : *c) + if (active (lit)) + occs (lit).push_back (c); + +#ifndef QUIET + const int64_t old_resolutions = stats.elimres; +#endif + const int old_eliminated = stats.all.eliminated; + const int old_fixed = stats.all.fixed; + + // Limit on garbage literals during variable elimination. If the limit is + // hit a garbage collection is performed. + // + const int64_t garbage_limit = (2 * stats.irrlits / 3) + (1 << 20); + + // Main loops tries to eliminate variables according to the schedule. The + // schedule is updated dynamically and variables are potentially + // rescheduled to be tried again if they occur in a removed clause. + // +#ifndef QUIET + int64_t tried = 0; +#endif + while (!unsat && !terminated_asynchronously () && + stats.elimres <= resolution_limit && !schedule.empty ()) { + int idx = schedule.front (); + schedule.pop_front (); + flags (idx).elim = false; + try_to_eliminate_variable (eliminator, idx, deleted_binary_clause); +#ifndef QUIET + tried++; +#endif + if (stats.garbage.literals <= garbage_limit) + continue; + mark_redundant_clauses_with_eliminated_variables_as_garbage (); + garbage_collection (); + } + + // If the schedule is empty all variables have been tried (even + // rescheduled ones). Otherwise asynchronous termination happened or we + // ran into the resolution limit (or derived unsatisfiability). + // + completed = !schedule.size (); + + if (!completed) + last.elim.marked = marked_before; + + PHASE ("elim-round", stats.elimrounds, + "tried to eliminate %" PRId64 " variables %.0f%% (%zd remain)", + tried, percent (tried, scheduled), schedule.size ()); + + schedule.erase (); + + // Collect potential literal clause instantiation pairs, which needs full + // occurrence lists and thus we have it here before resetting them. + // + Instantiator instantiator; + if (!unsat && !terminated_asynchronously () && opts.instantiate) + collect_instantiation_candidates (instantiator); + + reset_occs (); + reset_noccs (); + + // Mark all redundant clauses with eliminated variables as garbage. + // + if (!unsat) + mark_redundant_clauses_with_eliminated_variables_as_garbage (); + + int eliminated = stats.all.eliminated - old_eliminated; +#ifndef QUIET + int64_t resolutions = stats.elimres - old_resolutions; + PHASE ("elim-round", stats.elimrounds, + "eliminated %d variables %.0f%% in %" PRId64 " resolutions", + eliminated, percent (eliminated, scheduled), resolutions); +#endif + + last.elim.subsumephases = stats.subsumephases; + const int units = stats.all.fixed - old_fixed; + report ('e', !opts.reportall && !(eliminated + units)); + STOP_SIMPLIFIER (elim, ELIM); + + if (!unsat && !terminated_asynchronously () && + instantiator) // Do we have candidate pairs? + instantiate (instantiator); + + return eliminated; // non-zero if successful +} + +/*------------------------------------------------------------------------*/ + +// Increase elimination bound (additional clauses allowed during variable +// elimination), which is triggered if elimination with last bound completed +// (including no new subsumptions). This was pioneered by GlueMiniSAT in +// the SAT Race 2015 and then picked up Chanseok Oh in his COMinisatPS +// solver, which in turn is used in the Maple series of SAT solvers. +// The bound is no increased if the maximum bound is reached. + +void Internal::increase_elimination_bound () { + + if (lim.elimbound >= opts.elimboundmax) + return; + + if (lim.elimbound < 0) + lim.elimbound = 0; + else if (!lim.elimbound) + lim.elimbound = 1; + else + lim.elimbound *= 2; + + if (lim.elimbound > opts.elimboundmax) + lim.elimbound = opts.elimboundmax; + + PHASE ("elim-phase", stats.elimphases, + "new elimination bound %" PRId64 "", lim.elimbound); + + // Now reschedule all active variables for elimination again. + // +#ifdef LOGGING + int count = 0; +#endif + for (auto idx : vars) { + if (!active (idx)) + continue; + if (flags (idx).elim) + continue; + mark_elim (idx); +#ifdef LOGGING + count++; +#endif + } + LOG ("marked %d variables as elimination candidates", count); + + report ('^'); +} + +void Internal::init_citten () { + if (!opts.elimdef) + return; + assert (!citten); + citten = kitten_init (); +} + +void Internal::reset_citten () { + if (citten) { + kitten_release (citten); + citten = 0; + } +} + +/*------------------------------------------------------------------------*/ + +void Internal::elim (bool update_limits) { + + if (unsat) + return; + if (level) + backtrack (); + if (!propagate ()) { + learn_empty_clause (); + return; + } + + stats.elimphases++; + PHASE ("elim-phase", stats.elimphases, + "starting at most %d elimination rounds", opts.elimrounds); + + if (external_prop) { + assert (!level); + private_steps = true; + } + +#ifndef QUIET + int old_active_variables = active (); + int old_eliminated = stats.all.eliminated; +#endif + + // Make sure there was a complete subsumption phase since last + // elimination including vivification etc. + // + if (last.elim.subsumephases == stats.subsumephases) + subsume (); + + reset_watches (); // saves lots of memory + + init_citten (); + + // Alternate one round of bounded variable elimination ('elim_round') and + // subsumption ('subsume_round'), blocked ('block') and covered clause + // elimination ('cover') until nothing changes, or the round limit is hit. + // The loop also aborts early if no variable could be eliminated, the + // empty clause is resolved, it is asynchronously terminated or a + // resolution limit is hit. + + // This variable determines whether the whole loop of this bounded + // variable elimination phase ('elim') ran until completion. This + // potentially triggers an incremental increase of the elimination bound. + // + bool phase_complete = false, deleted_binary_clause = false; + + int round = 1; +#ifndef QUIET + int eliminated = 0; +#endif + + bool round_complete = false; + while (!unsat && !phase_complete && !terminated_asynchronously ()) { +#ifndef QUIET + int eliminated = +#endif + elim_round (round_complete, deleted_binary_clause); + + if (!round_complete) { + PHASE ("elim-phase", stats.elimphases, "last round %d incomplete %s", + round, eliminated ? "but successful" : "and unsuccessful"); + assert (!phase_complete); + break; + } + + if (round++ >= opts.elimrounds) { + PHASE ("elim-phase", stats.elimphases, "round limit %d hit (%s)", + round - 1, + eliminated ? "though last round successful" + : "last round unsuccessful anyhow"); + assert (!phase_complete); + break; + } + + // Prioritize 'subsumption' over blocked and covered clause elimination. + + if (subsume_round ()) + continue; + if (block ()) + continue; + if (cover ()) + continue; + + // Was not able to generate new variable elimination candidates after + // variable elimination round, neither through subsumption, nor blocked, + // nor covered clause elimination. + // + PHASE ("elim-phase", stats.elimphases, + "no new variable elimination candidates"); + + assert (round_complete); + phase_complete = true; + } + + if (phase_complete) { + stats.elimcompleted++; + PHASE ("elim-phase", stats.elimphases, + "fully completed elimination %" PRId64 + " at elimination bound %" PRId64 "", + stats.elimcompleted, lim.elimbound); + } else { + PHASE ("elim-phase", stats.elimphases, + "incomplete elimination %" PRId64 + " at elimination bound %" PRId64 "", + stats.elimcompleted + 1, lim.elimbound); + } + + reset_citten (); + if (deleted_binary_clause) + delete_garbage_clauses (); + init_watches (); + connect_watches (); + + if (unsat) + LOG ("elimination derived empty clause"); + else if (propagated < trail.size ()) { + LOG ("elimination produced %zd units", + (size_t) (trail.size () - propagated)); + if (!propagate ()) { + LOG ("propagating units after elimination results in empty clause"); + learn_empty_clause (); + } + } + + // If we ran variable elimination until completion we increase the + // variable elimination bound and reschedule elimination of all variables. + // + if (phase_complete) + increase_elimination_bound (); + +#ifndef QUIET + eliminated = stats.all.eliminated - old_eliminated; + PHASE ("elim-phase", stats.elimphases, "eliminated %d variables %.2f%%", + eliminated, percent (eliminated, old_active_variables)); +#endif + + if (external_prop) { + assert (!level); + private_steps = false; + } + + if (!update_limits) + return; + + int64_t delta = scale (opts.elimint * (stats.elimphases + 1)); + lim.elim = stats.conflicts + delta; + + PHASE ("elim-phase", stats.elimphases, + "new limit at %" PRId64 " conflicts after %" PRId64 " conflicts", + lim.elim, delta); + + last.elim.fixed = stats.all.fixed; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/elim.hpp b/src/sat/cadical/elim.hpp new file mode 100644 index 000000000..3f5fb63ac --- /dev/null +++ b/src/sat/cadical/elim.hpp @@ -0,0 +1,53 @@ +#ifndef _elim_hpp_INCLUDED +#define _elim_hpp_INCLUDED + +#include "heap.hpp" // Alphabetically after 'elim.hpp'. + +namespace CaDiCaL { + +struct Internal; + +struct elim_more { + Internal *internal; + elim_more (Internal *i) : internal (i) {} + bool operator() (unsigned a, unsigned b); +}; + +typedef heap ElimSchedule; + +struct proof_clause { + int64_t id; + vector literals; + // for lrat + unsigned cid; // kitten id + bool learned; + vector chain; +}; + +enum GateType { NO = 0, EQUI = 1, AND = 2, ITE = 3, XOR = 4, DEF = 5 }; + +struct Eliminator { + + Internal *internal; + ElimSchedule schedule; + + Eliminator (Internal *i) + : internal (i), schedule (elim_more (i)), definition_unit (0), + gatetype (NO) {} + ~Eliminator (); + + queue backward; + + Clause *dequeue (); + void enqueue (Clause *); + + vector gates; + unsigned definition_unit; + vector proof_clauses; + vector marked; + GateType gatetype; +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/elimfast.cpp b/src/sat/cadical/elimfast.cpp new file mode 100644 index 000000000..6806a0637 --- /dev/null +++ b/src/sat/cadical/elimfast.cpp @@ -0,0 +1,570 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Implements a variant of elimination with a much lower limit to be run as +// preprocessing. See elim for comments + +/*------------------------------------------------------------------------*/ + +// Flush garbage clause, check fast elimination limits and return number of +// remaining occurrences (or 'fastelimbound + 1' if some limit was hit). + +int64_t Internal::flush_elimfast_occs (int lit) { + const int64_t occslim = opts.fastelimbound; + const int64_t clslim = opts.fastelimocclim; + const int64_t failed = occslim + 1; + Occs &os = occs (lit); + const const_occs_iterator end = os.end (); + occs_iterator j = os.begin (), i = j; + int64_t res = 0; + while (i != end) { + Clause *c = *i++; + if (c->collect ()) + continue; + *j++ = c; + if (c->size > clslim) { + res = failed; + break; + } + if (++res > occslim) { + assert (opts.fastelimbound < 0 || res == failed); + break; + } + } + if (i != j) { + while (i != end) + *j++ = *i++; + os.resize (j - os.begin ()); + shrink_occs (os); + } + return res; +} + +/*------------------------------------------------------------------------*/ + +// Check whether the number of non-tautological resolvents on 'pivot' is +// smaller or equal to the number of clauses with 'pivot' or '-pivot'. This +// is the main criteria of bounded variable elimination. As a side effect +// it flushes garbage clauses with that variable, sorts its occurrence lists +// (smallest clauses first) and also negates pivot if it has more positive +// than negative occurrences. + +bool Internal::elimfast_resolvents_are_bounded (Eliminator &eliminator, + int pivot) { + assert (eliminator.gates.empty ()); + assert (!eliminator.definition_unit); + + stats.elimtried++; + + assert (!unsat); + assert (active (pivot)); + + const Occs &ps = occs (pivot); + const Occs &ns = occs (-pivot); + + int64_t pos = ps.size (); + int64_t neg = ns.size (); + + int64_t bound = opts.fastelimbound; + + if (!pos || !neg) + return bound >= 0; + + const int64_t sum = pos + neg; + const int64_t product = pos * neg; + if (bound > sum) + bound = sum; + + LOG ("checking number resolvents on %d bounded by " + "%" PRId64 " = %" PRId64 " + %" PRId64 " + %d", + pivot, bound, pos, neg, opts.fastelimbound); + + if (product <= bound) { + LOG ("fast elimination occurrence limits sufficiently small enough"); + return true; + } + + // Try all resolutions between a positive occurrence (outer loop) of + // 'pivot' and a negative occurrence of 'pivot' (inner loop) as long the + // bound on non-tautological resolvents is not hit and the size of the + // generated resolvents does not exceed the resolvent clause size limit. + + int64_t resolvents = 0; // Non-tautological resolvents. + + for (const auto &c : ps) { + assert (!c->redundant); + if (c->garbage) + continue; + for (const auto &d : ns) { + assert (!d->redundant); + if (d->garbage) + continue; + if (resolve_clauses (eliminator, c, pivot, d, true)) { + resolvents++; + int size = clause.size (); + clause.clear (); + LOG ("now at least %" PRId64 + " non-tautological resolvents on pivot %d", + resolvents, pivot); + if (size > opts.fastelimclslim) { + LOG ("resolvent size %d too big after %" PRId64 + " resolvents on %d", + size, resolvents, pivot); + return false; + } + if (resolvents > bound) { + LOG ("too many non-tautological resolvents on %d", pivot); + return false; + } + } else if (unsat) + return false; + else if (val (pivot)) + return false; + } + } + + LOG ("need %" PRId64 " <= %" PRId64 " non-tautological resolvents", + resolvents, bound); + + return true; +} +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +// Add all resolvents on 'pivot' and connect them. + +inline void Internal::elimfast_add_resolvents (Eliminator &eliminator, + int pivot) { + + assert (eliminator.gates.empty ()); + assert (!eliminator.definition_unit); + + LOG ("adding all resolvents on %d", pivot); + + assert (!val (pivot)); + assert (!flags (pivot).eliminated ()); + + const Occs &ps = occs (pivot); + const Occs &ns = occs (-pivot); +#ifdef LOGGING + int64_t resolvents = 0; +#endif + for (auto &c : ps) { + if (unsat) + break; + if (c->garbage) + continue; + for (auto &d : ns) { + if (unsat) + break; + if (d->garbage) + continue; + if (!resolve_clauses (eliminator, c, pivot, d, false)) + continue; + assert (!lrat || !lrat_chain.empty ()); + Clause *r = new_resolved_irredundant_clause (); + elim_update_added_clause (eliminator, r); + eliminator.enqueue (r); + lrat_chain.clear (); + clause.clear (); +#ifdef LOGGING + resolvents++; +#endif + } + } + + LOG ("added %" PRId64 " resolvents to eliminate %d", resolvents, pivot); +} + +/*------------------------------------------------------------------------*/ + +// Try to eliminate 'pivot' by bounded variable elimination. +void Internal::try_to_fasteliminate_variable (Eliminator &eliminator, + int pivot, + bool &deleted_binary_clause) { + + if (!active (pivot)) + return; + assert (!frozen (pivot)); + + // First flush garbage clauses and check limits. + + int64_t bound = opts.fastelimbound; + + int64_t pos = flush_elimfast_occs (pivot); + if (pos > bound) { + LOG ("too many occurrences thus not eliminated %d", pivot); + assert (!eliminator.schedule.contains (abs (pivot))); + return; + } + + int64_t neg = flush_elimfast_occs (-pivot); + if (neg > bound) { + LOG ("too many occurrences thus not eliminated %d", -pivot); + assert (!eliminator.schedule.contains (abs (pivot))); + return; + } + + const int64_t product = pos * neg; + const int64_t sum = pos + neg; + if (bound > sum) + bound = sum; + + if (pos > neg) { + pivot = -pivot; + swap (pos, neg); + } + + LOG ("pivot %d occurs positively %" PRId64 + " times and negatively %" PRId64 " times", + pivot, pos, neg); + + assert (!eliminator.schedule.contains (abs (pivot))); + assert (pos <= neg); + + LOG ("trying to eliminate %d", pivot); + assert (!flags (pivot).eliminated ()); + + // Sort occurrence lists, such that shorter clauses come first. + Occs &ps = occs (pivot); + stable_sort (ps.begin (), ps.end (), clause_smaller_size ()); + Occs &ns = occs (-pivot); + stable_sort (ns.begin (), ns.end (), clause_smaller_size ()); + + if (!unsat && !val (pivot)) { + if (product <= bound || + elimfast_resolvents_are_bounded (eliminator, pivot)) { + LOG ("number of resolvents on %d are bounded", pivot); + elimfast_add_resolvents (eliminator, pivot); + if (!unsat) + mark_eliminated_clauses_as_garbage (eliminator, pivot, + deleted_binary_clause); + if (active (pivot)) + mark_eliminated (pivot); + } else { + LOG ("too many resolvents on %d so not eliminated", pivot); + } + } + + unmark_gate_clauses (eliminator); + elim_backward_clauses (eliminator); +} + +/*------------------------------------------------------------------------*/ + +// This function performs one round of bounded variable elimination and +// returns the number of eliminated variables. The additional result +// 'completed' is true if this elimination round ran to completion (all +// variables have been tried). Otherwise it was asynchronously terminated +// or the resolution limit was hit. + +int Internal::elimfast_round (bool &completed, + bool &deleted_binary_clause) { + + assert (opts.fastelim); + assert (!unsat); + + START_SIMPLIFIER (fastelim, ELIM); + + stats.elimfastrounds++; + + assert (!level); + + int64_t resolution_limit; + + if (opts.elimlimited) { + int64_t delta = stats.propagations.search; + delta *= 1e-3 * opts.elimeffort; + if (delta < opts.elimmineff) + delta = opts.elimmineff; + if (delta > opts.elimmaxeff) + delta = opts.elimmaxeff; + delta = max (delta, (int64_t) 2l * active ()); + + PHASE ("fastelim-round", stats.elimfastrounds, + "limit of %" PRId64 " resolutions", delta); + + resolution_limit = stats.elimres + delta; + } else { + PHASE ("fastelim-round", stats.elimfastrounds, "resolutions unlimited"); + resolution_limit = LONG_MAX; + } + + init_noccs (); + + // First compute the number of occurrences of each literal and at the same + // time mark satisfied clauses and update 'elim' flags of variables in + // clauses with root level assigned literals (both false and true). + // + for (const auto &c : clauses) { + if (c->garbage || c->redundant) + continue; + bool satisfied = false, falsified = false; + for (const auto &lit : *c) { + const signed char tmp = val (lit); + if (tmp > 0) + satisfied = true; + else if (tmp < 0) + falsified = true; + else + assert (active (lit)); + } + if (satisfied) + mark_garbage (c); // forces more precise counts + else { + for (const auto &lit : *c) { + if (!active (lit)) + continue; + if (falsified) + mark_elim (lit); // simulate unit propagation + noccs (lit)++; + } + } + } + + init_occs (); + + Eliminator eliminator (this); + ElimSchedule &schedule = eliminator.schedule; + assert (schedule.empty ()); + + // Now find elimination candidates which occurred in clauses removed since + // the last time we ran bounded variable elimination, which in turned + // triggered their 'elim' bit to be set. + // + for (auto idx : vars) { + if (!active (idx)) + continue; + if (frozen (idx)) + continue; + if (!flags (idx).elim) + continue; + LOG ("scheduling %d for elimination initially", idx); + schedule.push_back (idx); + } + + schedule.shrink (); + +#ifndef QUIET + int64_t scheduled = schedule.size (); +#endif + + PHASE ("fastelim-round", stats.elimfastrounds, + "scheduled %" PRId64 " variables %.0f%% for elimination", + scheduled, percent (scheduled, active ())); + + // Connect irredundant clauses. + // + for (const auto &c : clauses) + if (!c->garbage && !c->redundant) + for (const auto &lit : *c) + if (active (lit)) + occs (lit).push_back (c); + +#ifndef QUIET + const int64_t old_resolutions = stats.elimres; +#endif + const int old_eliminated = stats.all.eliminated; + const int old_fixed = stats.all.fixed; + + // Limit on garbage literals during variable elimination. If the limit is + // hit a garbage collection is performed. + // + const int64_t garbage_limit = (2 * stats.irrlits / 3) + (1 << 20); + + // Main loops tries to eliminate variables according to the schedule. The + // schedule is updated dynamically and variables are potentially + // rescheduled to be tried again if they occur in a removed clause. + // +#ifndef QUIET + int64_t tried = 0; +#endif + while (!unsat && !terminated_asynchronously () && + stats.elimres <= resolution_limit && !schedule.empty ()) { + int idx = schedule.front (); + schedule.pop_front (); + flags (idx).elim = false; + try_to_fasteliminate_variable (eliminator, idx, deleted_binary_clause); +#ifndef QUIET + tried++; +#endif + if (stats.garbage.literals <= garbage_limit) + continue; + mark_redundant_clauses_with_eliminated_variables_as_garbage (); + garbage_collection (); + } + + // If the schedule is empty all variables have been tried (even + // rescheduled ones). Otherwise asynchronous termination happened or we + // ran into the resolution limit (or derived unsatisfiability). + // + completed = !schedule.size (); + + PHASE ("fastelim-round", stats.elimfastrounds, + "tried to eliminate %" PRId64 " variables %.0f%% (%zd remain)", + tried, percent (tried, scheduled), schedule.size ()); + + schedule.erase (); + + reset_occs (); + reset_noccs (); + + // Mark all redundant clauses with eliminated variables as garbage. + // + if (!unsat) + mark_redundant_clauses_with_eliminated_variables_as_garbage (); + + int eliminated = stats.all.eliminated - old_eliminated; + stats.all.fasteliminated += eliminated; +#ifndef QUIET + int64_t resolutions = stats.elimres - old_resolutions; + PHASE ("fastelim-round", stats.elimfastrounds, + "eliminated %d variables %.0f%% in %" PRId64 " resolutions", + eliminated, percent (eliminated, scheduled), resolutions); +#endif + + const int units = stats.all.fixed - old_fixed; + report ('e', !opts.reportall && !(eliminated + units)); + STOP_SIMPLIFIER (fastelim, ELIM); + + return eliminated; // non-zero if successful +} + +/*------------------------------------------------------------------------*/ + +void Internal::elimfast () { + + if (unsat) + return; + if (level) + backtrack (); + if (!propagate ()) { + learn_empty_clause (); + return; + } + + stats.elimfastphases++; + PHASE ("fastelim-phase", stats.elimfastphases, + "starting at most %d elimination rounds", opts.fastelimrounds); + + if (external_prop) { + assert (!level); + private_steps = true; + } + +#ifndef QUIET + int old_active_variables = active (); + int old_eliminated = stats.all.eliminated; +#endif + + reset_watches (); // saves lots of memory + + // Alternate one round of bounded variable elimination ('elim_round') and + // subsumption ('subsume_round'), blocked ('block') and covered clause + // elimination ('cover') until nothing changes, or the round limit is hit. + // The loop also aborts early if no variable could be eliminated, the + // empty clause is resolved, it is asynchronously terminated or a + // resolution limit is hit. + + // This variable determines whether the whole loop of this bounded + // variable elimination phase ('elim') ran until completion. This + // potentially triggers an incremental increase of the elimination bound. + // + bool phase_complete = false, deleted_binary_clause = false; + + int round = 1; +#ifndef QUIET + int eliminated = 0; +#endif + + bool round_complete = false; + while (!unsat && !phase_complete && !terminated_asynchronously ()) { +#ifndef QUIET + int eliminated = +#endif + elimfast_round (round_complete, deleted_binary_clause); + + if (!round_complete) { + PHASE ("fastelim-phase", stats.elimphases, + "last round %d incomplete %s", round, + eliminated ? "but successful" : "and unsuccessful"); + assert (!phase_complete); + break; + } + + if (round++ >= opts.fastelimrounds) { + PHASE ("fastelim-phase", stats.elimphases, "round limit %d hit (%s)", + round - 1, + eliminated ? "though last round successful" + : "last round unsuccessful anyhow"); + assert (!phase_complete); + break; + } + + // Prioritize 'subsumption' over blocked and covered clause elimination. + + if (subsume_round ()) + continue; + + // Was not able to generate new variable elimination candidates after + // variable elimination round, neither through subsumption, nor blocked, + // nor covered clause elimination. + // + PHASE ("fastelim-phase", stats.elimphases, + "no new variable elimination candidates"); + + assert (round_complete); + phase_complete = true; + } + + for (auto idx : vars) { + if (active (idx)) + flags (idx).elim = true; + } + + if (phase_complete) { + stats.elimcompleted++; + PHASE ("fastelim-phase", stats.elimphases, + "fully completed elimination %" PRId64 + " at elimination bound %" PRId64 "", + stats.elimcompleted, lim.elimbound); + } else { + PHASE ("fastelim-phase", stats.elimphases, + "incomplete elimination %" PRId64 + " at elimination bound %" PRId64 "", + stats.elimcompleted + 1, lim.elimbound); + } + + if (deleted_binary_clause) + delete_garbage_clauses (); + init_watches (); + connect_watches (); + + if (unsat) + LOG ("elimination derived empty clause"); + else if (propagated < trail.size ()) { + LOG ("elimination produced %zd units", + (size_t) (trail.size () - propagated)); + if (!propagate ()) { + LOG ("propagating units after elimination results in empty clause"); + learn_empty_clause (); + } + } + +#ifndef QUIET + eliminated = stats.all.eliminated - old_eliminated; + PHASE ("fastelim-phase", stats.elimphases, + "eliminated %d variables %.2f%%", eliminated, + percent (eliminated, old_active_variables)); +#endif + + if (external_prop) { + assert (!level); + private_steps = false; + } +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/ema.cpp b/src/sat/cadical/ema.cpp new file mode 100644 index 000000000..ef6581482 --- /dev/null +++ b/src/sat/cadical/ema.cpp @@ -0,0 +1,95 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +// Updating an exponential moving average is placed here since we want to +// log both updates and phases of initialization, thus need 'LOG'. +// +// We now use initialization bias correction as in the ADAM method +// [KingmaBa-ICLR'15] instead of our ad-hoc initialization method used +// before. Our old variant used exponentially decreasing alphas: +// +// 1, +// 1/2, 1/2, +// 1/4, 1/4, 1/4, 1/4 +// 1/8, 1/8, 1/8, 1/8, 1/8, 1/8, 1/8, 1/8, +// ... +// 2^-n, ..., 2^-n 'n' times +// alpha, alpha, ... now 'alpha' forever. +// +// where 2^-n is the smallest negative power of two above 'alpha' +// +// This old method is better than the initializations described in our +// [BiereFroehlich-POS'15] paper and actually faster than the ADAM method, +// but less precise. We consider this old method obsolete now but it +// could still be useful for implementations relying on integers instead +// of floating points because it only needs shifts and integer arithmetic. +// +// Our new method for unbiased initialization of the exponential averages +// works as follows. First the biased moving average is computed as usual. +// Note that (as already before) we use the simpler equation +// +// new_biased = old_biased + alpha * (y - old_biased); +// +// which in principle (and thus easy to remember) can be implemented as +// +// biased += alpha * (y - biased); +// +// The original formulation in the ADAM paper (with 'alpha = 1 - beta') is +// +// new_biased = beta * old_biased + (1 - beta) * y +// +// To show that these are equivalent (modulo floating point issues) +// consider the following equivalent expressions: +// +// old_biased + alpha * (y - old_biased) +// old_biased + alpha * y - alpha * old_biased +// (1 - alpha) * old_biased + alpha * y +// beta * old_biased + (1 - beta) * y +// +// The real new idea taken from the ADAM paper is however to fix the biased +// moving average with a correction term '1.0 / (1.0 - pow (beta, updated))' +// by multiplication to obtain an unbiased moving average (called simply +// 'value' in our 'code'). In order to avoid computing 'pow' every time, we +// use 'exp' which is multiplied in every update with 'beta'. + +void EMA::update (Internal *internal, double y, const char *name) { +#ifdef LOGGING + updated++; + const double old_value = value; +#endif + const double old_biased = biased; + const double delta = y - old_biased; + const double scaled_delta = alpha * delta; + const double new_biased = old_biased + scaled_delta; + LOG ("update %" PRIu64 " of biased %s EMA %g with %g (delta %g) " + "yields %g (scaled delta %g)", + updated, name, old_biased, y, delta, new_biased, scaled_delta); + biased = new_biased; + const double old_exp = exp; + double new_exp, div, new_value; + if (old_exp) { + new_exp = old_exp * beta; + assert (new_exp < 1); + exp = new_exp; + div = 1 - new_exp; + assert (div > 0); + new_value = new_biased / div; + } else { + new_value = new_biased; +#ifdef LOGGING + new_exp = 0; + div = 1; +#endif + } + value = new_value; + LOG ("update %" PRIu64 " of corrected %s EMA %g with %g (delta %g) " + "yields %g (exponent %g, divisor %g)", + updated, name, old_value, y, delta, new_value, new_exp, div); +#ifndef LOGGING + (void) internal; + (void) name; +#endif +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/ema.hpp b/src/sat/cadical/ema.hpp new file mode 100644 index 000000000..39eba390a --- /dev/null +++ b/src/sat/cadical/ema.hpp @@ -0,0 +1,68 @@ +#ifndef _ema_hpp_INCLUDED +#define _ema_hpp_INCLUDED + +#include + +namespace CaDiCaL { + +struct Internal; + +// This is a more complex generic exponential moving average class to +// support more robust initialization (see comments in the 'update' +// implementation). + +struct EMA { + +#ifdef LOGGING + uint64_t updated; +#endif + double value; // unbiased (corrected) moving average + double biased; // biased initialized moving average + double alpha; // input scaling with 'alpha = 1 - beta' + double beta; // decay of 'biased' with 'beta = 1 - alpha' + double exp; // 'exp = pow (beta, updated)' + + EMA () + : +#ifdef LOGGING + updated (0), +#endif + value (0), biased (0), alpha (0), beta (0), exp (0) { + } + + EMA (double a) + : +#ifdef LOGGING + updated (0), +#endif + value (0), biased (0), alpha (a), beta (1 - a), exp (!!beta) { + assert (beta >= 0); + } + + operator double () const { return value; } + void update (Internal *, double y, const char *name); +}; + +} // namespace CaDiCaL + +/*------------------------------------------------------------------------*/ + +// Compact average update and initialization macros for better logging. + +#define UPDATE_AVERAGE(A, Y) \ + do { \ + A.update (internal, (Y), #A); \ + } while (0) + +#define INIT_EMA(E, WINDOW) \ + do { \ + assert ((WINDOW) >= 1); \ + double ALPHA = 1.0 / (double) (WINDOW); \ + E = EMA (ALPHA); \ + LOG ("init " #E " EMA target alpha %g window %d", ALPHA, \ + (int) WINDOW); \ + } while (0) + +/*------------------------------------------------------------------------*/ + +#endif diff --git a/src/sat/cadical/extend.cpp b/src/sat/cadical/extend.cpp new file mode 100644 index 000000000..26f081360 --- /dev/null +++ b/src/sat/cadical/extend.cpp @@ -0,0 +1,281 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +void External::push_zero_on_extension_stack () { + extension.push_back (0); + LOG ("pushing 0 on extension stack"); +} + +void External::push_id_on_extension_stack (int64_t id) { + const uint32_t higher_bits = static_cast (id << 32); + const uint32_t lower_bits = (id & (((int64_t) 1 << 32) - 1)); + extension.push_back (higher_bits); + extension.push_back (lower_bits); + LOG ("pushing id %" PRIu64 " = %d + %d", id, higher_bits, lower_bits); +} + +void External::push_clause_literal_on_extension_stack (int ilit) { + assert (ilit); + const int elit = internal->externalize (ilit); + assert (elit); + extension.push_back (elit); + LOG ("pushing clause literal %d on extension stack (internal %d)", elit, + ilit); +} + +void External::push_witness_literal_on_extension_stack (int ilit) { + assert (ilit); + const int elit = internal->externalize (ilit); + assert (elit); + extension.push_back (elit); + LOG ("pushing witness literal %d on extension stack (internal %d)", elit, + ilit); + if (marked (witness, elit)) + return; + LOG ("marking witness %d", elit); + mark (witness, elit); +} + +// The extension stack allows to reconstruct a satisfying assignment for the +// original formula after removing eliminated clauses. This was pioneered +// by Niklas Soerensson in MiniSAT and for instance is described in our +// inprocessing paper, published at IJCAR'12. This first function adds a +// clause to this stack. First the blocking or eliminated literal is added, +// and then the rest of the clause. + +void External::push_clause_on_extension_stack (Clause *c) { + internal->stats.weakened++; + internal->stats.weakenedlen += c->size; + push_zero_on_extension_stack (); + push_id_on_extension_stack (c->id); + push_zero_on_extension_stack (); + for (const auto &lit : *c) + push_clause_literal_on_extension_stack (lit); +} + +void External::push_clause_on_extension_stack (Clause *c, int pivot) { + push_zero_on_extension_stack (); + push_witness_literal_on_extension_stack (pivot); + push_clause_on_extension_stack (c); +} + +void External::push_binary_clause_on_extension_stack (int64_t id, int pivot, + int other) { + internal->stats.weakened++; + internal->stats.weakenedlen += 2; + push_zero_on_extension_stack (); + push_witness_literal_on_extension_stack (pivot); + push_zero_on_extension_stack (); + push_id_on_extension_stack (id); + push_zero_on_extension_stack (); + push_clause_literal_on_extension_stack (pivot); + push_clause_literal_on_extension_stack (other); +} + +/*------------------------------------------------------------------------*/ + +void External::push_external_clause_and_witness_on_extension_stack ( + const vector &c, const vector &w, int64_t id) { + assert (id); + extension.push_back (0); + for (const auto &elit : w) { + assert (elit != INT_MIN); + init (abs (elit)); + extension.push_back (elit); + mark (witness, elit); + } + extension.push_back (0); + const uint32_t higher_bits = static_cast (id << 32); + const uint32_t lower_bits = (id & (((int64_t) 1 << 32) - 1)); + extension.push_back (higher_bits); + extension.push_back (lower_bits); + extension.push_back (0); + for (const auto &elit : c) { + assert (elit != INT_MIN); + init (abs (elit)); + extension.push_back (elit); + } +} + +/*------------------------------------------------------------------------*/ + +// This is the actual extension process. It goes backward over the clauses +// on the extension stack and flips the assignment of one of the blocking +// literals in the conditional autarky stored before the clause. In the +// original algorithm for witness construction for variable elimination and +// blocked clause removal the conditional autarky consists of a single +// literal from the removed clause, while in general the autarky witness can +// contain an arbitrary set of literals. We are using the more general +// witness reconstruction here which for instance would also work for +// super-blocked or set-blocked clauses. + +void External::extend () { + + assert (!extended); + START (extend); + internal->stats.extensions++; + + PHASE ("extend", internal->stats.extensions, + "mapping internal %d assignments to %d assignments", + internal->max_var, max_var); + +#ifndef QUIET + int64_t updated = 0; +#endif + for (unsigned i = 1; i <= (unsigned) max_var; i++) { + const int ilit = e2i[i]; + if (!ilit) + continue; + if (i >= vals.size ()) + vals.resize (i + 1, false); + vals[i] = (internal->val (ilit) > 0); +#ifndef QUIET + updated++; +#endif + } + PHASE ("extend", internal->stats.extensions, + "updated %" PRId64 " external assignments", updated); + PHASE ("extend", internal->stats.extensions, + "extending through extension stack of size %zd", + extension.size ()); + const auto begin = extension.begin (); + auto i = extension.end (); +#ifndef QUIET + int64_t flipped = 0; +#endif + while (i != begin) { + bool satisfied = false; + int lit; + assert (i != begin); + while ((lit = *--i)) { + if (satisfied) + continue; + if (ival (lit) == lit) + satisfied = true; + assert (i != begin); + } + assert (i != begin); + LOG ("id=%" PRId64, ((int64_t) *i << 32) + *(i - 1)); + assert (*i || *(i - 1)); + --i; + assert (i != begin); + --i; + assert (i != begin); + assert (!*i); + --i; + assert (i != begin); + if (satisfied) + while (*--i) + assert (i != begin); + else { + while ((lit = *--i)) { + const int tmp = ival (lit); // not 'signed char'!!! + if (tmp != lit) { + LOG ("flipping blocking literal %d", lit); + assert (lit); + assert (lit != INT_MIN); + size_t idx = abs (lit); + if (idx >= vals.size ()) + vals.resize (idx + 1, false); + vals[idx] = !vals[idx]; + internal->stats.extended++; +#ifndef QUIET + flipped++; +#endif + } + assert (i != begin); + } + } + } + PHASE ("extend", internal->stats.extensions, + "flipped %" PRId64 " literals during extension", flipped); + extended = true; + LOG ("extended"); + STOP (extend); +} + +/*------------------------------------------------------------------------*/ + +bool External::traverse_witnesses_backward (WitnessIterator &it) { + if (internal->unsat) + return true; + vector clause, witness; + const auto begin = extension.begin (); + auto i = extension.end (); + while (i != begin) { + int lit; + while ((lit = *--i)) + clause.push_back (lit); + assert (!lit); + --i; + const int64_t id = + ((int64_t) * (i - 1) << 32) + static_cast (*i); + assert (id); + i -= 2; + assert (!*i); + assert (i != begin); + while ((lit = *--i)) + witness.push_back (lit); + reverse (clause.begin (), clause.end ()); + reverse (witness.begin (), witness.end ()); + LOG (clause, "traversing clause"); + if (!it.witness (clause, witness, id)) + return false; + clause.clear (); + witness.clear (); + } + return true; +} + +bool External::traverse_witnesses_forward (WitnessIterator &it) { + if (internal->unsat) + return true; + vector clause, witness; + const auto end = extension.end (); + auto i = extension.begin (); + if (i != end) { + int lit = *i++; + do { + assert (!lit), (void) lit; + while ((lit = *i++)) + witness.push_back (lit); + assert (!lit); + assert (i != end); + assert (!*i); + const int64_t id = + ((int64_t) *i << 32) + static_cast (*(i + 1)); + assert (id > 0); + i += 3; + assert (*i); + assert (i != end); + while (i != end && (lit = *i++)) + clause.push_back (lit); + if (!it.witness (clause, witness, id)) + return false; + clause.clear (); + witness.clear (); + } while (i != end); + } + return true; +} + +/*------------------------------------------------------------------------*/ + +void External::conclude_sat () { + if (!internal->proof || concluded) + return; + concluded = true; + if (!extended) + extend (); + vector model; + for (int idx = 1; idx <= max_var; idx++) { + if (ervars[idx]) + continue; + const int lit = ival (idx); + model.push_back (lit); + } + internal->proof->conclude_sat (model); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/external.cpp b/src/sat/cadical/external.cpp new file mode 100644 index 000000000..acb11a9d6 --- /dev/null +++ b/src/sat/cadical/external.cpp @@ -0,0 +1,1022 @@ +#include "internal.hpp" +#include + +namespace CaDiCaL { + +External::External (Internal *i) + : internal (i), max_var (0), vsize (0), extended (false), + concluded (false), terminator (0), learner (0), fixed_listener (0), + propagator (0), solution (0), vars (max_var) { + assert (internal); + assert (!internal->external); + internal->external = this; +} + +External::~External () { + if (solution) + delete[] solution; +} + +void External::enlarge (int new_max_var) { + + assert (!extended); + + size_t new_vsize = vsize ? 2 * vsize : 1 + (size_t) new_max_var; + while (new_vsize <= (size_t) new_max_var) + new_vsize *= 2; + LOG ("enlarge external size from %zd to new size %zd", vsize, new_vsize); + vsize = new_vsize; +} + +void External::init (int new_max_var, bool extension) { + assert (!extended); + if (new_max_var <= max_var) + return; + int new_vars = new_max_var - max_var; + int old_internal_max_var = internal->max_var; + int new_internal_max_var = old_internal_max_var + new_vars; + internal->init_vars (new_internal_max_var); + if ((size_t) new_max_var >= vsize) + enlarge (new_max_var); + LOG ("initialized %d external variables", new_vars); + if (!max_var) { + assert (e2i.empty ()); + e2i.push_back (0); + ext_units.push_back (0); + ext_units.push_back (0); + ext_flags.push_back (0); + ervars.push_back (0); + assert (internal->i2e.empty ()); + internal->i2e.push_back (0); + } else { + assert (e2i.size () == (size_t) max_var + 1); + assert (internal->i2e.size () == (size_t) old_internal_max_var + 1); + } + unsigned iidx = old_internal_max_var + 1, eidx; + for (eidx = max_var + 1u; eidx <= (unsigned) new_max_var; + eidx++, iidx++) { + LOG ("mapping external %u to internal %u", eidx, iidx); + assert (e2i.size () == eidx); + e2i.push_back (iidx); + ext_units.push_back (0); + ext_units.push_back (0); + ext_flags.push_back (0); + ervars.push_back (0); + internal->i2e.push_back (eidx); + assert (internal->i2e[iidx] == (int) eidx); + assert (e2i[eidx] == (int) iidx); + } + if (extension) + internal->stats.variables_extension += new_vars; + else + internal->stats.variables_original += new_vars; + if (new_max_var >= (int64_t) is_observed.size ()) + is_observed.resize (1 + (size_t) new_max_var, false); + if (internal->opts.checkfrozen) + if (new_max_var >= (int64_t) moltentab.size ()) + moltentab.resize (1 + (size_t) new_max_var, false); + assert (iidx == (size_t) new_internal_max_var + 1); + assert (eidx == (size_t) new_max_var + 1); + assert (ext_units.size () == (size_t) new_max_var * 2 + 2); + max_var = new_max_var; +} + +/*------------------------------------------------------------------------*/ + +void External::reset_assumptions () { + assumptions.clear (); + internal->reset_assumptions (); +} + +void External::reset_concluded () { + concluded = false; + internal->reset_concluded (); +} + +void External::reset_constraint () { + constraint.clear (); + internal->reset_constraint (); +} + +void External::reset_extended () { + if (!extended) + return; + LOG ("reset extended"); + extended = false; +} + +void External::reset_limits () { internal->reset_limits (); } + +/*------------------------------------------------------------------------*/ + +// when extension is true, elit should be a fresh variable and +// we can set a flag that it is an extension variable. +// This is then used in the API contracts, that extension variables are +// never part of the input +int External::internalize (int elit, bool extension) { + int ilit; + if (elit) { + assert (elit != INT_MIN); + const int eidx = abs (elit); + if (extension && eidx <= max_var) + FATAL ("can not add a definition for an already used variable %d", + eidx); + if (eidx > max_var) { + init (eidx, extension); + } + if (extension) { + assert (ervars.size () > (size_t) eidx); + ervars[eidx] = true; + } + ilit = e2i[eidx]; + if (elit < 0) + ilit = -ilit; + if (!ilit) { + assert (internal->max_var < INT_MAX); + ilit = internal->max_var + 1u; + internal->init_vars (ilit); + e2i[eidx] = ilit; + LOG ("mapping external %d to internal %d", eidx, ilit); + e2i[eidx] = ilit; + internal->i2e.push_back (eidx); + assert (internal->i2e[ilit] == eidx); + assert (e2i[eidx] == ilit); + if (elit < 0) + ilit = -ilit; + } + if (internal->opts.checkfrozen) { + assert (eidx < (int64_t) moltentab.size ()); + if (moltentab[eidx]) + FATAL ("can not reuse molten literal %d", eidx); + } + Flags &f = internal->flags (ilit); + if (f.status == Flags::UNUSED) + internal->mark_active (ilit); + else if (f.status != Flags::ACTIVE && f.status != Flags::FIXED) + internal->reactivate (ilit); + if (!marked (tainted, elit) && marked (witness, -elit)) { + assert (!internal->opts.checkfrozen); + LOG ("marking tainted %d", elit); + mark (tainted, elit); + } + } else + ilit = 0; + return ilit; +} + +void External::add (int elit) { + assert (elit != INT_MIN); + reset_extended (); + + bool forgettable = false; + + if (internal->opts.check && + (internal->opts.checkwitness || internal->opts.checkfailed)) { + + forgettable = + internal->from_propagator && internal->ext_clause_forgettable; + + // Forgettable clauses (coming from the external propagator) are not + // saved into the external 'original' stack. They are stored separately + // in external 'forgettable_original', from where they are deleted when + // the corresponding clause is deleted (actually deleted, not just + // marked as garbage). + if (!forgettable) + original.push_back (elit); + } + + const int ilit = internalize (elit); + assert (!elit == !ilit); + + // The external literals of the new clause must be saved for later + // when the proof is printed during add_original_lit (0) + if (elit && (internal->proof || forgettable)) { + eclause.push_back (elit); + if (internal->lrat) { + // actually find unit of -elit (flips elit < 0) + unsigned eidx = (elit > 0) + 2u * (unsigned) abs (elit); + assert ((size_t) eidx < ext_units.size ()); + const int64_t id = ext_units[eidx]; + bool added = ext_flags[abs (elit)]; + if (id && !added) { + ext_flags[abs (elit)] = true; + internal->lrat_chain.push_back (id); + } + } + } + + if (!elit && internal->proof && internal->lrat) { + for (const auto &elit : eclause) { + ext_flags[abs (elit)] = false; + } + } + + if (elit) + LOG ("adding external %d as internal %d", elit, ilit); + internal->add_original_lit (ilit); + + // Clean-up saved external literals once proof line is printed + if (!elit && (internal->proof || forgettable)) + eclause.clear (); +} + +void External::assume (int elit) { + assert (elit); + reset_extended (); + if (internal->proof) + internal->proof->add_assumption (elit); + assumptions.push_back (elit); + const int ilit = internalize (elit); + assert (ilit); + LOG ("assuming external %d as internal %d", elit, ilit); + internal->assume (ilit); +} + +bool External::flip (int elit) { + assert (elit); + assert (elit != INT_MIN); + assert (!propagator); + + int eidx = abs (elit); + if (eidx > max_var) + return false; + if (marked (witness, elit)) + return false; + int ilit = e2i[eidx]; + if (!ilit) + return false; + bool res = internal->flip (ilit); + if (res && extended) + reset_extended (); + return res; +} + +bool External::flippable (int elit) { + assert (elit); + assert (elit != INT_MIN); + assert (!propagator); + + int eidx = abs (elit); + if (eidx > max_var) + return false; + if (marked (witness, elit)) + return false; + int ilit = e2i[eidx]; + if (!ilit) + return false; + return internal->flippable (ilit); +} + +bool External::failed (int elit) { + assert (elit); + assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) + return 0; + int ilit = e2i[eidx]; + if (!ilit) + return 0; + if (elit < 0) + ilit = -ilit; + return internal->failed (ilit); +} + +void External::constrain (int elit) { + if (constraint.size () && !constraint.back ()) { + LOG (constraint, "replacing previous constraint"); + reset_constraint (); + } + assert (elit != INT_MIN); + reset_extended (); + const int ilit = internalize (elit); + assert (!elit == !ilit); + if (elit) + LOG ("adding external %d as internal %d to constraint", elit, ilit); + else if (!elit && internal->proof) { + internal->proof->add_constraint (constraint); + } + constraint.push_back (elit); + internal->constrain (ilit); +} + +bool External::failed_constraint () { + return internal->failed_constraint (); +} + +void External::phase (int elit) { + assert (elit); + assert (elit != INT_MIN); + const int ilit = internalize (elit); + internal->phase (ilit); +} + +void External::unphase (int elit) { + assert (elit); + assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) { + UNUSED: + LOG ("resetting forced phase of unused external %d ignored", elit); + return; + } + int ilit = e2i[eidx]; + if (!ilit) + goto UNUSED; + if (elit < 0) + ilit = -ilit; + internal->unphase (ilit); +} + +/*------------------------------------------------------------------------*/ + +// External propagation related functions +// +// Note that when an already assigned variable is added as observed, the +// solver will backtrack to undo this assignment. +// +void External::add_observed_var (int elit) { + if (!propagator) { + LOG ("No connected propagator that could observe the variable, " + "observed flag is not set."); + return; + } + + assert (elit); + assert (elit != INT_MIN); + reset_extended (); // tainting! + + int eidx = abs (elit); + if (eidx <= max_var && + (marked (witness, elit) || marked (witness, -elit))) { + LOG ("Error, only clean variables are allowed to become observed."); + assert (false); + + // TODO: here needs to come the taint and restore of the newly + // observed variable. Restore_clauses must be called before continue. + // LOG ("marking tainted %d", elit); + // mark (tainted, elit); + // mark (tainted, -elit); + // restore_clauses ... + } + + if (eidx >= (int64_t) is_observed.size ()) + is_observed.resize (1 + (size_t) eidx, false); + + if (is_observed[eidx]) + return; + + LOG ("marking %d as externally watched", eidx); + + // Will do the necessary internalization + freeze (elit); + is_observed[eidx] = true; + + int ilit = internalize (elit); + // internal add-observed-var backtracks to a lower decision level to + // unassign the variable in case it was already assigned previously (but + // not on the current level) + internal->add_observed_var (ilit); + + if (propagator->is_lazy) + return; + + // In case this variable was already assigned (e.g. via unit clause) and + // got compacted to map to another (not observed) variable, it can not be + // unnasigned so it must be notified explicitly now. (-> Can lead to + // repeated fixed assignment notifications, in case it was unobserved and + // observed again. But a repeated notification is less error-prone than + // never notifying an assignment.) + const int tmp = fixed (elit); + if (!tmp) + return; + int unit = tmp < 0 ? -elit : elit; + + LOG ("notify propagator about fixed assignment upon observe for %d", + unit); + + // internal add-observed-var had to backtrack to root-level already + assert (!internal->level); + + std::vector assigned = {unit}; + propagator->notify_assignment (assigned); +} + +void External::remove_observed_var (int elit) { + if (!propagator) { + LOG ("No connected propagator that could have watched the variable"); + return; + } + int eidx = abs (elit); + + if (eidx > max_var) + return; + + if (is_observed[eidx]) { + // Follow opposite order of add_observed_var, first remove internal + // is_observed + int ilit = e2i[eidx]; // internalize (elit); + internal->remove_observed_var (ilit); + + is_observed[eidx] = false; + melt (elit); + LOG ("unmarking %d as externally watched", eidx); + } +} + +void External::reset_observed_vars () { + // Shouldn't be called if there is no connected propagator + assert (propagator); + reset_extended (); + + internal->notified = 0; + LOG ("reset notified counter to 0"); + + if (!is_observed.size ()) + return; + + assert (!max_var || (size_t) max_var + 1 == is_observed.size ()); + + for (auto elit : vars) { + int eidx = abs (elit); + assert (eidx <= max_var); + if (is_observed[eidx]) { + int ilit = internalize (elit); + internal->remove_observed_var (ilit); + LOG ("unmarking %d as externally watched", eidx); + is_observed[eidx] = false; + melt (elit); + } + } +} + +bool External::observed (int elit) { + assert (elit); + assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) + return false; + if (eidx >= (int) is_observed.size ()) + return false; + + return is_observed[eidx]; +} + +bool External::is_witness (int elit) { + assert (elit); + assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) + return false; + return (marked (witness, elit) || marked (witness, -elit)); +} + +bool External::is_decision (int elit) { + assert (elit); + assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) + return false; + + int ilit = internalize (elit); + return internal->is_decision (ilit); +} + +void External::force_backtrack (size_t new_level) { + if (!propagator) { + LOG ("No connected propagator that could force backtracking"); + return; + } + LOG ("force backtrack to level %zd", new_level); + internal->force_backtrack (new_level); +} + +/*------------------------------------------------------------------------*/ + +int External::propagate_assumptions () { + int res = internal->propagate_assumptions (); + if (res == 10 && !extended) + extend (); // Call solution reconstruction + check_solve_result (res); + reset_limits (); + return res; +} + +void External::implied (std::vector &trailed) { + std::vector ilit_implicants; + internal->implied (ilit_implicants); + + // Those implied literals must be filtered out that are witnesses + // on the reconstruction stack -> no inplace externalize is possible. + // (Internal does not see these marks, so no earlier filter is + // possible.) + + trailed.clear(); + + for (const auto &ilit : ilit_implicants) { + assert (ilit); + const int elit = internal->externalize (ilit); + const int eidx = abs (elit); + const bool is_extension_var = ervars[eidx]; + if (!marked (tainted, elit) && !is_extension_var) { + trailed.push_back (elit); + } + } +} + +void External::conclude_unknown () { + if (!internal->proof || concluded) + return; + concluded = true; + + vector trail; + implied (trail); + internal->proof->conclude_unknown (trail); +} + +/*------------------------------------------------------------------------*/ + +// Internal checker if 'solve' claims the formula to be satisfiable. + +void External::check_satisfiable () { + LOG ("checking satisfiable"); + if (!extended) + extend (); + if (internal->opts.checkwitness) + check_assignment (&External::ival); + if (internal->opts.checkassumptions && !assumptions.empty ()) + check_assumptions_satisfied (); + if (internal->opts.checkconstraint && !constraint.empty ()) + check_constraint_satisfied (); +} + +// Internal checker if 'solve' claims formula to be unsatisfiable. + +void External::check_unsatisfiable () { + LOG ("checking unsatisfiable"); + if (!internal->opts.checkfailed) + return; + if (!assumptions.empty () || !constraint.empty ()) + check_failing (); +} + +// Check result of 'solve' to be correct. + +void External::check_solve_result (int res) { + if (!internal->opts.check) + return; + if (res == 10) + check_satisfiable (); + if (res == 20) + check_unsatisfiable (); +} + +// Prepare checking that completely molten literals are not used as argument +// of 'add' or 'assume', which is invalid under freezing semantics. This +// case would be caught by our 'restore' implementation so is only needed +// for checking the deprecated 'freeze' semantics. + +void External::update_molten_literals () { + if (!internal->opts.checkfrozen) + return; + assert ((size_t) max_var + 1 == moltentab.size ()); +#ifdef LOGGING + int registered = 0, molten = 0; +#endif + for (auto lit : vars) { + if (moltentab[lit]) { + LOG ("skipping already molten literal %d", lit); +#ifdef LOGGING + molten++; +#endif + } else if (frozen (lit)) + LOG ("skipping currently frozen literal %d", lit); + else { + LOG ("new molten literal %d", lit); + moltentab[lit] = true; +#ifdef LOGGING + registered++; + molten++; +#endif + } + } + LOG ("registered %d new molten literals", registered); + LOG ("reached in total %d molten literals", molten); +} + +int External::solve (bool preprocess_only) { + reset_extended (); + update_molten_literals (); + int res = internal->solve (preprocess_only); + check_solve_result (res); + reset_limits (); + return res; +} + +void External::terminate () { internal->terminate (); } + +int External::lookahead () { + reset_extended (); + update_molten_literals (); + int ilit = internal->lookahead (); + const int elit = + (ilit && ilit != INT_MIN) ? internal->externalize (ilit) : 0; + LOG ("lookahead internal %d external %d", ilit, elit); + return elit; +} + +CaDiCaL::CubesWithStatus External::generate_cubes (int depth, + int min_depth = 0) { + reset_extended (); + update_molten_literals (); + reset_limits (); + auto cubes = internal->generate_cubes (depth, min_depth); + auto externalize = [this] (int ilit) { + const int elit = ilit ? internal->externalize (ilit) : 0; + MSG ("lookahead internal %d external %d", ilit, elit); + return elit; + }; + auto externalize_map = [this, externalize] (std::vector cube) { + (void) this; + MSG ("Cube : "); + std::for_each (begin (cube), end (cube), externalize); + }; + std::for_each (begin (cubes.cubes), end (cubes.cubes), externalize_map); + + return cubes; +} + +/*------------------------------------------------------------------------*/ + +void External::freeze (int elit) { + reset_extended (); + int ilit = internalize (elit); + unsigned eidx = vidx (elit); + if (eidx >= frozentab.size ()) + frozentab.resize (eidx + 1, 0); + unsigned &ref = frozentab[eidx]; + if (ref < UINT_MAX) { + ref++; + LOG ("external variable %d frozen once and now frozen %u times", eidx, + ref); + } else + LOG ("external variable %d frozen but remains frozen forever", eidx); + internal->freeze (ilit); +} + +void External::melt (int elit) { + reset_extended (); + int ilit = internalize (elit); + unsigned eidx = vidx (elit); + assert (eidx < frozentab.size ()); + unsigned &ref = frozentab[eidx]; + assert (ref > 0); + if (ref < UINT_MAX) { + if (!--ref) { + if (observed (elit)) { + ref++; + LOG ("external variable %d is observed, can not be completely " + "molten", + eidx); + } else + LOG ("external variable %d melted once and now completely melted", + eidx); + } else + LOG ("external variable %d melted once but remains frozen %u times", + eidx, ref); + } else + LOG ("external variable %d melted but remains frozen forever", eidx); + internal->melt (ilit); +} + +/*------------------------------------------------------------------------*/ + +void External::check_assignment (int (External::*a) (int) const) { + + // First check all assigned and consistent. + // + for (auto idx : vars) { + if (!(this->*a) (idx)) + FATAL ("unassigned variable: %d", idx); + int value_idx = (this->*a) (idx); + int value_neg_idx = (this->*a) (-idx); + if (value_idx == idx) + assert (value_neg_idx == idx); + else { + assert (value_idx == -idx); + assert (value_neg_idx == -idx); + } + if (value_idx != value_neg_idx) + FATAL ("inconsistently assigned literals %d and %d", idx, -idx); + } + + // Then check that all (saved) original clauses are satisfied. + // + bool satisfied = false; + const auto end = original.end (); + auto start = original.begin (), i = start; +#ifndef QUIET + int64_t count = 0; +#endif + for (; i != end; i++) { + int lit = *i; + if (!lit) { + if (!satisfied) { + fatal_message_start (); + fputs ("unsatisfied clause:\n", stderr); + for (auto j = start; j != i; j++) + fprintf (stderr, "%d ", *j); + fputc ('0', stderr); + fatal_message_end (); + } + satisfied = false; + start = i + 1; +#ifndef QUIET + count++; +#endif + } else if (!satisfied && (this->*a) (lit) == lit) + satisfied = true; + } + + bool presence_flag; + // Check those forgettable external clauses that are still present, but + // only if the external propagator is still connected (otherwise solution + // reconstruction is allowed to touch the previously observed variables so + // there is no guarantee that the final model will satisfy these clauses.) + for (const auto &forgettables : forgettable_original) { + if (!propagator) + break; + presence_flag = true; + satisfied = false; +#ifndef QUIET + count++; +#endif + std::vector literals; + for (const auto lit : forgettables.second) { + if (presence_flag) { + // First integer is a Boolean flag, not a literal + if (!lit) { + // Deleted clauses can be ignored, they count as satisfied + satisfied = true; + break; + } + presence_flag = false; + continue; + } + + if ((this->*a) (lit) == lit) { + satisfied = true; + break; + } + } + + if (!satisfied) { + fatal_message_start (); + fputs ("unsatisfied external forgettable clause:\n", stderr); + for (size_t j = 1; j < forgettables.second.size (); j++) + fprintf (stderr, "%d ", forgettables.second[j]); + fputc ('0', stderr); + fatal_message_end (); + } + } +#ifndef QUIET + VERBOSE (1, "satisfying assignment checked on %" PRId64 " clauses", + count); +#endif +} + +/*------------------------------------------------------------------------*/ + +void External::check_assumptions_satisfied () { + for (const auto &lit : assumptions) { + // Not 'signed char' !!!! + const int tmp = ival (lit); + if (tmp != lit) + FATAL ("assumption %d falsified", lit); + if (!tmp) + FATAL ("assumption %d unassigned", lit); + } + VERBOSE (1, "checked that %zd assumptions are satisfied", + assumptions.size ()); +} + +void External::check_constraint_satisfied () { + for (const auto lit : constraint) { + if (ival (lit) == lit) { + VERBOSE (1, "checked that constraint is satisfied"); + return; + } + } + FATAL ("constraint not satisfied"); +} + +void External::check_failing () { + Solver *checker = new Solver (); + DeferDeletePtr delete_checker (checker); + checker->prefix ("checker "); +#ifdef LOGGING + if (internal->opts.log) + checker->set ("log", true); +#endif + + for (const auto lit : assumptions) { + if (!failed (lit)) + continue; + LOG ("checking failed literal %d in core", lit); + checker->add (lit); + checker->add (0); + } + if (failed_constraint ()) { + LOG (constraint, "checking failed constraint"); + for (const auto lit : constraint) + checker->add (lit); + } else if (constraint.size ()) + LOG (constraint, "constraint satisfied and ignored"); + + // Add original clauses as last step, failing () and failed_constraint () + // might add more external clauses (due to lazy explanation) + for (const auto lit : original) + checker->add (lit); + + // Add every forgettable external clauses + for (const auto &forgettables : forgettable_original) { + bool presence_flag = true; + for (const auto lit : forgettables.second) { + if (presence_flag) { + // First integer is a Boolean flag, not a literal, ignore it here + presence_flag = false; + continue; + } + checker->add (lit); + } + checker->add (0); + } + + int res = checker->solve (); + if (res != 20) + FATAL ("failed assumptions do not form a core"); + delete_checker.free (); + VERBOSE (1, "checked that %zd failing assumptions form a core", + assumptions.size ()); +} + +/*------------------------------------------------------------------------*/ + +// Traversal of unit clauses is implemented here. + +// In principle we want to traverse the clauses of the simplified formula +// only, particularly eliminated variables should be completely removed. +// This poses the question what to do with unit clauses. Should they be +// considered part of the simplified formula or of the witness to construct +// solutions for the original formula? Ideally they should be considered +// to be part of the witness only, i.e., as they have been simplified away. + +// Therefore we distinguish frozen and non-frozen units during clause +// traversal. Frozen units are treated as unit clauses while non-frozen +// units are treated as if they were already eliminated and put on the +// extension stack as witness clauses. + +// Furthermore, eliminating units during 'compact' could be interpreted as +// variable elimination, i.e., just add the resolvents (remove falsified +// literals), then drop the clauses with the unit, and push the unit on the +// extension stack. This is of course only OK if the user did not freeze +// that variable (even implicitly during assumptions). + +// Thanks go to Fahiem Bacchus for asking why there is a necessity to +// distinguish these two cases (frozen and non-frozen units). The answer is +// that it is not strictly necessary, and this distinction could be avoided +// by always treating units as remaining unit clauses, thus only using the +// first of the two following functions and dropping the 'if (!frozen (idx)) +// continue;' check in it. This has however the down-side that those units +// are still in the simplified formula and only as units. I would not +// consider such a formula as really being 'simplified'. On the other hand +// if the user explicitly freezes a literal, then it should continue to be +// in the simplified formula during traversal. So also only using the +// second function is not ideal. + +// There is however a catch where this solution breaks down (in the sense of +// producing less optimal results - that is keeping units in the formula +// which better would be witness clauses). The problem is with compact +// preprocessing which removes eliminated but also fixed internal variables. +// One internal unit (fixed) variable is kept and all the other external +// literals which became unit are mapped to that internal literal (negated +// or positively). Compact is called non-deterministically from the point +// of the user and thus there is no control on when this happens. If +// compact happens those external units are mapped to a single internal +// literal now and then all share the same 'frozen' counter. So if the +// user freezes one of them all in essence get frozen, which in turn then +// makes a difference in terms of traversing such a unit as unit clause or +// as unit witness. + +bool External::traverse_all_frozen_units_as_clauses (ClauseIterator &it) { + if (internal->unsat) + return true; + + vector clause; + + for (auto idx : vars) { + if (!frozen (idx)) + continue; + const int tmp = fixed (idx); + if (!tmp) + continue; + int unit = tmp < 0 ? -idx : idx; + clause.push_back (unit); + if (!it.clause (clause)) + return false; + clause.clear (); + } + + return true; +} + +bool External::traverse_all_non_frozen_units_as_witnesses ( + WitnessIterator &it) { + if (internal->unsat) + return true; + + vector clause_and_witness; + for (auto idx : vars) { + if (frozen (idx)) + continue; + const int tmp = fixed (idx); + if (!tmp) + continue; + int unit = tmp < 0 ? -idx : idx; + const int ilit = e2i[idx] * (tmp < 0 ? -1 : 1); + // heurstically add + max_var to the id to avoid reusing ids + const int64_t id = internal->lrat ? internal->unit_id (ilit) : 1; + assert (id); + clause_and_witness.push_back (unit); + if (!it.witness (clause_and_witness, clause_and_witness, id + max_var)) + return false; + clause_and_witness.clear (); + } + + return true; +} + +/*------------------------------------------------------------------------*/ + +void External::copy_flags (External &other) const { + const vector &this_ftab = internal->ftab; + vector &other_ftab = other.internal->ftab; + const unsigned limit = min (max_var, other.max_var); + for (unsigned eidx = 1; eidx <= limit; eidx++) { + const int this_ilit = e2i[eidx]; + if (!this_ilit) + continue; + const int other_ilit = other.e2i[eidx]; + if (!other_ilit) + continue; + if (!internal->active (this_ilit)) + continue; + if (!other.internal->active (other_ilit)) + continue; + assert (this_ilit != INT_MIN); + assert (other_ilit != INT_MIN); + const Flags &this_flags = this_ftab[abs (this_ilit)]; + Flags &other_flags = other_ftab[abs (other_ilit)]; + this_flags.copy (other_flags); + } +} + +/*------------------------------------------------------------------------*/ + +void External::export_learned_empty_clause () { + assert (learner); + if (learner->learning (0)) { + LOG ("exporting learned empty clause"); + learner->learn (0); + } else + LOG ("not exporting learned empty clause"); +} + +void External::export_learned_unit_clause (int ilit) { + assert (learner); + if (learner->learning (1)) { + LOG ("exporting learned unit clause"); + const int elit = internal->externalize (ilit); + assert (elit); + learner->learn (elit); + learner->learn (0); + } else + LOG ("not exporting learned unit clause"); +} + +void External::export_learned_large_clause (const vector &clause) { + assert (learner); + size_t size = clause.size (); + assert (size <= (unsigned) INT_MAX); + if (learner->learning ((int) size)) { + LOG ("exporting learned clause of size %zu", size); + for (auto ilit : clause) { + const int elit = internal->externalize (ilit); + assert (elit); + learner->learn (elit); + } + learner->learn (0); + } else + LOG ("not exporting learned clause of size %zu", size); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/external.hpp b/src/sat/cadical/external.hpp new file mode 100644 index 000000000..25e21affb --- /dev/null +++ b/src/sat/cadical/external.hpp @@ -0,0 +1,461 @@ +#ifndef _external_hpp_INCLUDED +#define _external_hpp_INCLUDED + +/*------------------------------------------------------------------------*/ + +#include "range.hpp" +#include +#include + +/*------------------------------------------------------------------------*/ + +namespace CaDiCaL { + +using namespace std; + +/*------------------------------------------------------------------------*/ + +// The CaDiCaL code is split into three layers: +// +// Solver: facade object providing the actual API of the solver +// External: communication layer between 'Solver' and 'Internal' +// Internal: the actual solver code +// +// Note, that 'Solver' is defined in 'cadical.hpp' and 'solver.cpp', while +// 'External' and 'Internal' in '{external,internal}.{hpp,cpp}'. +// +// Also note, that any user should access the library only through the +// 'Solver' API. For the library internal 'Parser' code we make an +// exception and allow access to both 'External' and 'Internal'. The former +// to enforce the same external to internal mapping of variables and the +// latter for profiling and messages. The same applies to 'App'. +// +// The 'External' class provided here stores the information needed to map +// external variable indices to internal variables (actually literals). +// This is helpful for shrinking the working size of the internal solver +// after many variables become inactive. It will also help to provide +// support for extended resolution in the future, since it allows to +// introduce only internally visible variables (even though we do not know +// how to support generating incremental proofs in this situation yet). +// +// External literals are usually called 'elit' and internal 'ilit'. + +/*------------------------------------------------------------------------*/ + +struct Clause; +struct Internal; +struct CubesWithStatus; + +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ + +struct External { + + /*==== start of state ==================================================*/ + + Internal *internal; // The actual internal solver. + + int max_var; // External maximum variable index. + size_t vsize; // Allocated external size. + + vector vals; // Current external (extended) assignment. + vector e2i; // External 'idx' to internal 'lit'. + + vector assumptions; // External assumptions. + vector constraint; // External constraint. Terminated by zero. + + vector + ext_units; // External units. Needed to compute LRAT for eclause + vector ext_flags; // to avoid duplicate units + vector eclause; // External version of original input clause. + // The extension stack for reconstructing complete satisfying assignments + // (models) of the original external formula is kept in this external + // solver object. It keeps track of blocked clauses and clauses containing + // eliminated variable. These irredundant clauses are stored in terms of + // external literals on the 'extension' stack after mapping the + // internal literals given as arguments with 'externalize'. + + bool extended; // Have been extended. + bool concluded; + vector extension; // Solution reconstruction extension stack. + + vector witness; // Literal witness on extension stack. + vector tainted; // Literal tainted in adding literals. + + vector ervars; // Variables added through Extended Resolution. + + vector frozentab; // Reference counts for frozen variables. + + // Regularly checked terminator if non-zero. The terminator is set from + // 'Solver::set (Terminator *)' and checked by 'Internal::terminating ()'. + + Terminator *terminator; + + // If there is a learner export learned clauses. + + Learner *learner; + + void export_learned_empty_clause (); + void export_learned_unit_clause (int ilit); + void export_learned_large_clause (const vector &); + + // If there is a listener for fixed assignments. + + FixedAssignmentListener *fixed_listener; + + // If there is an external propagator. + + ExternalPropagator *propagator; + + vector is_observed; // Quick flag for each external variable + + // Saved 'forgettable' original clauses coming from the external + // propagator. The value of the map starts with a Boolean flag indicating + // if the clause is still present or got already deleted, and then + // followed by the literals of the clause. + unordered_map> forgettable_original; + + void add_observed_var (int elit); + void remove_observed_var (int elit); + void reset_observed_vars (); + + bool observed (int elit); + bool is_witness (int elit); + bool is_decision (int elit); + + void force_backtrack (size_t new_level); + + //----------------------------------------------------------------------// + + signed char *solution; // Given solution checking for debugging. + int solution_size; // Given solution checking for debugging. + vector original; // Saved original formula for checking. + + // If 'opts.checkfrozen' is set make sure that only literals are added + // which were never completely molten before. These molten literals are + // marked at the beginning of the 'solve' call. Note that variables + // larger than 'max_var' are not molten and can thus always be used in the + // future. Only needed to check and debug old style freeze semantics. + // + vector moltentab; + + //----------------------------------------------------------------------// + + const Range vars; // Provides safe variable iterations. + + /*==== end of state ====================================================*/ + + // These two just factor out common sanity (assertion) checking code. + + inline int vidx (int elit) const { + assert (elit); + assert (elit != INT_MIN); + int res = abs (elit); + assert (res <= max_var); + return res; + } + + inline int vlit (int elit) const { + assert (elit); + assert (elit != INT_MIN); + assert (abs (elit) <= max_var); + return elit; + } + + inline bool is_valid_input (int elit) { + assert (elit); + assert (elit != INT_MIN); + int eidx = abs (elit); + return eidx > max_var || !ervars[eidx]; + } + + /*----------------------------------------------------------------------*/ + + // The following five functions push individual literals or clauses on the + // extension stack. They all take internal literals as argument, and map + // them back to external literals first, before pushing them on the stack. + + void push_zero_on_extension_stack (); + + // Our general version of extension stacks always pushes a set of witness + // literals (for variable elimination the literal of the eliminated + // literal and for blocked clauses the blocking literal) followed by all + // the clause literals starting with and separated by zero. + // + void push_clause_literal_on_extension_stack (int ilit); + void push_witness_literal_on_extension_stack (int ilit); + + void push_clause_on_extension_stack (Clause *); + void push_clause_on_extension_stack (Clause *, int witness); + void push_binary_clause_on_extension_stack (int64_t id, int witness, + int other); + + // The main 'extend' function which extends an internal assignment to an + // external assignment using the extension stack (and sets 'extended'). + // + void extend (); + void conclude_sat (); + + /*----------------------------------------------------------------------*/ + + // Marking external literals. + + unsigned elit2ulit (int elit) const { + assert (elit); + assert (elit != INT_MIN); + const int idx = abs (elit) - 1; + assert (idx <= max_var); + return 2u * idx + (elit < 0); + } + + bool marked (const vector &map, int elit) const { + const unsigned ulit = elit2ulit (elit); + return ulit < map.size () ? map[ulit] : false; + } + + void mark (vector &map, int elit) { + const unsigned ulit = elit2ulit (elit); + if (ulit >= map.size ()) + map.resize (ulit + 1, false); + map[ulit] = true; + } + + void unmark (vector &map, int elit) { + const unsigned ulit = elit2ulit (elit); + if (ulit < map.size ()) + map[ulit] = false; + } + + /*----------------------------------------------------------------------*/ + + void push_external_clause_and_witness_on_extension_stack ( + const vector &clause, const vector &witness, int64_t id); + + void push_id_on_extension_stack (int64_t id); + + // Restore a clause, which was pushed on the extension stack. + void restore_clause (const vector::const_iterator &begin, + const vector::const_iterator &end, + const int64_t id); + + void restore_clauses (); + + /*----------------------------------------------------------------------*/ + + // Explicitly freeze and melt literals (instead of just freezing + // internally and implicitly assumed literals). Passes on freezing and + // melting to the internal solver, which has separate frozen counters. + + void freeze (int elit); + void melt (int elit); + + bool frozen (int elit) { + assert (elit); + assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) + return false; + if (eidx >= (int) frozentab.size ()) + return false; + return frozentab[eidx] > 0; + } + + /*----------------------------------------------------------------------*/ + + External (Internal *); + ~External (); + + void enlarge (int new_max_var); // Enlarge allocated 'vsize'. + void init (int new_max_var, + bool extension = false); // Initialize up-to 'new_max_var'. + + int internalize ( + int, + bool extension = false); // Translate external to internal literal. + + /*----------------------------------------------------------------------*/ + + // According to the CaDiCaL API contract (as well as IPASIR) we have to + // forget about the previous assumptions after a 'solve' call. This + // should however be delayed until we transition out of an 'UNSATISFIED' + // state, i.e., after no more 'failed' calls are expected. Note that + // 'failed' requires to know the failing assumptions, and the 'failed' + // status of those should cleared before at start of the next 'solve'. + // As a consequence 'reset_assumptions' is only called from + // 'transition_to_unknown_state' in API calls in 'solver.cpp'. + + void reset_assumptions (); + + // Similarly to 'failed', 'conclude' needs to know about failing + // assumptions and therefore needs to be reset when leaving the + // 'UNSATISFIED' state. + // + void reset_concluded (); + + // Similarly a valid external assignment obtained through 'extend' has to + // be reset at each point it risks to become invalid. This is done + // in the external layer in 'external.cpp' functions.. + + void reset_extended (); + + // Finally, the semantics of incremental solving also require that limits + // are only valid for the next 'solve' call. Since the limits can not + // really be queried, handling them is less complex and they are just + // reset immediately at the end of 'External::solve'. + + void reset_limits (); + + /*----------------------------------------------------------------------*/ + + // Proxies to IPASIR functions. + + void add (int elit); + void assume (int elit); + int solve (bool preprocess_only); + + // We call it 'ival' as abbreviation for 'val' with 'int' return type to + // avoid bugs due to using 'signed char tmp = val (lit)', which might turn + // a negative value into a positive one (happened in 'extend'). + // + inline int ival (int elit) const { + assert (elit != INT_MIN); + int eidx = abs (elit); + bool val = false; + if (eidx <= max_var && (size_t) eidx < vals.size ()) + val = vals[eidx]; + if (elit < 0) + val = !val; + return val ? elit : -elit; + } + + bool flip (int elit); + bool flippable (int elit); + + bool failed (int elit); + + void terminate (); + + // Other important non IPASIR functions. + + /*----------------------------------------------------------------------*/ + + // Add literal to external constraint. + // + void constrain (int elit); + + // Returns true if 'solve' returned 20 because of the constraint. + // + bool failed_constraint (); + + // Deletes the current constraint clause. Called on + // 'transition_to_unknown_state' and if a new constraint is added. Can be + // called directly using the API. + // + void reset_constraint (); + + /*----------------------------------------------------------------------*/ + + int propagate_assumptions (); + void implied (std::vector &entrailed); + void conclude_unknown (); + + /*----------------------------------------------------------------------*/ + int lookahead (); + CaDiCaL::CubesWithStatus generate_cubes (int, int); + + int fixed (int elit) const; // Implemented in 'internal.hpp'. + + /*----------------------------------------------------------------------*/ + + void phase (int elit); + void unphase (int elit); + + /*----------------------------------------------------------------------*/ + + // Traversal functions for the witness stack and units. The explanation + // in 'external.cpp' for why we have to distinguish these cases. + + bool traverse_all_frozen_units_as_clauses (ClauseIterator &); + bool traverse_all_non_frozen_units_as_witnesses (WitnessIterator &); + bool traverse_witnesses_backward (WitnessIterator &); + bool traverse_witnesses_forward (WitnessIterator &); + + /*----------------------------------------------------------------------*/ + + // Copy flags for determining preprocessing state. + + void copy_flags (External &other) const; + + /*----------------------------------------------------------------------*/ + + // Check solver behaves as expected during testing and debugging. + + void check_assumptions_satisfied (); + void check_constraint_satisfied (); + void check_failing (); + + void check_solution_on_learned_clause (); + void check_solution_on_shrunken_clause (Clause *); + void check_solution_on_learned_unit_clause (int unit); + void check_no_solution_after_learning_empty_clause (); + + void check_learned_empty_clause () { + if (solution) + check_no_solution_after_learning_empty_clause (); + } + + void check_learned_unit_clause (int unit) { + if (solution) + check_solution_on_learned_unit_clause (unit); + } + + void check_learned_clause () { + if (solution) + check_solution_on_learned_clause (); + } + + void check_shrunken_clause (Clause *c) { + if (solution) + check_solution_on_shrunken_clause (c); + } + + void check_assignment (int (External::*assignment) (int) const); + + void check_satisfiable (); + void check_unsatisfiable (); + + void check_solve_result (int res); + + void update_molten_literals (); + + /*----------------------------------------------------------------------*/ + + // For debugging and testing only. See 'solution.hpp' for more details. + // TODO: if elit > solution_size, elit is an extension variable. For now + // the clause will count as satisfied regardless. For the future one + // should check that actually there is one consistent extension for the + // solution that satisfies the clauses with this extension variable (by + // setting it to a value once a clause is learned which is not satisfied + // already). + // + inline int sol (int elit) const { + assert (solution); + assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) + return 0; + else if (eidx > solution_size) + return elit; + signed char value = solution[eidx]; + if (!value) + return 0; + if (elit < 0) + value = -value; + return value > 0 ? elit : -elit; + } +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/external_propagate.cpp b/src/sat/cadical/external_propagate.cpp new file mode 100644 index 000000000..5b1605a7f --- /dev/null +++ b/src/sat/cadical/external_propagate.cpp @@ -0,0 +1,1226 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*----------------------------------------------------------------------------*/ +// +// Mark a variable as an observed one. It can be a new variable. It is +// assumed to be clean (not eliminated by previous simplifications). +// +void Internal::add_observed_var (int ilit) { + int idx = vidx (ilit); + if (idx >= (int64_t) relevanttab.size ()) + relevanttab.resize (1 + (size_t) idx, 0); + unsigned &ref = relevanttab[idx]; + if (ref < UINT_MAX) { + ref++; + LOG ("variable %d is observed %u times", idx, ref); + } else + LOG ("variable %d remains observed forever", idx); + // TODO: instead of actually backtracking, it would be enough to notify + // backtrack and re-play again every levels' notification to the + // propagator + if (val (ilit) && level && !fixed (ilit)) { + // The variable is already assigned, but we can not send a notification + // about it because it happened on an earlier decision level. + // To not break the stack-like view of the trail, we simply backtrack to + // undo this unnotifiable assignment. + const int assignment_level = var (ilit).level; + backtrack (assignment_level - 1); + } else if (level && fixed (ilit)) { + backtrack (0); + } +} + +/*----------------------------------------------------------------------------*/ +// +// Removing an observed variable should happen only once it is ensured +// that there is no unexplained propagation in the implication +// graph involving this variable. +// +void Internal::remove_observed_var (int ilit) { + if (!fixed (ilit) && level) { + backtrack (); + } + + assert (fixed (ilit) || !level); + + const int idx = vidx (ilit); + assert ((size_t) idx < relevanttab.size ()); + unsigned &ref = relevanttab[idx]; + assert (fixed (ilit) || ref > 0); + if (fixed (ilit)) + ref = 0; + else if (ref < UINT_MAX) { + if (!--ref) { + LOG ("variable %d is not observed anymore", idx); + } else + LOG ("variable %d is unobserved once but remains observed %u times", + ilit, ref); + } else + LOG ("variable %d remains observed forever", idx); +} + +/*----------------------------------------------------------------------------*/ +// +// Supposed to be used only by mobical. +// +bool Internal::observed (int ilit) const { + assert ((size_t) vidx (ilit) < relevanttab.size ()); + return relevanttab[vidx (ilit)] > 0; +} + +/*----------------------------------------------------------------------------*/ +// +// Check for unexplained propagations upon disconnecting external propagator +// +void Internal::set_tainted_literal () { + if (!opts.ilb) { + return; + } + for (auto idx : vars) { + if (!val (idx)) + continue; + if (var (idx).reason != external_reason) + continue; + if (!tainted_literal) { + tainted_literal = idx; + continue; + } + assert (val (tainted_literal)); + if (var (idx).level < var (tainted_literal).level) { + tainted_literal = idx; + } + } +} + +void Internal::renotify_trail_after_ilb () { + if (!external_prop || external_prop_is_lazy || !trail.size () || + !opts.ilb) { + return; + } + LOG ("notify external propagator about new assignments (after ilb)"); +#ifndef NDEBUG + LOG ("(decision level: %d, trail size: %zd, notified %zd)", level, + trail.size (), notified); +#endif + renotify_full_trail (); +} + +void Internal::renotify_trail_after_local_search () { + if (!external_prop || external_prop_is_lazy || !trail.size ()) { + return; + } + LOG ("notify external propagator about new assignments (after local " + "search)"); +#ifndef NDEBUG + LOG ("(decision level: %d, trail size: %zd, notified %zd)", level, + trail.size (), notified); +#endif + renotify_full_trail (); +} + +// It repeats ALL assignments of the trail, so the already notified +// root-level assignments will be notified multiple times. + +void Internal::renotify_full_trail () { + const size_t end_of_trail = trail.size (); + if (level) { + notified = 0; // TODO: save the last notified root-level position + // somewhere and use it here + notify_backtrack (0); + } + std::vector assigned; + + int prev_max_level = 0; + int current_level = 0; + int propagator_level = 0; + + while (notified < end_of_trail) { + int ilit = trail[notified++]; + // In theory, 0 ilit can happen due to pseudo-decision levels + if (!ilit) + current_level = prev_max_level + 1; + else + current_level = var (ilit).level; + + if (current_level > propagator_level) { + if (assigned.size ()) + external->propagator->notify_assignment (assigned); + while (current_level > propagator_level) { + external->propagator->notify_new_decision_level (); + propagator_level++; + } + assigned.clear (); + } + // Current level can be smaller than prev_max_level due to chrono + if (current_level > prev_max_level) + prev_max_level = current_level; + + if (!observed (ilit)) + continue; + + int elit = externalize (ilit); // TODO: double-check tainting + 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. + assert (external->observed (elit) || fixed (ilit)); + if (!external->ervars[abs (elit)]) + assigned.push_back (elit); + } + if (assigned.size ()) + external->propagator->notify_assignment (assigned); + assigned.clear (); + + // In case there are some left over empty levels on the top of the trail, + // the external propagtor must be notified about them so the levels are + // synced + while (level > propagator_level) { + external->propagator->notify_new_decision_level (); + propagator_level++; + } + + return; +} + +/*----------------------------------------------------------------------------*/ +// +// Check if the variable is assigned by decision. +// +bool Internal::is_decision (int ilit) { + if (!level || fixed (ilit) || !val (ilit)) + return false; + + const int idx = vidx (ilit); + Var &v = var (idx); +#ifndef NDEBUG + LOG (v.reason, + "is_decision: i%d (current level: %d, is_fixed: %d, v.level: %d, " + "is_external_reason: %d, v.reason: )", + ilit, level, fixed (ilit), v.level, v.reason == external_reason); +#endif + if (!v.level || v.reason) + return false; + assert (!v.reason); + return true; +} + +void Internal::force_backtrack (size_t new_level) { + if (!forced_backt_allowed || level <= 0 || new_level >= (size_t) level) + return; + +#ifndef NDEBUG + LOG ("external propagator forces backtrack to decision level" + "%zd (from level %d)", + new_level, level); +#endif + backtrack (new_level); +} + +/*----------------------------------------------------------------------------*/ +// +// Call external propagator to check if there is a literal to be propagated. +// The reason of the propagation is not necessarily asked at that point. +// +// In case the externally propagated literal is already falsified, the +// reason is asked and conflict analysis starts. In case the externally +// propagated literal is already satisfied, nothing happens. +// +bool Internal::external_propagate () { + if (level) + require_mode (SEARCH); + assert (!unsat); + + size_t before = num_assigned; + bool cb_repropagate_needed = true; + while (cb_repropagate_needed && !conflict && external_prop && + !external_prop_is_lazy && !private_steps) { +#ifndef NDEBUG + LOG ("external propagation starts (decision level: %d, trail size: " + "%zd, notified %zd)", + level, trail.size (), notified); +#endif + cb_repropagate_needed = false; + // external->reset_extended (); //TODO for inprocessing + + notify_assignments (); + + int elit = external->propagator->cb_propagate (); + stats.ext_prop.ext_cb++; + stats.ext_prop.eprop_call++; + while (elit) { + assert (external->is_observed[abs (elit)]); + int ilit = external->e2i[abs (elit)]; + if (elit < 0) + ilit = -ilit; + int tmp = val (ilit); +#ifndef NDEBUG + assert (fixed (ilit) || observed (ilit)); + LOG ("External propagation of e%d (i%d val: %d)", elit, ilit, tmp); +#endif + if (!tmp) { + // variable is not assigned, it can be propagated + if (!level) { + Clause *res = learn_external_reason_clause (ilit, elit); +#ifndef LOGGING + LOG (res, "reason clause of external propagation of %d:", elit); +#endif + (void) res; + } else + search_assign_external (ilit); + stats.ext_prop.eprop_prop++; + + if (unsat || conflict) + break; + propagate (); + if (unsat || conflict) + break; + notify_assignments (); + } else if (tmp < 0) { + LOG ("External propgation of %d is falsified under current trail", + ilit); + stats.ext_prop.eprop_conf++; + int level_before = level; + size_t assigned = num_assigned; + Clause *res = learn_external_reason_clause (ilit, elit); +#ifndef LOGGING + LOG (res, "reason clause of external propagation of %d:", elit); +#endif + (void) res; + bool trail_changed = + (num_assigned != assigned || level != level_before || + propagated < trail.size ()); + + if (unsat || conflict) + break; + + if (trail_changed) { + propagate (); + if (unsat || conflict) + break; + notify_assignments (); + } + } // else (tmp > 0) -> the case of a satisfied literal is ignored + elit = external->propagator->cb_propagate (); + stats.ext_prop.ext_cb++; + stats.ext_prop.eprop_call++; + } + +#ifndef NDEBUG + LOG ("External propagation ends (decision level: %d, trail size: %zd, " + "notified %zd)", + level, trail.size (), notified); +#endif + if (!unsat && !conflict) { + int level_before = level; + size_t assigned = num_assigned; + bool has_external_clause = ask_external_clause (); + // New observed variable might have triggered a backtrack during this + // ask_external_clause call, so we need to propagate before continuing + stats.ext_prop.ext_cb++; + stats.ext_prop.elearn_call++; + + bool trail_changed = + (num_assigned != assigned || level != level_before || + propagated < trail.size ()); + if (trail_changed) { + propagate (); // unsat or conflict will be caught later + if (!unsat || !conflict) + notify_assignments (); + } + +#ifndef NDEBUG + if (has_external_clause) + LOG ("New external clauses are to be added."); + else + LOG ("No external clauses to be added."); +#endif + + while (has_external_clause) { + level_before = level; + assigned = num_assigned; + + add_external_clause (0); + trail_changed = + (num_assigned != assigned || level != level_before || + propagated < trail.size ()); + cb_repropagate_needed = true; + + if (unsat || conflict) { + cb_repropagate_needed = false; + break; + } + + if (trail_changed) { + propagate (); + if (unsat || conflict) { + cb_repropagate_needed = false; + break; + } + + notify_assignments (); + } + has_external_clause = ask_external_clause (); + stats.ext_prop.ext_cb++; + stats.ext_prop.elearn_call++; + } + } +#ifndef NDEBUG + LOG ("External clause addition ends on decision level %d at trail " + "size " + "%zd (notified %zd)", + level, trail.size (), notified); +#endif + } + if (before < num_assigned) + did_external_prop = true; + return !conflict; +} + +/*----------------------------------------------------------------------------*/ +// +// Helper function, calls 'cb_has_external_clause', while maintains the +// related redundancy type of the clause. +// + +bool Internal::ask_external_clause () { + ext_clause_forgettable = false; + bool res = + external->propagator->cb_has_external_clause (ext_clause_forgettable); + + return res; +} +/*----------------------------------------------------------------------------*/ +// +// Literals of the externally learned clause must be reordered based on the +// assignment levels of the literals. +// +void Internal::move_literals_to_watch () { + if (clause.size () < 2) + return; + if (!level) + return; + + for (int i = 0; i < 2; i++) { + int highest_position = i; + int highest_literal = clause[i]; + + int highest_level = var (highest_literal).level; + int highest_value = val (highest_literal); + + for (size_t j = i + 1; j < clause.size (); j++) { + const int other = clause[j]; + const int other_level = var (other).level; + const int other_value = val (other); + + if (other_value < 0) { + if (highest_value >= 0) + continue; + if (other_level <= highest_level) + continue; + } else if (other_value > 0) { + if (highest_value > 0 && other_level >= highest_level) + continue; + } else { + if (highest_value >= 0) + continue; + } + + highest_position = j; + highest_literal = other; + highest_level = other_level; + highest_value = other_value; + } +#ifndef NDEBUG + LOG ("highest position: %d highest level: %d highest value: %d", + highest_position, highest_level, highest_value); +#endif + + if (highest_position == i) + continue; + if (highest_position > i) { + std::swap (clause[i], clause[highest_position]); + } + } +} + +/*----------------------------------------------------------------------------*/ +// +// Reads out from the external propagator the lemma/proapgation reason +// clause literal by literal. In case propagated_elit is 0, it is about an +// external clause via 'cb_add_external_clause_lit'. Otherwise, it is about +// learning the reason of 'propagated_elit' via 'cb_add_reason_clause_lit'. +// The learned clause is simplified by the current root-level assignment +// (i.e. root-level falsified literals are removed, root satisfied clauses +// are skipped). Duplicate literals are removed, tauotologies are detected +// and skipped. It always adds the original (un-simplified) external clause +// to the proof as an input clause and +// the simplified version of it (except exceptions below) as a derived +// clause. +// +// In case the external clause, after simplifications, is satisfied, no +// clause is constructed, and the function returns 0. In case the external +// clause, after simplifications, is empty, no clause is constructed, unsat +// is set true and the function returns 0. In case the external clause, +// after simplifications, is unit, no clause is constructed, +// 'Internal::clause' has the unit literal (without 0) and the function +// returns 0. +// +// In every other cases a new clause is constructed and the pointer is in +// newest_clause +// +void Internal::add_external_clause (int propagated_elit, + bool no_backtrack) { + assert (original.empty ()); + int elit = 0; + + if (propagated_elit) { + // Propagation reason clauses are by default assumed to be forgettable + // irredundant. In case they would be unforgettably important, the + // propagator can add them as an explicit unforgettable external clause + // or set 'are_reasons_forgettable' to false. + ext_clause_forgettable = external->propagator->are_reasons_forgettable; +#ifndef NDEBUG + LOG ("add external reason of propagated lit: %d", propagated_elit); +#endif + elit = external->propagator->cb_add_reason_clause_lit (propagated_elit); + } else + elit = external->propagator->cb_add_external_clause_lit (); + + // we need to be build a new LRAT chain if we are already in the middle of + // the analysis (like during failed assumptions) + LOG (lrat_chain, "lrat chain before"); + std::vector lrat_chain_ext = std::move (lrat_chain); + lrat_chain.clear (); + clause.clear (); + + // Read out the external lemma into original and simplify it into clause + assert (clause.empty ()); + assert (original.empty ()); + + assert (!force_no_backtrack); + assert (!from_propagator); + force_no_backtrack = no_backtrack; + from_propagator = true; + while (elit) { + assert (external->is_observed[abs (elit)]); + external->add (elit); + if (propagated_elit) + elit = + external->propagator->cb_add_reason_clause_lit (propagated_elit); + else + elit = external->propagator->cb_add_external_clause_lit (); + } + external->add (elit); + assert (original.empty ()); + assert (clause.empty ()); + force_no_backtrack = false; + from_propagator = false; + lrat_chain = std::move (lrat_chain_ext); + LOG (lrat_chain, "lrat chain after"); +} + +/*----------------------------------------------------------------------------*/ +// +// Recursively calls 'learn_external_reason_clause' to explain every +// backward reachable externally propagated literal starting from 'ilit'. +// +void Internal::explain_reason (int ilit, Clause *reason, int &open) { + if (!opts.exteagerreasons) + return; +#ifndef NDEBUG + LOG (reason, "explain_reason of %d (open: %d)", ilit, open); +#endif + assert (reason); + assert (reason != external_reason); + for (const auto &other : *reason) { + if (other == ilit) + continue; + Flags &f = flags (other); + if (f.seen) + continue; + Var &v = var (other); + if (!v.level) + continue; + assert (val (other) < 0); + assert (v.level <= level); + if (v.reason == external_reason) { + v.reason = learn_external_reason_clause (-other, 0, true); + } + if (v.level && v.reason) { + f.seen = true; + open++; + } + } +} + +/*----------------------------------------------------------------------------*/ +// +// In case external propagation was used, the reason clauses of the relevant +// propagations must be learned lazily before/during conflict analysis. +// While conflict analysis needs to analyze only the current level, lazy +// clause learning must check every clause on every level that is backward +// reachable from the conflicting clause to guarantee that the assignment +// levels of the variables are accurate. So this explanation round is +// separated from the conflict analysis, thereby guranteeing that the flags +// and datastructures can be properly used later. +// +// This function must be called before the conflict analysis, in order to +// guarantee that every relevant reason clause is indeed learned already and +// to be sure that the levels of assignments are set correctly. +// +// Later TODO: experiment with bounded explanation (explain only those +// literals that are directly used during conflict analysis + +// minimizing/shrinking). The assignment levels are then only +// over-approximated. +// +void Internal::explain_external_propagations () { + assert (conflict); + assert (clause.empty ()); + + Clause *reason = conflict; + std::vector seen_lits; + int open = 0; // Seen but not explained literal + + explain_reason (0, reason, open); // marks conflict clause lits as seen + int i = trail.size (); // Start at end-of-trail + while (i > 0) { + const int lit = trail[--i]; + if (!flags (lit).seen) + continue; + seen_lits.push_back (lit); + Var &v = var (lit); + if (!v.level) + continue; + if (v.reason) { + open--; + explain_reason (lit, v.reason, open); + } + if (!open) + break; + } + assert (!open); + + if (!opts.exteagerrecalc) { + for (auto lit : seen_lits) { + Flags &f = flags (lit); + f.seen = false; + } +#ifndef NDEBUG + for (auto idx : vars) { + assert (!flags (idx).seen); + } +#endif + } + + // Traverse now in the opposite direction (from lower to higher levels) + // and calculate the actual assignment level for the seen assignments. + for (auto it = seen_lits.rbegin (); it != seen_lits.rend (); ++it) { + const int lit = *it; + Flags &f = flags (lit); + Var &v = var (lit); + if (v.reason) { + int real_level = 0; + for (const auto &other : *v.reason) { + if (other == lit) + continue; + int tmp = var (other).level; + if (tmp > real_level) + real_level = tmp; + } + if (v.level && !real_level) { + build_chain_for_units (lit, v.reason, 1); + learn_unit_clause (lit); + lrat_chain.clear (); + v.reason = 0; + } + assert (v.level >= real_level); + if (v.level > real_level) { + v.level = real_level; + } + } + f.seen = false; + } + +#if 0 // has been fuzzed extensively + for (auto idx : vars) { + assert (!flags (idx).seen); + } +#endif +} + +/*----------------------------------------------------------------------------*/ +// +// Learns the reason clause of the propagation of ilit from the +// external propagator via 'add_external_clause'. +// In case of falsified propagation steps, if the propagated literal is +// already fixed to the opposite value, externalize will not necessarily +// give back the original elit (but an equivalent one). To avoid that, in +// falsified propagation cases the propagated elit is added as a second +// argument. +// +Clause *Internal::learn_external_reason_clause (int ilit, + int falsified_elit, + bool no_backtrack) { + assert (external->propagator); + // we cannot modify clause during analysis + auto clause_tmp = std::move (clause); + + assert (clause.empty ()); + assert (original.empty ()); + + stats.ext_prop.eprop_expl++; + + int elit = 0; + if (!falsified_elit) { + assert (!fixed (ilit)); + elit = externalize (ilit); + } else + elit = falsified_elit; + + LOG ("ilit: %d, elit: %d", ilit, elit); + add_external_clause (elit, no_backtrack); + +#ifndef NDEBUG + if (!falsified_elit && newest_clause) { + // Check if external propagation is correct wrt to the topological order + // defined by the trail. In case it is a falsified external propagation + // step, the order does not matter, the reason simply supposed to be a + // falsified clause. + const int propagated_ilit = ilit; + for (auto const reason_ilit : *newest_clause) { + assert (var (reason_ilit).trail <= var (propagated_ilit).trail); + } + } +#endif + + clause = std::move (clause_tmp); + return newest_clause; +} + +/*----------------------------------------------------------------------------*/ +// +// Helper function to be able to call learn_external_reason_clause when the +// internal clause is already used in the caller side (for example during +// proof checking). These calls are assumed to be without a falsified elit. +// Dont use it in general instead of learn_external_reason_clause because it +// does not support the corner cases where a literal remains in clause. +// +Clause *Internal::wrapped_learn_external_reason_clause (int ilit) { + Clause *res; + std::vector chain_tmp{std::move (lrat_chain)}; + lrat_chain.clear (); + if (clause.empty ()) { + res = learn_external_reason_clause (ilit, 0, true); + } else { + std::vector clause_tmp{std::move (clause)}; + clause.clear (); + res = learn_external_reason_clause (ilit, 0, true); + // The learn_external_reason clause can leave a literal in clause when + // there is a falsified elit arg. Here it is not allowed to + // happen. + assert (clause.empty ()); + + clause = std::move (clause_tmp); + clause_tmp.clear (); + } + assert (lrat_chain.empty ()); + lrat_chain = std::move (chain_tmp); + chain_tmp.clear (); + return res; +} + +/*----------------------------------------------------------------------------*/ +// +// Checks if the new clause forces backtracking, new assignments or conflict +// analysis +// +void Internal::handle_external_clause (Clause *res) { + if (from_propagator) + stats.ext_prop.elearned++; + // at level 0 we have to do nothing... + if (!level) + return; + if (!res) { + if (from_propagator) + stats.ext_prop.elearn_prop++; + // new unit clause. For now just backtrack. + assert (!force_no_backtrack); + assert (level); + // if (!opts.chrono) { + backtrack (); + // } + return; + } + if (from_propagator) + stats.ext_prop.elearned++; + assert (res->size >= 2); + const int pos0 = res->literals[0]; + const int pos1 = res->literals[1]; + if (force_no_backtrack) { + assert (val (pos1) < 0); + assert (val (pos0) >= 0); + return; + // TODO: maybe fix levels + } + const int l1 = var (pos1).level; + if (val (pos0) < 0) { // conflicting or propagating clause + assert (0 < l1 && l1 <= var (pos0).level); + if (!opts.chrono) { + backtrack (l1); + } + if (val (pos0) < 0) { + conflict = res; + if (!from_propagator) { + // its better to backtrack instead of analyze + backtrack (l1 - 1); + conflict = 0; + assert (!val (pos0) && !val (pos1)); + } + } else { + search_assign_driving (pos0, res); + } + if (from_propagator) + stats.ext_prop.elearn_conf++; + return; + } + if (val (pos1) < 0 && !val (pos0)) { // propagating clause + if (!opts.chrono) { + backtrack (l1); + } + search_assign_driving (pos0, res); + if (from_propagator) + stats.ext_prop.elearn_conf++; + return; + } +} + +/*----------------------------------------------------------------------------*/ +// +// Asks the external propagator if the current solution is OK +// by calling 'cb_check_found_model (model)'. +// +// The checked model is built up after everything is restored +// from the reconstruction stack and every variable is reactivated +// and so it is not just simply the trail (i.e. it can be expensive). +// +// If the external propagator approves the model, the function +// returns true. +// +// If the propagator does not approve the model, the solver asks +// the propagator to add an external clause. +// This external clause, however, does NOT have to be falsified by +// the current model. The possible cases and reactions are described +// below in the function. The possible states after that function: +// - A solution was found and accepted by the external propagator +// - A conflicting clause was learned from the external propagator +// - The empty clause was learned due to something new learned from +// the external propagator. +// +// In case only new variables were introduced, but no new clauses were +// added, the function will return without a conflict to the outer CDCL +// loop, where the new (not yet satisfied) variables are recognized and +// the search continues. +bool Internal::external_check_solution () { + if (!external_prop) + return true; + + bool trail_changed = true; + bool added_new_clauses = false; + while (trail_changed || added_new_clauses) { + notify_assignments (); + if (!satisfied ()) + break; + trail_changed = false; // to be on the safe side + added_new_clauses = false; + LOG ("Final check by external propagator is invoked."); + stats.ext_prop.echeck_call++; + external->reset_extended (); + external->extend (); + + std::vector etrail; + + // Here the variables must be filtered by external->is_observed, + // because fixed variables are internally not necessarily observed + // anymore. + for (int idx = 1; idx <= external->max_var; idx++) { + if (!external->is_observed[idx]) + continue; + const int lit = external->ival (idx); + etrail.push_back (lit); +#ifndef NDEBUG +#ifdef LOGGING + bool p = external->vals[idx]; + LOG ("evals[%d]: %d ival(%d): %d", idx, p, idx, lit); +#endif +#endif + } + + bool is_consistent = + external->propagator->cb_check_found_model (etrail); + stats.ext_prop.ext_cb++; + if (is_consistent) { + LOG ("Found solution is approved by external propagator."); + return true; + } + + bool has_external_clause = ask_external_clause (); + + stats.ext_prop.ext_cb++; + stats.ext_prop.elearn_call++; + + if (has_external_clause) + LOG ( + "Found solution triggered new clauses from external propagator."); + + while (has_external_clause) { + int level_before = level; + size_t assigned = num_assigned; + add_external_clause (0); + bool trail_changed = + (num_assigned != assigned || level != level_before || + propagated < trail.size ()); + added_new_clauses = true; + // + // There are many possible scenarios here: + // - Learned conflicting clause: return to CDCL loop (conflict true) + // - Learned conflicting unit clause that after backtrack+BCP leads to + // a new complete solution: force the outer loop to check the new + // model (trail_changed is true, but (conflict & unsat) is false) + // - Learned empty clause: return to CDCL loop (unsat true) + // - Learned a non-conflicting unit clause: + // Though it does not invalidate the current solution, the solver + // will backtrack to the root level and will repropagate it. The + // search will start again (saved phases hopefully make it quick), + // but it is needed in order to guarantee that every fixed variable + // is properly handled+notified (important for incremental use + // cases). + // - Otherwise: the solution is considered approved and the CDCL-loop + // can return with res = 10. + // + if (unsat || conflict || trail_changed) + break; + has_external_clause = ask_external_clause (); + stats.ext_prop.ext_cb++; + stats.ext_prop.elearn_call++; + } + LOG ("No more external clause to add."); + if (unsat || conflict) + break; + } + + if (!unsat && conflict) { + const int conflict_level = var (conflict->literals[0]).level; + if (conflict_level != level) { + backtrack (conflict_level); + } + } + + return !conflict; +} + +/*----------------------------------------------------------------------------*/ +// +// Notify the external propagator that an observed variable got assigned. +// +void Internal::notify_assignments () { + if (!external_prop || external_prop_is_lazy || private_steps) + return; + + const size_t end_of_trail = trail.size (); + + if (notified >= end_of_trail) + return; + + LOG ("notify external propagator about new assignments"); + std::vector assigned; + + while (notified < end_of_trail) { + int ilit = trail[notified++]; + if (!observed (ilit)) + continue; + + int elit = externalize (ilit); // TODO: double-check tainting + assert (elit); + if (external->ervars[abs (elit)]) + continue; + // Fixed variables might get mapped (during compact) to another + // non-observed but fixed variable. + // This happens on root level, so notification about their assignment is + // already done. + assert (external->observed (elit) || + (fixed (ilit) && !external->ervars[abs (elit)])); + assigned.push_back (elit); + } + + external->propagator->notify_assignment (assigned); + return; +} + +/*----------------------------------------------------------------------------*/ + +void Internal::connect_propagator () { + if (level) + backtrack (); +} + +/*----------------------------------------------------------------------------*/ +// +// Notify the external propagator that a new decision level is started. +// +void Internal::notify_decision () { + if (!external_prop || external_prop_is_lazy || private_steps) + return; + external->propagator->notify_new_decision_level (); +} + +/*----------------------------------------------------------------------------*/ +// +// Notify the external propagator that backtrack to new_level. +// +void Internal::notify_backtrack (size_t new_level) { + if (!external_prop || external_prop_is_lazy || private_steps) + return; + external->propagator->notify_backtrack (new_level); +} + +/*----------------------------------------------------------------------------*/ +// +// Ask the external propagator if there is a suggested literal as next +// decision. +// +int Internal::ask_decision () { + if (!external_prop || external_prop_is_lazy || private_steps) + return 0; + + assert (!unsat); + assert (!conflict); + notify_assignments (); + int level_before = level; + forced_backt_allowed = true; + int elit = external->propagator->cb_decide (); + forced_backt_allowed = false; + stats.ext_prop.ext_cb++; + + if (level_before != level) { + + propagate (); + assert (!unsat); + assert (!conflict); + notify_assignments (); + + // In case the external propagator forced to backtrack below the + // pseduo decision levels, we must go back to the CDCL loop instead of + // making a decision. + if ((size_t) level < assumptions.size () || + ((size_t) level == assumptions.size () && constraint.size ())) { + return 0; + } + } + + if (!elit) + return 0; + LOG ("external propagator proposes decision: %d", elit); + assert (external->is_observed[abs (elit)]); + if (!external->is_observed[abs (elit)]) + return 0; + + int ilit = external->e2i[abs (elit)]; + if (elit < 0) + ilit = -ilit; + + assert (fixed (ilit) || observed (ilit)); + + LOG ("Asking external propagator for decision returned: %d (internal: " + "%d, fixed: %d, val: %d)", + elit, ilit, fixed (ilit), val (ilit)); + + if (fixed (ilit) || val (ilit)) { + LOG ("Proposed decision variable is already assigned, falling back to " + "internal decision."); + return 0; + } + + return ilit; +} + +/*----------------------------------------------------------------------------*/ +// +// Check if the clause is a forgettable clause coming from the external +// propagator. +// +bool Internal::is_external_forgettable (int64_t id) { + assert (opts.check); + return (external->forgettable_original.find (id) != + external->forgettable_original.end ()); +} + +/*----------------------------------------------------------------------------*/ +// +// When an external forgettable clause is deleted, it is marked in the +// 'forgettable_original' hash, so that the internal model checking can +// ignore it. +// +void Internal::mark_garbage_external_forgettable (int64_t id) { + assert (opts.check); + assert (is_external_forgettable (id)); + + LOG (external->forgettable_original[id], + "forgettable external lemma is deleted:"); + // Mark as removed by flipping the first flag to false. + external->forgettable_original[id][0] = 0; +} + +/*----------------------------------------------------------------------------*/ +// +// Check that the literals in the clause are properly ordered. Used only +// internally for debug purposes. +// +void Internal::check_watched_literal_invariants () { +#ifndef NDEBUG + int v0 = 0; + int v1 = 0; + + if (val (clause[0]) > 0) + v0 = 1; + else if (val (clause[0]) < 0) + v0 = -1; + + if (val (clause[1]) > 0) + v1 = 1; + else if (val (clause[1]) < 0) + v1 = -1; + assert (v0 >= v1); +#endif + if (val (clause[0]) > 0) { + if (val (clause[1]) > 0) { // Case 1: Both literals are satisfied + // They are ordered by lower to higher decision level + assert (var (clause[0]).level <= var (clause[1]).level); + + // Every other literal of the clause is either + // - satisfied at higher level + // - unassigned + // - falsified + for (size_t i = 2; i < clause.size (); i++) + assert (val (clause[i]) <= 0 || + (var (clause[1]).level <= var (clause[i]).level)); + + } else if (val (clause[1]) == + 0) { // Case 2: First satisfied, next unassigned + + // Every other literal of the clause is either + // - unassigned + // - falsified + for (size_t i = 2; i < clause.size (); i++) + assert (val (clause[i]) <= 0); + + } else { // Case 3: First satisfied, next falsified -> could have been a + // reason of a previous propagation + // Every other literal of the clause is falsified but at a lower + // decision level + for (size_t i = 2; i < clause.size (); i++) + assert (val (clause[i]) < 0 && + (var (clause[1]).level >= var (clause[i]).level)); + } + } else if (val (clause[0]) == 0) { + if (val (clause[1]) == 0) { // Case 4: Both literals are unassigned + + // Every other literal of the clause is either + // - unassigned + // - falsified + for (size_t i = 2; i < clause.size (); i++) + assert (val (clause[i]) <= 0); + + } else { // Case 5: First unassigned, next falsified -> PROPAGATE + // Every other literal of the clause is falsified but at a lower + // decision level + for (size_t i = 2; i < clause.size (); i++) + assert (val (clause[i]) < 0 && + (var (clause[1]).level >= var (clause[i]).level)); + } + } else { + assert (val (clause[0]) < 0 && + val (clause[1]) < 0); // Case 6: Both literals are falsified + + // They are ordered by higher to lower decision level + assert (var (clause[0]).level >= var (clause[1]).level); + + // Every other literal of the clause is falsified, but at a lower level + for (size_t i = 2; i < clause.size (); i++) + assert (val (clause[i]) < 0 && + (var (clause[1]).level >= var (clause[i]).level)); + } +} + +#ifndef NDEBUG + +/*----------------------------------------------------------------------------*/ +// +// An expensive function that can be used for deep-debug trail-related +// issues in mobical. Do not use it unless it is really unavoidable. +// +// eq_class contains all the merged external literals that are currently +// compacted to the internal literal of trail[0] and return true. +// +// In case trail[0] does not exists or is not on the root level, the +// function returns false (indicating that there was no merger literal +// found). +// +bool Internal::get_merged_literals (std::vector &eq_class) { + eq_class.clear (); + + if (!trail.size ()) + return false; + + int ilit = trail[0]; + size_t lit_level = var (ilit).level; + + if (!lit_level) { + // Collect all the variables that are merged and mapped to that ilit + size_t e2i_size = external->e2i.size (); + int ivar = abs (ilit); + for (size_t i = 0; i < e2i_size; i++) { + int other = abs (external->e2i[i]); + if (other == ivar) { + if (external->e2i[i] == ilit) + eq_class.push_back (i); + else + eq_class.push_back (-1 * i); + } + } + + return true; + } + + return false; +} + +/*----------------------------------------------------------------------------*/ +// +// Collect all external variables that are FIXED internally. Again an +// expensive function that should be called only for debugging in mobical. +// +// Do not use it unless it is really unavoidable. +// +void Internal::get_all_fixed_literals (std::vector &fixed_lits) { + fixed_lits.clear (); + if (!trail.size ()) + return; + + int e2i_size = external->e2i.size (); + int ilit; + for (int eidx = 1; eidx < e2i_size; eidx++) { + ilit = external->e2i[eidx]; + if (ilit && !external->ervars[eidx]) { + Flags &f = flags (ilit); + if (f.status == Flags::FIXED) { + fixed_lits.push_back (vals[abs (ilit)] * eidx); + } + } + } +} +#endif + +} // namespace CaDiCaL diff --git a/src/sat/cadical/factor.cpp b/src/sat/cadical/factor.cpp new file mode 100644 index 000000000..dbb5a84e5 --- /dev/null +++ b/src/sat/cadical/factor.cpp @@ -0,0 +1,921 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +#define FACTORS 1 +#define QUOTIENT 2 +#define NOUNTED 4 + +inline bool factor_occs_size::operator() (unsigned a, unsigned b) { + size_t s = internal->occs (internal->u2i (a)).size (); + size_t t = internal->occs (internal->u2i (b)).size (); + if (s > t) + return true; + if (s < t) + return false; + return a > b; +} + +// do full occurence list as in elim.cpp but filter out useless clauses +void Internal::factor_mode () { + reset_watches (); + + assert (!watching ()); + init_occs (); + + const int size_limit = opts.factorsize; + + vector bincount, largecount; + const unsigned max_lit = 2 * (max_var + 1); + enlarge_zero (bincount, max_lit); + if (size_limit > 2) + enlarge_zero (largecount, max_lit); + + vector candidates; + int64_t &ticks = stats.ticks.factor; + ticks += 1 + cache_lines (clauses.size (), sizeof (Clause *)); + + // push binary clauses on the occurrence stack. + for (const auto &c : clauses) { + ticks++; + if (c->garbage) + continue; + if (c->redundant && c->size > 2) + continue; + if (c->size > size_limit) + continue; + if (c->size == 2) { + const int lit = c->literals[0]; + const int other = c->literals[1]; + bincount[vlit (lit)]++; + bincount[vlit (other)]++; + occs (lit).push_back (c); + occs (other).push_back (c); + continue; + } + candidates.push_back (c); + for (const auto &lit : *c) { + largecount[vlit (lit)]++; + } + } + if (size_limit == 2) + return; + + // iterate counts of larger clauses rounds often + const unsigned rounds = opts.factorcandrounds; + unsigned candidates_before = 0; + for (unsigned round = 1; round <= rounds; round++) { + LOG ("factor round %d", round); + if (candidates.size () == candidates_before) + break; + ticks += 1 + cache_lines (candidates.size (), sizeof (Clause *)); + candidates_before = candidates.size (); + vector newlargecount; + enlarge_zero (newlargecount, max_lit); + const auto begin = candidates.begin (); + auto p = candidates.begin (); + auto q = p; + const auto end = candidates.end (); + while (p != end) { + Clause *c = *q++ = *p++; + ticks++; + for (const auto &lit : *c) { + const auto idx = vlit (lit); + if (bincount[idx] + largecount[idx] < 2) { + q--; + goto CONTINUE_WITH_NEXT_CLAUSE; + } + } + for (const auto &lit : *c) { + const auto idx = vlit (lit); + newlargecount[idx]++; + } + CONTINUE_WITH_NEXT_CLAUSE: + continue; + } + candidates.resize (q - begin); + largecount.swap (newlargecount); + } + + // finally push remaining clause on the occurrence stack + for (const auto &c : candidates) + for (const auto &lit : *c) + occs (lit).push_back (c); +} + +// go back to two watch scheme +void Internal::reset_factor_mode () { + reset_occs (); + init_watches (); + connect_watches (); +} + +Factoring::Factoring (Internal *i, int64_t l) + : internal (i), limit (l), schedule (i) { + const unsigned max_var = internal->max_var; + const unsigned max_lit = 2 * (max_var + 1); + initial = max_var; + bound = internal->lim.elimbound; + enlarge_zero (count, max_lit); + quotients.first = quotients.last = 0; +} + +Factoring::~Factoring () { + assert (counted.empty ()); + assert (nounted.empty ()); + assert (flauses.empty ()); + internal->release_quotients (*this); + schedule.erase (); // actually not necessary +} + +double Internal::tied_next_factor_score (int lit) { + double res = occs (lit).size (); + LOG ("watches score %g of %d", res, lit); + return res; +} + +// the marks in cadical have 6 bits for marking but work on idx +// to mark everything (FACTORS, QUOTIENT, NOUNTED) we shift the bits +// depending on the sign of factor (+ bitmask) +// i.e. if factor is positive, we apply a bitmask to only get +// the first three bits (& 7u) +// otherwise we leftshift by 3 (>> 3) to get the bits 4,5,6 +// use markfact, unmarkfact, getfact for this purpose. +// +Quotient *Internal::new_quotient (Factoring &factoring, int factor) { + assert (!getfact (factor, FACTORS)); + markfact (factor, FACTORS); + Quotient *res = new Quotient (factor); + res->next = 0; + res->matched = 0; + Quotient *last = factoring.quotients.last; + res->bid = 0; + if (last) { + assert (factoring.quotients.first); + assert (!last->next); + last->next = res; + res->id = last->id + 1; + } else { + assert (!factoring.quotients.first); + factoring.quotients.first = res; + res->id = 0; + } + factoring.quotients.last = res; + res->prev = last; + LOG ("new quotient[%zu] with factor %d", res->id, factor); + return res; +} + +void Internal::release_quotients (Factoring &factoring) { + for (Quotient *q = factoring.quotients.first, *next; q; q = next) { + next = q->next; + int factor = q->factor; + assert (getfact (factor, FACTORS)); + unmarkfact (factor, FACTORS); + delete q; + } + factoring.quotients.first = factoring.quotients.last = 0; +} + +size_t Internal::first_factor (Factoring &factoring, int factor) { + assert (!factoring.quotients.first); + Quotient *quotient = new_quotient (factoring, factor); + vector &qlauses = quotient->qlauses; + int64_t ticks = 0; + for (const auto &c : occs (factor)) { + qlauses.push_back (c); + ticks++; + } + size_t res = qlauses.size (); + LOG ("quotient[0] factor %d size %zu", factor, res); + // This invariant can of course be broken by previous factorings + // assert (res > 1); + stats.ticks.factor += ticks; + return res; +} + +void Internal::clear_nounted (vector &nounted) { + for (const auto &lit : nounted) { + assert (getfact (lit, NOUNTED)); + unmarkfact (lit, NOUNTED); + } + nounted.clear (); +} + +void Internal::clear_flauses (vector &flauses) { + for (auto c : flauses) { + assert (c->swept); + c->swept = false; + } + flauses.clear (); +} + +Quotient *Internal::best_quotient (Factoring &factoring, + size_t *best_reduction_ptr) { + size_t factors = 1, best_reduction = 0; + Quotient *best = 0; + for (Quotient *q = factoring.quotients.first; q; q = q->next) { + size_t quotients = q->qlauses.size (); + size_t before_factorization = quotients * factors; + size_t after_factorization = quotients + factors; + if (before_factorization == after_factorization) + LOG ("quotient[%zu] factors %zu clauses into %zu thus no change", + factors - 1, before_factorization, after_factorization); + else if (before_factorization < after_factorization) + LOG ("quotient[%zu] factors %zu clauses into %zu thus %zu more", + factors - 1, before_factorization, after_factorization, + after_factorization - before_factorization); + else { + size_t delta = before_factorization - after_factorization; + LOG ("quotient[%zu] factors %zu clauses into %zu thus %zu less", + factors - 1, before_factorization, after_factorization, delta); + if (!best || best_reduction < delta) { + best_reduction = delta; + best = q; + } + } + factors++; + } + if (!best) { + LOG ("no decreasing quotient found"); + return 0; + } + LOG ("best decreasing quotient[%zu] with reduction %zu", best->id, + best_reduction); + *best_reduction_ptr = best_reduction; + return best; +} + +int Internal::next_factor (Factoring &factoring, unsigned *next_count_ptr) { + Quotient *last_quotient = factoring.quotients.last; + assert (last_quotient); + vector &last_clauses = last_quotient->qlauses; + vector &count = factoring.count; + vector &counted = factoring.counted; + vector &flauses = factoring.flauses; + assert (counted.empty ()); + assert (flauses.empty ()); + const int initial = factoring.initial; + int64_t ticks = 1 + cache_lines (last_clauses.size (), sizeof (Clause *)); + for (auto c : last_clauses) { + assert (!c->swept); + int min_lit = 0; + unsigned factors = 0; + size_t min_size = 0; + ticks++; + for (const auto &other : *c) { + if (getfact (other, FACTORS)) { + if (factors++) + break; + } else { + assert (!getfact (other, QUOTIENT)); + markfact (other, QUOTIENT); + const size_t other_size = occs (other).size (); + if (!min_lit || other_size < min_size) { + min_lit = other; + min_size = other_size; + } + } + } + assert (factors); + if (factors == 1) { + assert (min_lit); + const int c_size = c->size; + vector &nounted = factoring.nounted; + assert (nounted.empty ()); + ticks += 1 + cache_lines (occs (min_lit).size (), sizeof (Clause *)); + for (auto d : occs (min_lit)) { + if (c == d) + continue; + ticks++; + if (d->swept) + continue; + if (d->size != c_size) + continue; + int next = 0; + for (const auto &other : *d) { + if (getfact (other, QUOTIENT)) + continue; + if (getfact (other, FACTORS)) + goto CONTINUE_WITH_NEXT_MIN_WATCH; + if (getfact (other, NOUNTED)) + goto CONTINUE_WITH_NEXT_MIN_WATCH; + if (next) + goto CONTINUE_WITH_NEXT_MIN_WATCH; + next = other; + } + assert (next); + if (abs (next) > abs (initial)) + continue; + if (!active (next)) + continue; + assert (!getfact (next, FACTORS)); + assert (!getfact (next, NOUNTED)); + markfact (next, NOUNTED); + nounted.push_back (next); + d->swept = true; + flauses.push_back (d); + if (!count[vlit (next)]) + counted.push_back (next); + count[vlit (next)]++; + CONTINUE_WITH_NEXT_MIN_WATCH:; + } + clear_nounted (nounted); + } + for (const auto &other : *c) + if (getfact (other, QUOTIENT)) + unmarkfact (other, QUOTIENT); + stats.ticks.factor += ticks; + ticks = 0; + if (stats.ticks.factor > factoring.limit) + break; + } + clear_flauses (flauses); + unsigned next_count = 0; + int next = 0; + if (stats.ticks.factor <= factoring.limit) { + unsigned ties = 0; + for (const auto &lit : counted) { + const unsigned lit_count = count[vlit (lit)]; + if (lit_count < next_count) + continue; + if (lit_count == next_count) { + assert (lit_count); + ties++; + } else { + assert (lit_count > next_count); + next_count = lit_count; + next = lit; + ties = 1; + } + } + if (next_count < 2) { + LOG ("next factor count %u smaller than 2", next_count); + next = 0; + } else if (ties > 1) { + LOG ("found %u tied next factor candidate literals with count %u", + ties, next_count); + double next_score = -1; + for (const auto &lit : counted) { + const unsigned lit_count = count[vlit (lit)]; + if (lit_count != next_count) + continue; + double lit_score = tied_next_factor_score (lit); + assert (lit_score >= 0); + LOG ("score %g of next factor candidate %d", lit_score, lit); + if (lit_score <= next_score) + continue; + next_score = lit_score; + next = lit; + } + assert (next_score >= 0); + assert (next); + LOG ("best score %g of next factor %d", next_score, next); + } else { + assert (ties == 1); + LOG ("single next factor %d with count %u", next, next_count); + } + } + for (const auto &lit : counted) + count[vlit (lit)] = 0; + counted.clear (); + assert (!next || next_count > 1); + *next_count_ptr = next_count; + return next; +} + +void Internal::factorize_next (Factoring &factoring, int next, + unsigned expected_next_count) { + Quotient *last_quotient = factoring.quotients.last; + Quotient *next_quotient = new_quotient (factoring, next); + + assert (last_quotient); + vector &last_clauses = last_quotient->qlauses; + vector &next_clauses = next_quotient->qlauses; + vector &matches = next_quotient->matches; + vector &flauses = factoring.flauses; + assert (flauses.empty ()); + + int64_t ticks = 1 + cache_lines (last_clauses.size (), sizeof (Clause *)); + + size_t i = 0; + + for (auto c : last_clauses) { + assert (!c->swept); + int min_lit = 0; + unsigned factors = 0; + size_t min_size = 0; + ticks++; + for (const auto &other : *c) { + if (getfact (other, FACTORS)) { + if (factors++) + break; + } else { + assert (!getfact (other, QUOTIENT)); + markfact (other, QUOTIENT); + const size_t other_size = occs (other).size (); + if (!min_lit || other_size < min_size) { + min_lit = other; + min_size = other_size; + } + } + } + assert (factors); + if (factors == 1) { + assert (min_lit); + const int c_size = c->size; + ticks += 1 + cache_lines (occs (min_lit).size (), sizeof (Clause *)); + for (auto d : occs (min_lit)) { + if (c == d) + continue; + ticks++; + if (d->swept) + continue; + if (d->size != c_size) + continue; + for (const auto &other : *d) { + if (getfact (other, QUOTIENT)) + continue; + if (other != next) + goto CONTINUE_WITH_NEXT_MIN_WATCH; + } + LOG (c, "matched"); + LOG (d, "keeping"); + + next_clauses.push_back (d); + matches.push_back (i); + flauses.push_back (d); + d->swept = true; + break; + + CONTINUE_WITH_NEXT_MIN_WATCH:; + } + } + for (const auto &other : *c) + if (getfact (other, QUOTIENT)) + unmarkfact (other, QUOTIENT); + i++; + } + clear_flauses (flauses); + stats.ticks.factor += ticks; + + assert (expected_next_count <= next_clauses.size ()); + (void) expected_next_count; +} + +// We only need to enlarge factoring.count as everything else is +// initialized in internal +void Internal::resize_factoring (Factoring &factoring, int lit) { + assert (lit > 0); + size_t new_var_size = lit + 1; + size_t new_lit_size = 2 * new_var_size; + enlarge_zero (factoring.count, new_lit_size); +} + +void Internal::flush_unmatched_clauses (Quotient *q) { + Quotient *prev = q->prev; + vector &q_matches = q->matches, &prev_matches = prev->matches; + vector &q_clauses = q->qlauses, &prev_clauses = prev->qlauses; + const size_t n = q_clauses.size (); + assert (n == q_matches.size ()); + bool prev_is_first = !prev->id; + size_t i = 0; + while (i < q_matches.size ()) { + size_t j = q_matches[i]; + q_matches[i] = i; + assert (i <= j); + if (!prev_is_first) { + size_t matches = prev_matches[j]; + prev_matches[i] = matches; + } + Clause *c = prev_clauses[j]; + prev_clauses[i] = c; + i++; + } + LOG ("flushing %zu clauses of quotient[%zu]", prev_clauses.size () - n, + prev->id); + if (!prev_is_first) + prev_matches.resize (n); + prev_clauses.resize (n); +} + +// special case when we have two quotients with negated factors. +// in this case, factoring does not make sense, and instead we +// can resolve the clauses of the two quotients. +// this subsumes all clauses in all quotients. +void Internal::add_self_subsuming_factor (Quotient *q, Quotient *p) { + const int factor = q->factor; + const int not_factor = p->factor; + assert (-factor == not_factor); + LOG ( + "adding self subsuming factor because blocked clause is a tautology"); + for (auto c : q->qlauses) { + for (const auto &lit : *c) { + if (lit == factor) + continue; + clause.push_back (lit); + } + if (lrat) { + for (auto d : p->qlauses) { + bool match = true; + for (const auto &lit : *d) { + if (lit == not_factor) + continue; + if (std::find (clause.begin (), clause.end (), lit) == + clause.end ()) { + match = false; + break; + } + } + if (match) { + lrat_chain.push_back (d->id); + break; + } + } + lrat_chain.push_back (c->id); + assert (lrat_chain.size () == 2); + } + if (clause.size () > 1) { + new_factor_clause (); + } else { + const int unit = clause[0]; + const signed char tmp = val (unit); + if (!tmp) + assign_unit (unit); + else if (tmp < 0) { + if (lrat) { + int64_t id = unit_id (-unit); + lrat_chain.push_back (id); + std::reverse (lrat_chain.begin (), lrat_chain.end ()); + } + learn_empty_clause (); + clause.clear (); + lrat_chain.clear (); + break; + } + } + clause.clear (); + lrat_chain.clear (); + } +} + +bool Internal::self_subsuming_factor (Quotient *q) { + Quotient *x = 0, *y = 0; + bool found = false; + for (Quotient *p = q; p; p = p->prev) { + const int factor = p->factor; + Flags &f = flags (factor); + if (f.seen) { + assert (std::find (analyzed.begin (), analyzed.end (), -factor) != + analyzed.end ()); + found = true; + x = p; + for (Quotient *r = q; r; r = r->prev) { + if (r->factor != -factor) + continue; + y = r; + break; + } + break; + } + analyzed.push_back (factor); + f.seen = true; + } + assert (!found || (x && y)); + clear_analyzed_literals (); + if (found) { + add_self_subsuming_factor (x, y); + return true; + } + return false; +} + +// this is a pure binary clauses containing fresh and one other literal +// it is added for all applicable quotients. +void Internal::add_factored_divider (Quotient *q, int fresh) { + const int factor = q->factor; + LOG ("factored %d divider %d", factor, fresh); + clause.push_back (factor); + clause.push_back (fresh); + new_factor_clause (); + clause.clear (); + if (lrat) + mini_chain.push_back (-clause_id); +} + +// this clause is blocked on fresh, i.e., it contains all literals from +// the binaries above, but negated. This is only added to the proof, to +// make checking easier. +void Internal::blocked_clause (Quotient *q, int not_fresh) { + if (!proof) + return; + int64_t new_id = ++clause_id; + q->bid = new_id; + assert (clause.empty ()); + for (Quotient *p = q; p; p = p->prev) + clause.push_back (-p->factor); + clause.push_back (not_fresh); + assert (!lrat || mini_chain.size ()); + proof->add_derived_clause (new_id, true, clause, mini_chain); + mini_chain.clear (); + clause.clear (); +} + +// this is the other side of the factored clauses. To derive these, +// one can resolved the blocked clause on all matching clauses of +// one type +void Internal::add_factored_quotient (Quotient *q, int not_fresh) { + LOG ("adding factored quotient[%zu] clauses", q->id); + const int factor = q->factor; + assert (lrat_chain.empty ()); + auto qlauses = q->qlauses; + for (unsigned idx = 0; idx < qlauses.size (); idx++) { + const auto c = qlauses[idx]; + assert (clause.empty ()); + for (const auto &other : *c) { + if (other == factor) { + continue; + } + clause.push_back (other); + } + if (lrat) { + assert (proof); + assert (q->bid); + unsigned idxtoo = idx; + for (Quotient *p = q; p; p = p->prev) { + lrat_chain.push_back (p->qlauses[idxtoo]->id); + if (p->prev) + idxtoo = p->matches[idx]; + } + lrat_chain.push_back (q->bid); + } + clause.push_back (not_fresh); + new_factor_clause (); + clause.clear (); + lrat_chain.clear (); + } + if (proof) { + for (Quotient *p = q; p; p = p->prev) { + clause.push_back (-p->factor); + } + clause.push_back (not_fresh); + proof->delete_clause (q->bid, true, clause); + clause.clear (); + } +} + +// remove deleted clauses once factored. +void Internal::eagerly_remove_from_occurences (Clause *c) { + for (const auto &lit : *c) { + auto &occ = occs (lit); + auto p = occ.begin (); + auto q = occ.begin (); + auto begin = occ.begin (); + auto end = occ.end (); + while (p != end) { + *q = *p++; + if (*q != c) + q++; + } + assert (q + 1 == p); + occ.resize (q - begin); + } +} + +// delete the factored clauses +void Internal::delete_unfactored (Quotient *q) { + LOG ("deleting unfactored quotient[%zu] clauses", q->id); + for (auto c : q->qlauses) { + eagerly_remove_from_occurences (c); + mark_garbage (c); + stats.literals_unfactored += c->size; + stats.clauses_unfactored++; + } +} + +// update the priority queue for scheduling +void Internal::update_factored (Factoring &factoring, Quotient *q) { + const int factor = q->factor; + update_factor_candidate (factoring, factor); + update_factor_candidate (factoring, -factor); + for (auto c : q->qlauses) { + LOG (c, "deleting unfactored"); + for (const auto &lit : *c) + if (lit != factor) + update_factor_candidate (factoring, lit); + } +} + +bool Internal::apply_factoring (Factoring &factoring, Quotient *q) { + for (Quotient *p = q; p->prev; p = p->prev) + flush_unmatched_clauses (p); + if (self_subsuming_factor (q)) { + for (Quotient *p = q; p; p = p->prev) + delete_unfactored (p); + for (Quotient *p = q; p; p = p->prev) + update_factored (factoring, p); + return true; + } + const int fresh = get_new_extension_variable (); + if (!fresh) + return false; + stats.factored++; + factoring.fresh.push_back (fresh); + for (Quotient *p = q; p; p = p->prev) + add_factored_divider (p, fresh); + const int not_fresh = -fresh; + blocked_clause (q, not_fresh); + add_factored_quotient (q, not_fresh); + for (Quotient *p = q; p; p = p->prev) + delete_unfactored (p); + for (Quotient *p = q; p; p = p->prev) + update_factored (factoring, p); + assert (fresh > 0); + resize_factoring (factoring, fresh); + return true; +} + +void Internal::update_factor_candidate (Factoring &factoring, int lit) { + FactorSchedule &schedule = factoring.schedule; + const size_t size = occs (lit).size (); + const unsigned idx = vlit (lit); + if (schedule.contains (idx)) + schedule.update (idx); + else if (size > 1) { + schedule.push_back (idx); + } +} + +void Internal::schedule_factorization (Factoring &factoring) { + for (const auto &idx : vars) { + if (active (idx)) { + Flags &f = flags (idx); + const int lit = idx; + const int not_lit = -lit; + if (f.factor & 1) + update_factor_candidate (factoring, lit); + if (f.factor & 2) + update_factor_candidate (factoring, not_lit); + } + } +#ifndef QUIET + size_t size_cands = factoring.schedule.size (); + VERBOSE (2, "scheduled %zu factorization candidate literals %.0f %%", + size_cands, percent (size_cands, max_var)); +#endif +} + +bool Internal::run_factorization (int64_t limit) { + Factoring factoring = Factoring (this, limit); + schedule_factorization (factoring); + bool done = false; +#ifndef QUIET + unsigned factored = 0; +#endif + int64_t *ticks = &stats.ticks.factor; + VERBOSE (3, "factorization limit of %" PRIu64 " ticks", limit - *ticks); + + while (!unsat && !done && !factoring.schedule.empty ()) { + const unsigned ufirst = factoring.schedule.pop_front (); + LOG ("next factor candidate %d", ufirst); + const int first = u2i (ufirst); + const int first_idx = vidx (first); + if (!active (first_idx)) + continue; + if (!occs (first).size ()) { + factoring.schedule.clear (); + break; + } + if (*ticks > limit) { + VERBOSE (2, "factorization ticks limit hit"); + break; + } + if (terminated_asynchronously ()) + break; + Flags &f = flags (first_idx); + const unsigned bit = 1u << (first < 0); + if (!(f.factor & bit)) + continue; + f.factor &= ~bit; + const size_t first_count = first_factor (factoring, first); + if (first_count > 1) { + for (;;) { + unsigned next_count; + const int next = next_factor (factoring, &next_count); + if (next == 0) + break; + assert (next_count > 1); + if (next_count < 2) + break; + factorize_next (factoring, next, next_count); + } + size_t reduction; + Quotient *q = best_quotient (factoring, &reduction); + if (q && (int) reduction > factoring.bound) { + if (apply_factoring (factoring, q)) { +#ifndef QUIET + factored++; +#endif + } else + done = true; + } + } + release_quotients (factoring); + } + + // since we cannot remove elements from the heap we check wether the + // first element in the heap has occurences + bool completed = factoring.schedule.empty (); + if (!completed) { + const unsigned idx = factoring.schedule.front (); + completed = occs (u2i (idx)).empty (); + } + // kissat initializes scores for new variables at this point, however + // this is actually done already during resize of internal +#ifndef QUIET + report ('f', !factored); +#endif + return completed; +} + +int Internal::get_new_extension_variable () { + const int current_max_external = external->max_var; + const int new_external = current_max_external + 1; + const int new_internal = external->internalize (new_external, true); + // one sideeffect of internalize is enlarging the internal datastructures + // which can initialize the watches (wtab) + if (watching ()) + reset_watches (); + // it does not enlarge otab, however, so we do this manually + init_occs (); + assert (vlit (new_internal)); + return new_internal; +} + +bool Internal::factor () { + if (unsat) + return false; + if (terminated_asynchronously ()) + return false; + if (!opts.factor) + return false; + // The following assertion fails if there are *only* user propagator + // clauses (which are redundant). + // assert (stats.mark.factor || clauses.empty ()); + if (last.factor.marked >= stats.mark.factor) { + VERBOSE (3, + "factorization skipped as no literals have been" + "marked to be added (%" PRIu64 " < %" PRIu64 ")", + last.factor.marked, stats.mark.factor); + return false; + } + assert (!level); + + SET_EFFORT_LIMIT (limit, factor, stats.factor); + if (!stats.factor) + limit += opts.factoriniticks * 1e6; + + START_SIMPLIFIER (factor, FACTOR); + stats.factor++; + +#ifndef QUIET + struct { + int64_t variables, clauses, ticks; + } before, after, delta; + before.variables = stats.variables_extension + stats.variables_original; + before.ticks = stats.ticks.factor; + before.clauses = stats.current.irredundant; +#endif + + factor_mode (); + bool completed = run_factorization (limit); + reset_factor_mode (); + + propagated = 0; + if (!unsat && !propagate ()) { + learn_empty_clause (); + } + +#ifndef QUIET + after.variables = stats.variables_extension + stats.variables_original; + after.clauses = stats.current.irredundant; + after.ticks = stats.ticks.factor; + delta.variables = after.variables - before.variables; + delta.clauses = before.clauses - after.clauses; + delta.ticks = after.ticks - before.ticks; + VERBOSE (2, "used %f million factorization ticks", delta.ticks * 1e-6); + phase ("factorization", stats.factor, + "introduced %" PRId64 " extension variables %.0f%%", + delta.variables, percent (delta.variables, before.variables)); + phase ("factorization", stats.factor, + "removed %" PRId64 " irredundant clauses %.0f%%", delta.clauses, + percent (delta.clauses, before.clauses)); +#endif + + if (completed) + last.factor.marked = stats.mark.factor; + STOP_SIMPLIFIER (factor, FACTOR); + return true; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/factor.hpp b/src/sat/cadical/factor.hpp new file mode 100644 index 000000000..ac01d35c5 --- /dev/null +++ b/src/sat/cadical/factor.hpp @@ -0,0 +1,54 @@ +#ifndef _factor_hpp_INCLUDED +#define _factor_hpp_INCLUDED + +#include "clause.hpp" +#include "heap.hpp" + +namespace CaDiCaL { + +struct Internal; + +struct factor_occs_size { + Internal *internal; + factor_occs_size (Internal *i) : internal (i) {} + bool operator() (unsigned a, unsigned b); +}; + +struct Quotient { + Quotient (int f) : factor (f) {} + ~Quotient () {} + int factor; + size_t id; + int64_t bid; // for LRAT + Quotient *prev, *next; + vector qlauses; + vector matches; + size_t matched; +}; + +typedef heap FactorSchedule; + +struct Factoring { + Factoring (Internal *, int64_t); + ~Factoring (); + + // These are initialized by the constructor + Internal *internal; + int64_t limit; + FactorSchedule schedule; + + int initial; + int bound; + vector count; + vector fresh; + vector counted; + vector nounted; + vector flauses; + struct { + Quotient *first, *last; + } quotients; +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/file.cpp b/src/sat/cadical/file.cpp new file mode 100644 index 000000000..7f8a82dea --- /dev/null +++ b/src/sat/cadical/file.cpp @@ -0,0 +1,506 @@ +#include "internal.hpp" + +/*------------------------------------------------------------------------*/ + +// Some more low-level 'C' headers. + +extern "C" { +#include +#include +#include +#include +#include +#include +#include +} + +#ifndef _WIN32 + +extern "C" { +#include +} + +#endif + +#if defined(__APPLE__) || defined(__MACH__) + +extern "C" { +#include +#include +} + +#include + +#endif + +/*------------------------------------------------------------------------*/ + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Private constructor. + +File::File (Internal *i, bool w, int c, int p, FILE *f, const char *n) + : internal (i), +#if !defined(QUIET) || !defined(NDEBUG) + writing (w), +#endif + close_file (c), child_pid (p), file (f), _name (strdup (n)), + _lineno (1), _bytes (0) { + (void) w; + assert (f), assert (n); +} + +/*------------------------------------------------------------------------*/ + +bool File::exists (const char *path) { + struct stat buf; + if (stat (path, &buf)) + return false; + if (access (path, R_OK)) + return false; + return true; +} + +bool File::writable (const char *path) { + int res; + if (!path) + res = 1; + else if (!strcmp (path, "/dev/null")) + res = 0; + else { + if (!*path) + res = 2; + else { + struct stat buf; + const char *p = strrchr (path, '/'); + if (!p) { + if (stat (path, &buf)) + res = ((errno == ENOENT) ? 0 : -2); + else if (S_ISDIR (buf.st_mode)) + res = 3; + else + res = (access (path, W_OK) ? 4 : 0); + } else if (!p[1]) + res = 5; + else { + size_t len = p - path; + char *dirname = new char[len + 1]; + strncpy (dirname, path, len); + dirname[len] = 0; + if (stat (dirname, &buf)) + res = 6; + else if (!S_ISDIR (buf.st_mode)) + res = 7; + else if (access (dirname, W_OK)) + res = 8; + else if (stat (path, &buf)) + res = (errno == ENOENT) ? 0 : -3; + else + res = access (path, W_OK) ? 9 : 0; + delete[] dirname; + } + } + } + return !res; +} + +bool File::piping () { + struct stat stat; + int fd = fileno (file); + if (fstat (fd, &stat)) + return true; + return S_ISFIFO (stat.st_mode); +} + +// These are signatures for supported compressed file types. In 2018 the +// SAT Competition was running on StarExec and used internally 'bzip2' +// compressed files, but gave them uncompressed to the solver using exactly +// the same path (with '.bz2' suffix). Then 'CaDiCaL' tried to read that +// actually uncompressed file through 'bzip2', which of course failed. Now +// we double check and fall back to reading the file as is, if the signature +// does not match after issuing a warning. + +static int xzsig[] = {0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00, 0x00, EOF}; +static int bz2sig[] = {0x42, 0x5A, 0x68, EOF}; +static int gzsig[] = {0x1F, 0x8B, EOF}; +static int sig7z[] = {0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C, EOF}; +static int lzmasig[] = {0x5D, EOF}; + +bool File::match (Internal *internal, const char *path, const int *sig) { + assert (path); + FILE *tmp = fopen (path, "r"); + if (!tmp) { + WARNING ("failed to open '%s' to check signature", path); + return false; + } + bool res = true; + for (const int *p = sig; res && (*p != EOF); p++) + res = (cadical_getc_unlocked (tmp) == *p); + fclose (tmp); + if (!res) + WARNING ("file type signature check for '%s' failed", path); + return res; +} + +size_t File::size (const char *path) { + struct stat buf; + if (stat (path, &buf)) + return 0; + return (size_t) buf.st_size; +} + +// Check that 'prg' is in the 'PATH' and thus can be found if executed +// through 'popen' or 'exec'. + +char *File::find_program (const char *prg) { + size_t prglen = strlen (prg); + const char *c = getenv ("PATH"); + if (!c) + return 0; + size_t len = strlen (c); + char *e = new char[len + 1]; + strcpy (e, c); + char *res = 0; + for (char *p = e, *q; !res && p < e + len; p = q) { + for (q = p; *q && *q != ':'; q++) + ; + *q++ = 0; + size_t pathlen = (q - p) + prglen; + char *path = new char[pathlen + 1]; + snprintf (path, pathlen + 1, "%s/%s", p, prg); + assert (strlen (path) == pathlen); + if (exists (path)) + res = path; + else + delete[] path; + } + delete[] e; + return res; +} + +/*------------------------------------------------------------------------*/ + +FILE *File::open_file (Internal *internal, const char *path, + const char *mode) { + (void) internal; + return fopen (path, mode); +} + +FILE *File::read_file (Internal *internal, const char *path) { + MSG ("opening file to read '%s'", path); + return open_file (internal, path, "r"); +} + +FILE *File::write_file (Internal *internal, const char *path) { + MSG ("opening file to write '%s'", path); + return open_file (internal, path, "wb"); +} + +/*------------------------------------------------------------------------*/ + +void File::split_str (const char *command, std::vector &argv) { + const char *c = command; + while (*c && *c == ' ') + c++; + while (*c) { + const char *p = c; + while (*p && *p != ' ') + p++; + const size_t bytes = p - c; + char *arg = new char[bytes + 1]; + (void) strncpy (arg, c, bytes); + arg[bytes] = 0; + argv.push_back (arg); + while (*p && *p == ' ') + p++; + c = p; + } +} + +void File::delete_str_vector (std::vector &argv) { + for (auto str : argv) + delete[] str; +} + +FILE *File::open_pipe (Internal *internal, const char *fmt, + const char *path, const char *mode) { +#ifdef QUIET + (void) internal; +#endif + size_t prglen = 0; + while (fmt[prglen] && fmt[prglen] != ' ') + prglen++; + char *prg = new char[prglen + 1]; + strncpy (prg, fmt, prglen); + prg[prglen] = 0; + char *found = find_program (prg); + if (found) + MSG ("found '%s' in path for '%s'", found, prg); + if (!found) + MSG ("did not find '%s' in path", prg); + delete[] prg; + if (!found) + return 0; + delete[] found; + size_t cmd_size = strlen (fmt) + strlen (path); + char *cmd = new char[cmd_size]; + snprintf (cmd, cmd_size, fmt, path); + FILE *res = popen (cmd, mode); + delete[] cmd; + return res; +} + +FILE *File::read_pipe (Internal *internal, const char *fmt, const int *sig, + const char *path) { + if (!File::exists (path)) { + LOG ("file '%s' does not exist", path); + return 0; + } + LOG ("file '%s' exists", path); + if (sig && !File::match (internal, path, sig)) + return 0; + LOG ("file '%s' matches signature for '%s'", path, fmt); + MSG ("opening pipe to read '%s'", path); + return open_pipe (internal, fmt, path, "r"); +} + +#ifndef _WIN32 + +#if defined(__APPLE__) || defined(__MACH__) +static std::mutex compressed_file_writing_mutex; +#endif + +FILE *File::write_pipe (Internal *internal, const char *command, + const char *path, int &child_pid) { + assert (command[0] && command[0] != ' '); + MSG ("writing through command '%s' to '%s'", command, path); +#ifdef QUIET + (void) internal; +#endif + std::vector args; + split_str (command, args); + assert (!args.empty ()); + args.push_back (0); + char **argv = args.data (); + char *absolute_command_path = find_program (argv[0]); + int pipe_fds[2], out; + FILE *res = 0; +#if defined(__APPLE__) || defined(__MACH__) + compressed_file_writing_mutex.lock (); +#endif + if (!absolute_command_path) + MSG ("could not find '%s' in 'PATH' environment variable", argv[0]); + else if (::pipe (pipe_fds) < 0) + MSG ("could not generate pipe to '%s' command", command); + else if ((out = ::open (path, O_CREAT | O_TRUNC | O_WRONLY, 0644)) < 0) + MSG ("could not open '%s' for writing", path); + else if ((child_pid = ::fork ()) < 0) { + MSG ("could not fork process to execute '%s' command", command); + ::close (out); + } else if (child_pid) { + ::close (pipe_fds[0]); + res = ::fdopen (pipe_fds[1], "wb"); + } else { + + // Connect stdin and stdout in child + + ::dup2 (pipe_fds[0], 0); + ::dup2 (out, 1); + + // Make sure to close all non-required fds to not cause hangs. + // This is handled now by closefrom and remains for documentation + // purposes: + // + // ::close (pipe_fds[0]); + // ::close (pipe_fds[1]); + // ::close (out); + + // Surpress '7z' verbose output on 'stderr'. + + if (command[0] == '7') { + ::close (2); + } + + // Before the fork another thread could have created more fds. These + // fds are cloned into the child process. As this inhibits pipes to + // be closed by the parent process we have to close all of the + // erroneously cloned fds here. + +#ifndef NCLOSEFROM + ::closefrom (3); +#else + // Simplistic replacement on Unix without 'closefrom'. + for (int fd = 3; fd != FD_SETSIZE; fd++) + ::close (fd); +#endif + execv (absolute_command_path, argv); + _exit (1); + } + if (absolute_command_path) + delete[] absolute_command_path; + delete_str_vector (args); +#ifdef QUIET + (void) internal; +#endif +#if defined(__APPLE__) || defined(__MACH__) + if (!res) + compressed_file_writing_mutex.unlock (); +#endif + return res; +} + +#endif + +/*------------------------------------------------------------------------*/ + +File *File::read (Internal *internal, FILE *f, const char *n) { + return new File (internal, false, 0, 0, f, n); +} + +File *File::write (Internal *internal, FILE *f, const char *n) { + return new File (internal, true, 0, 0, f, n); +} + +File *File::read (Internal *internal, const char *path) { + FILE *file; + int close_input = 2; + if (has_suffix (path, ".xz")) { + file = read_pipe (internal, "xz -c -d %s", xzsig, path); + if (!file) + goto READ_FILE; + } else if (has_suffix (path, ".lzma")) { + file = read_pipe (internal, "lzma -c -d %s", lzmasig, path); + if (!file) + goto READ_FILE; + } else if (has_suffix (path, ".bz2")) { + file = read_pipe (internal, "bzip2 -c -d %s", bz2sig, path); + if (!file) + goto READ_FILE; + } else if (has_suffix (path, ".gz")) { + file = read_pipe (internal, "gzip -c -d %s", gzsig, path); + if (!file) + goto READ_FILE; + } else if (has_suffix (path, ".7z")) { + file = read_pipe (internal, "7z x -so %s 2>/dev/null", sig7z, path); + if (!file) + goto READ_FILE; + } else { + READ_FILE: + file = read_file (internal, path); + close_input = 1; + } + + if (!file) + return 0; + + return new File (internal, false, close_input, 0, file, path); +} + +File *File::write (Internal *internal, const char *path) { + FILE *file; + int close_output = 3, child_pid = 0; +#ifndef _WIN32 + if (has_suffix (path, ".xz")) + file = write_pipe (internal, "xz -c", path, child_pid); + else if (has_suffix (path, ".bz2")) + file = write_pipe (internal, "bzip2 -c", path, child_pid); + else if (has_suffix (path, ".gz")) + file = write_pipe (internal, "gzip -c", path, child_pid); + else if (has_suffix (path, ".7z")) + file = write_pipe (internal, "7z a -an -txz -si -so", path, child_pid); + else +#endif + file = write_file (internal, path), close_output = 1; + + if (!file) + return 0; + + return new File (internal, true, close_output, child_pid, file, path); +} + +void File::close (bool print) { + assert (file); +#ifndef QUIET + if (internal->opts.quiet) + print = false; + else if (internal->opts.verbose > 0) + print = true; +#endif + if (close_file == 0) { + if (print) + MSG ("disconnecting from '%s'", name ()); + } + if (close_file == 1) { + if (print) + MSG ("closing file '%s'", name ()); + fclose (file); + } + if (close_file == 2) { + if (print) + MSG ("closing input pipe to read '%s'", name ()); + pclose (file); + } +#ifndef _WIN32 + if (close_file == 3) { + if (print) + MSG ("closing output pipe to write '%s'", name ()); + fclose (file); + waitpid (child_pid, 0, 0); +#if defined(__APPLE__) || defined(__MACH__) + compressed_file_writing_mutex.unlock (); +#endif + } +#endif + file = 0; // mark as closed + + // TODO what about error checking for 'fclose', 'pclose' or 'waitpid'? + +#ifndef QUIET + if (print) { + if (writing) { + uint64_t written_bytes = bytes (); + double written_mb = written_bytes / (double) (1 << 20); + MSG ("after writing %" PRIu64 " bytes %.1f MB", written_bytes, + written_mb); + if (close_file == 3) { + size_t actual_bytes = size (name ()); + if (actual_bytes) { + double actual_mb = actual_bytes / (double) (1 << 20); + MSG ("deflated to %zd bytes %.1f MB", actual_bytes, actual_mb); + MSG ("factor %.2f (%.2f%% compression)", + relative (written_bytes, actual_bytes), + percent (actual_bytes, written_bytes)); + } else + MSG ("but could not determine actual size of written file"); + } + } else { + uint64_t read_bytes = bytes (); + double read_mb = read_bytes / (double) (1 << 20); + MSG ("after reading %" PRIu64 " bytes %.1f MB", read_bytes, read_mb); + if (close_file == 2) { + size_t actual_bytes = size (name ()); + double actual_mb = actual_bytes / (double) (1 << 20); + MSG ("inflated from %zd bytes %.1f MB", actual_bytes, actual_mb); + MSG ("factor %.2f (%.2f%% compression)", + relative (read_bytes, actual_bytes), + percent (actual_bytes, read_bytes)); + } + } + } +#endif +} + +void File::flush () { + assert (file); + fflush (file); +} + +File::~File () { + if (file) + close (); + free (_name); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/file.hpp b/src/sat/cadical/file.hpp new file mode 100644 index 000000000..a42b31bbb --- /dev/null +++ b/src/sat/cadical/file.hpp @@ -0,0 +1,210 @@ +#ifndef _file_hpp_INCLUDED +#define _file_hpp_INCLUDED + +#include +#include +#include +#include +#include + +#ifndef NDEBUG +#include +#endif + +/*------------------------------------------------------------------------*/ +#ifndef NUNLOCKED +#define cadical_putc_unlocked putc_unlocked +#define cadical_getc_unlocked getc_unlocked +#else +#define cadical_putc_unlocked putc +#define cadical_getc_unlocked getc +#endif +/*------------------------------------------------------------------------*/ + +namespace CaDiCaL { + +// Wraps a 'C' file 'FILE' with name and supports zipped reading and writing +// through 'popen' using external helper tools. Reading has line numbers. +// Compression and decompression relies on external utilities, e.g., 'gzip', +// 'bzip2', 'xz', and '7z', which should be in the 'PATH'. + +struct Internal; + +class File { + + Internal *internal; +#if !defined(QUIET) || !defined(NDEBUG) + bool writing; +#endif + + int close_file; // need to close file (1=fclose, 2=pclose, 3=pipe) + int child_pid; + FILE *file; + char *_name; + uint64_t _lineno; + uint64_t _bytes; + + File (Internal *, bool, int, int, FILE *, const char *); + + static FILE *open_file (Internal *, const char *path, const char *mode); + static FILE *read_file (Internal *, const char *path); + static FILE *write_file (Internal *, const char *path); + + static void split_str (const char *, std::vector &); + static void delete_str_vector (std::vector &); + + static FILE *open_pipe (Internal *, const char *fmt, const char *path, + const char *mode); + static FILE *read_pipe (Internal *, const char *fmt, const int *sig, + const char *path); +#ifndef __WIN32 + static FILE *write_pipe (Internal *, const char *fmt, const char *path, + int &child_pid); +#endif + +public: + static char *find_program (const char *prg); // search in 'PATH' + static bool exists (const char *path); // file exists? + static bool writable (const char *path); // can write to that file? + static size_t size (const char *path); // file size in bytes + + bool piping (); // Is opened file a pipe? + + // Does the file match the file type signature. + // + static bool match (Internal *, const char *path, const int *sig); + + // Read from existing file. Assume given name. + // + static File *read (Internal *, FILE *f, const char *name); + + // Open file from path name for reading (possibly through opening a pipe + // to a decompression utility, based on the suffix). + // + static File *read (Internal *, const char *path); + + // Same for writing as for reading above. + // + static File *write (Internal *, FILE *, const char *name); + static File *write (Internal *, const char *path); + + ~File (); + + // Using the 'unlocked' versions here is way faster but + // not thread safe if the same file is used by different + // threads, which on the other hand currently is impossible. + + int get () { + assert (!writing); + int res = cadical_getc_unlocked (file); + if (res == '\n') + _lineno++; + if (res != EOF) + _bytes++; + return res; + } + + bool put (char ch) { + assert (writing); + if (cadical_putc_unlocked (ch, file) == EOF) + return false; + _bytes++; + return true; + } + + bool endl () { return put ('\n'); } + + bool put (unsigned char ch) { + assert (writing); + if (cadical_putc_unlocked (ch, file) == EOF) + return false; + _bytes++; + return true; + } + + bool put (const char *s) { + for (const char *p = s; *p; p++) + if (!put (*p)) + return false; + return true; + } + + bool put (int lit) { + assert (writing); + if (!lit) + return put ('0'); + else if (lit == -2147483648) { + assert (lit == INT_MIN); + return put ("-2147483648"); + } else { + char buffer[11]; + int i = sizeof buffer; + buffer[--i] = 0; + assert (lit != INT_MIN); + unsigned idx = abs (lit); + while (idx) { + assert (i > 0); + buffer[--i] = '0' + idx % 10; + idx /= 10; + } + if (lit < 0 && !put ('-')) + return false; + return put (buffer + i); + } + } + + bool put (int64_t l) { + assert (writing); + if (!l) + return put ('0'); + else if (l == INT64_MIN) { + assert (sizeof l == 8); + return put ("-9223372036854775808"); + } else { + char buffer[21]; + int i = sizeof buffer; + buffer[--i] = 0; + assert (l != INT64_MIN); + uint64_t k = l < 0 ? -l : l; + while (k) { + assert (i > 0); + buffer[--i] = '0' + k % 10; + k /= 10; + } + if (l < 0 && !put ('-')) + return false; + return put (buffer + i); + } + } + + bool put (uint64_t l) { + assert (writing); + if (!l) + return put ('0'); + else { + char buffer[22]; + int i = sizeof buffer; + buffer[--i] = 0; + while (l) { + assert (i > 0); + buffer[--i] = '0' + l % 10; + l /= 10; + } + return put (buffer + i); + } + } + + const char *name () const { return _name; } + uint64_t lineno () const { return _lineno; } + uint64_t bytes () const { return _bytes; } + + void connect_internal (Internal *i) { internal = i; } + bool closed () { return !file; } + + void close (bool print = false); + void flush (); +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/flags.cpp b/src/sat/cadical/flags.cpp new file mode 100644 index 000000000..de7e13b4f --- /dev/null +++ b/src/sat/cadical/flags.cpp @@ -0,0 +1,135 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +void Internal::mark_fixed (int lit) { + if (external->fixed_listener) { + int elit = externalize (lit); + assert (elit); + const int eidx = abs (elit); + if (!external->ervars[eidx]) + external->fixed_listener->notify_fixed_assignment (elit); + } + Flags &f = flags (lit); + assert (f.status == Flags::ACTIVE); + f.status = Flags::FIXED; + LOG ("fixed %d", abs (lit)); + stats.all.fixed++; + stats.now.fixed++; + stats.inactive++; + assert (stats.active); + stats.active--; + assert (!active (lit)); + assert (f.fixed ()); + + if (external_prop && private_steps) { + // If pre/inprocessing found a fixed assignment, we want the propagator + // to know about it. + // But at that point it is not guaranteed to be already on the trail, so + // notification will happen only later. + assert (!level); + } +} + +void Internal::mark_eliminated (int lit) { + Flags &f = flags (lit); + assert (f.status == Flags::ACTIVE); + f.status = Flags::ELIMINATED; + LOG ("eliminated %d", abs (lit)); + stats.all.eliminated++; + stats.now.eliminated++; + stats.inactive++; + assert (stats.active); + stats.active--; + assert (!active (lit)); + assert (f.eliminated ()); +} + +void Internal::mark_pure (int lit) { + Flags &f = flags (lit); + assert (f.status == Flags::ACTIVE); + f.status = Flags::PURE; + LOG ("pure %d", abs (lit)); + stats.all.pure++; + stats.now.pure++; + stats.inactive++; + assert (stats.active); + stats.active--; + assert (!active (lit)); + assert (f.pure ()); +} + +void Internal::mark_substituted (int lit) { + Flags &f = flags (lit); + assert (f.status == Flags::ACTIVE); + f.status = Flags::SUBSTITUTED; + LOG ("substituted %d", abs (lit)); + stats.all.substituted++; + stats.now.substituted++; + stats.inactive++; + assert (stats.active); + stats.active--; + assert (!active (lit)); + assert (f.substituted ()); +} + +void Internal::mark_active (int lit) { + Flags &f = flags (lit); + assert (f.status == Flags::UNUSED); + f.status = Flags::ACTIVE; + LOG ("activate %d previously unused", abs (lit)); + assert (stats.inactive); + stats.inactive--; + assert (stats.unused); + stats.unused--; + stats.active++; + assert (active (lit)); +} + +void Internal::reactivate (int lit) { + assert (!active (lit)); + Flags &f = flags (lit); + assert (f.status != Flags::FIXED); + assert (f.status != Flags::UNUSED); +#ifdef LOGGING + const char *msg = 0; +#endif + switch (f.status) { + default: + case Flags::ELIMINATED: + assert (f.status == Flags::ELIMINATED); + assert (stats.now.eliminated > 0); + stats.now.eliminated--; +#ifdef LOGGING + msg = "eliminated"; +#endif + break; + case Flags::SUBSTITUTED: +#ifdef LOGGING + msg = "substituted"; +#endif + assert (stats.now.substituted > 0); + stats.now.substituted--; + break; + case Flags::PURE: +#ifdef LOGGING + msg = "pure literal"; +#endif + assert (stats.now.pure > 0); + stats.now.pure--; + break; + } +#ifdef LOGGING + assert (msg); + LOG ("reactivate previously %s %d", msg, abs (lit)); +#endif + f.status = Flags::ACTIVE; + f.sweep = false; + assert (active (lit)); + stats.reactivated++; + assert (stats.inactive > 0); + stats.inactive--; + stats.active++; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/flags.hpp b/src/sat/cadical/flags.hpp new file mode 100644 index 000000000..d5ba3e2a4 --- /dev/null +++ b/src/sat/cadical/flags.hpp @@ -0,0 +1,85 @@ +#ifndef _flags_hpp_INCLUDED +#define _flags_hpp_INCLUDED + +namespace CaDiCaL { + +struct Flags { // Variable flags. + + // The first set of flags is related to 'analyze' and 'minimize'. + // + bool seen : 1; // seen in generating first UIP clause in 'analyze' + bool keep : 1; // keep in learned clause in 'minimize' + bool poison : 1; // can not be removed in 'minimize' + bool removable : 1; // can be removed in 'minimize' + bool shrinkable : 1; // can be removed in 'shrink' + bool added : 1; // has already been added to lrat_chain (in 'minimize') + + // These three variable flags are used to schedule clauses in subsumption + // ('subsume'), variables in bounded variable elimination ('elim') and in + // hyper ternary resolution ('ternary'). + // + bool elim : 1; // removed since last 'elim' round (*) + bool subsume : 1; // added since last 'subsume' round (*) + bool ternary : 1; // added in ternary clause since last 'ternary' (*) + bool sweep : 1; + bool blockable : 1; + + unsigned char + marked_signed : 2; // generate correct LRAT chains in decompose + unsigned char factor : 2; + + // These literal flags are used by blocked clause elimination ('block'). + // + unsigned char block : 2; // removed since last 'block' round (*) + unsigned char skip : 2; // skip this literal as blocking literal + + // Bits for handling assumptions. + // + unsigned char assumed : 2; + unsigned char failed : 2; // 0 if not part of failure + // 1 if positive lit is in failure + // 2 if negated lit is in failure + + enum { + UNUSED = 0, + ACTIVE = 1, + FIXED = 2, + ELIMINATED = 3, + SUBSTITUTED = 4, + PURE = 5 + }; + + unsigned char status : 3; + + // Initialized explicitly in 'Internal::init' through this function. + // + Flags () { + seen = keep = poison = removable = shrinkable = added = sweep = false; + subsume = elim = ternary = true; + block = 3u; + skip = assumed = failed = marked_signed = factor = 0; + status = UNUSED; + } + + bool unused () const { return status == UNUSED; } + bool active () const { return status == ACTIVE; } + bool fixed () const { return status == FIXED; } + bool eliminated () const { return status == ELIMINATED; } + bool substituted () const { return status == SUBSTITUTED; } + bool pure () const { return status == PURE; } + + // The flags marked with '(*)' are copied during 'External::copy_flags', + // which in essence means they are reset in the copy if they were clear. + // This avoids the effort of fruitless preprocessing the copy. + + void copy (Flags &dst) const { + dst.elim = elim; + dst.subsume = subsume; + dst.ternary = ternary; + dst.block = block; + } +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/flip.cpp b/src/sat/cadical/flip.cpp new file mode 100644 index 000000000..0df508246 --- /dev/null +++ b/src/sat/cadical/flip.cpp @@ -0,0 +1,269 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +bool Internal::flip (int lit) { + + // Do not try to flip inactive literals except for unused variables. + + if (!active (lit) && !flags (lit).unused ()) + return false; + + /* + if (flags (lit).unused ()) { + assert (lit <= max_var); + mark_active (lit); + set_val (lit, 1); + return true; + } + */ + + // TODO: Unused case is not handled yet. + // if (flags (lit).unused ()) return false; + + // Need to reestablish proper watching invariants as if there are no + // blocking literals as flipping in principle does not work with them. + + if (propergated < trail.size ()) + propergate (); + + LOG ("trying to flip %d", lit); + + const int idx = vidx (lit); + const signed char original_value = vals[idx]; + assert (original_value); + lit = original_value < 0 ? -idx : idx; + assert (val (lit) > 0); + + // Here we go over all the clauses in which 'lit' is watched by 'lit' and + // check whether assigning 'lit' to false would break watching invariants + // or even make the clause false. We also try to find replacement + // watches in case this fixes the watching invariant. This code is very + // similar to propagation of a literal in 'Internal::propagate'. + + bool res = true; + + Watches &ws = watches (lit); + const const_watch_iterator eow = ws.end (); + watch_iterator bow = ws.begin (); + + // We first go over binary watches/clauses first as this is cheaper and + // has higher chance of failure and we can not use blocking literals. + + for (const_watch_iterator i = bow; i != eow; i++) { + const Watch w = *i; + if (!w.binary ()) + continue; + const signed char b = val (w.blit); + if (b > 0) + continue; + assert (b < 0); + res = false; + break; + } + + if (res) { + const_watch_iterator i = bow; + watch_iterator j = bow; + + while (i != eow) { + + const Watch w = *j++ = *i++; + + if (w.binary ()) + continue; + + if (w.clause->garbage) { + j--; + continue; + } + + literal_iterator lits = w.clause->begin (); + + const int other = lits[0] ^ lits[1] ^ lit; + const signed char u = val (other); + if (u > 0) + continue; + + const int size = w.clause->size; + const literal_iterator middle = lits + w.clause->pos; + const const_literal_iterator end = lits + size; + literal_iterator k = middle; + + int r = 0; + signed char v = -1; + while (k != end && (v = val (r = *k)) < 0) + k++; + if (v < 0) { + k = lits + 2; + assert (w.clause->pos <= size); + while (k != middle && (v = val (r = *k)) < 0) + k++; + } + + if (v < 0) { + res = false; + break; + } + + assert (v > 0); + assert (lits + 2 <= k), assert (k <= w.clause->end ()); + w.clause->pos = k - lits; + lits[0] = other, lits[1] = r, *k = lit; + watch_literal (r, lit, w.clause); + j--; + } + + if (j != i) { + + while (i != eow) + *j++ = *i++; + + ws.resize (j - ws.begin ()); + } + } +#ifdef LOGGING + if (res) + LOG ("literal %d can be flipped", lit); + else + LOG ("literal %d can not be flipped", lit); +#endif + + if (res) { + + const int idx = vidx (lit); + const signed char original_value = vals[idx]; + assert (original_value); + lit = original_value < 0 ? -idx : idx; + assert (val (lit) > 0); + + LOG ("flipping value of %d = 1 to %d = -1", lit, lit); + + set_val (idx, -original_value); + assert (val (-lit) > 0); + assert (val (lit) < 0); + + Var &v = var (idx); + assert (trail[v.trail] == lit); + trail[v.trail] = -lit; + if (opts.ilb) { + if (!tainted_literal) + tainted_literal = lit; + else { + assert (val (tainted_literal)); + if (v.level < var (tainted_literal).level) { + tainted_literal = lit; + } + } + } + } else + LOG ("flipping value of %d failed", lit); + + return res; +} + +bool Internal::flippable (int lit) { + + // Can not check inactive literals except for unused variables. + + if (!active (lit) && !flags (lit).unused ()) + return false; + + /* + if (flags (lit).unused ()) { + assert (lit <= max_var); + mark_active (lit); + return true; + } + */ + // TODO: Unused case is not handled yet + // if (flags (lit).unused ()) return false; + + // Need to reestablish proper watching invariants as if there are no + // blocking literals as flipping in principle does not work with them. + + if (propergated < trail.size ()) + propergate (); + + LOG ("checking whether %d is flippable", lit); + + const int idx = vidx (lit); + const signed char original_value = vals[idx]; + assert (original_value); + lit = original_value < 0 ? -idx : idx; + assert (val (lit) > 0); + + // Here we go over all the clauses in which 'lit' is watched by 'lit' and + // check whether assigning 'lit' to false would break watching invariants + // or even make the clause false. In contrast to 'flip' we do not try to + // find replacement literals but do use blocking literals'. Therefore we + // also do not split the traversal code into two parts. + + bool res = true; + + Watches &ws = watches (lit); + const const_watch_iterator eow = ws.end (); + for (watch_iterator i = ws.begin (); i != eow; i++) { + + const Watch w = *i; + const signed char b = val (w.blit); + if (b > 0) + continue; + assert (b < 0); + + if (w.binary ()) { + res = false; + break; + } + + if (w.clause->garbage) + continue; + + literal_iterator lits = w.clause->begin (); + + const int other = lits[0] ^ lits[1] ^ lit; + const signed char u = val (other); + if (u > 0) { + i->blit = other; + continue; + } + + const int size = w.clause->size; + const literal_iterator middle = lits + w.clause->pos; + const const_literal_iterator end = lits + size; + literal_iterator k = middle; + + int r = 0; + signed char v = -1; + while (k != end && (v = val (r = *k)) < 0) + k++; + if (v < 0) { + k = lits + 2; + assert (w.clause->pos <= size); + while (k != middle && (v = val (r = *k)) < 0) + k++; + } + + if (v < 0) { + res = false; + break; + } + + assert (v > 0); + assert (lits + 2 <= k); + assert (k <= w.clause->end ()); + w.clause->pos = k - lits; + i->blit = r; + } + +#ifdef LOGGING + if (res) + LOG ("literal %d can be flipped", lit); + else + LOG ("literal %d can not be flipped", lit); +#endif + + return res; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/format.cpp b/src/sat/cadical/format.cpp new file mode 100644 index 000000000..87421a0fb --- /dev/null +++ b/src/sat/cadical/format.cpp @@ -0,0 +1,89 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +void Format::enlarge () { + char *old = buffer; + buffer = new char[size = size ? 2 * size : 1]; + memcpy (buffer, old, count); + delete[] old; +} + +inline void Format::push_char (char ch) { + if (size == count) + enlarge (); + buffer[count++] = ch; +} + +void Format::push_string (const char *s) { + char ch; + while ((ch = *s++)) + push_char (ch); +} + +void Format::push_int (int d) { + char tmp[16]; + snprintf (tmp, sizeof tmp, "%d", d); + push_string (tmp); +} + +void Format::push_uint64 (uint64_t u) { + char tmp[16]; + snprintf (tmp, sizeof tmp, "%" PRIu64, u); + push_string (tmp); +} + +static bool match_format (const char *&str, const char *pattern) { + assert (pattern); + const char *p = str; + const char *q = pattern; + while (*q) + if (*q++ != *p++) + return false; + str = p; + return true; +} + +const char *Format::add (const char *fmt, va_list &ap) { + const char *p = fmt; + char ch; + while ((ch = *p++)) { + if (ch != '%') + push_char (ch); + else if (*p == 'c') + push_char (va_arg (ap, int)), p++; + else if (*p == 'd') + push_int (va_arg (ap, int)), p++; + else if (*p == 's') + push_string (va_arg (ap, const char *)), p++; + else if (match_format (p, PRIu64)) + push_uint64 (va_arg (ap, uint64_t)); + else { + push_char ('%'); + push_char (*p); + break; + } // unsupported + } + push_char (0); + count--; // thus automatic append in subsequent calls. + return buffer; +} + +const char *Format::init (const char *fmt, ...) { + count = 0; + va_list ap; + va_start (ap, fmt); + const char *res = add (fmt, ap); + va_end (ap); + return res; +} + +const char *Format::append (const char *fmt, ...) { + va_list ap; + va_start (ap, fmt); + const char *res = add (fmt, ap); + va_end (ap); + return res; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/format.hpp b/src/sat/cadical/format.hpp new file mode 100644 index 000000000..8ad892cf4 --- /dev/null +++ b/src/sat/cadical/format.hpp @@ -0,0 +1,36 @@ +#ifndef _format_hpp_INCLUDED +#define _format_hpp_INCLUDED + +#include +#include + +namespace CaDiCaL { + +// This class provides a 'printf' style formatting utility. +// Only '%c', '%d', '%s' are supported at this point. +// It is used to capture and save an error message. + +class Format { + char *buffer; + int64_t count, size; + void enlarge (); + void push_char (char); + void push_string (const char *); + void push_int (int); + void push_uint64 (uint64_t); + const char *add (const char *fmt, va_list &); + +public: + Format () : buffer (0), count (0), size (0) {} + ~Format () { + if (buffer) + delete[] buffer; + } + const char *init (const char *fmt, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); + const char *append (const char *fmt, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); + operator const char * () const { return count ? buffer : 0; } +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/frattracer.cpp b/src/sat/cadical/frattracer.cpp new file mode 100644 index 000000000..4f9193b42 --- /dev/null +++ b/src/sat/cadical/frattracer.cpp @@ -0,0 +1,277 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +FratTracer::FratTracer (Internal *i, File *f, bool b, bool a) + : internal (i), file (f), binary (b), with_antecedents (a) +#ifndef QUIET + , + added (0), deleted (0), finalized (0), original (0) +#endif +{ + (void) internal; +} + +void FratTracer::connect_internal (Internal *i) { + internal = i; + file->connect_internal (internal); + LOG ("FRAT TRACER connected to internal"); +} + +FratTracer::~FratTracer () { + LOG ("FRAT TRACER delete"); + delete file; +} + +/*------------------------------------------------------------------------*/ + +inline void FratTracer::put_binary_zero () { + assert (binary); + assert (file); + file->put ((unsigned char) 0); +} + +inline void FratTracer::put_binary_lit (int lit) { + assert (binary); + assert (file); + assert (lit != INT_MIN); + unsigned x = 2 * abs (lit) + (lit < 0); + unsigned char ch; + while (x & ~0x7f) { + ch = (x & 0x7f) | 0x80; + file->put (ch); + x >>= 7; + } + ch = x; + file->put (ch); +} + +inline void FratTracer::put_binary_id (int64_t id, bool can_be_negative) { + assert (binary); + assert (file); + uint64_t x = abs (id); + if (can_be_negative) { + x = 2 * x + (id < 0); + } + unsigned char ch; + while (x & ~0x7f) { + ch = (x & 0x7f) | 0x80; + file->put (ch); + x >>= 7; + } + ch = x; + file->put (ch); +} + +/*------------------------------------------------------------------------*/ + +void FratTracer::frat_add_original_clause (int64_t id, + const vector &clause) { + if (binary) + file->put ('o'); + else + file->put ("o "); + if (binary) + put_binary_id (id); + else + file->put (id), file->put (" "); + for (const auto &external_lit : clause) + if (binary) + put_binary_lit (external_lit); + else + file->put (external_lit), file->put (' '); + if (binary) + put_binary_zero (); + else + file->put ("0\n"); +} + +void FratTracer::frat_add_derived_clause (int64_t id, + const vector &clause) { + if (binary) + file->put ('a'); + else + file->put ("a "); + if (binary) + put_binary_id (id); + else + file->put (id), file->put (" "); + for (const auto &external_lit : clause) + if (binary) + put_binary_lit (external_lit); + else + file->put (external_lit), file->put (' '); + if (binary) + put_binary_zero (); + else + file->put ("0\n"); +} + +void FratTracer::frat_add_derived_clause (int64_t id, + const vector &clause, + const vector &chain) { + if (binary) + file->put ('a'); + else + file->put ("a "); + if (binary) + put_binary_id (id); + else + file->put (id), file->put (" "); + for (const auto &external_lit : clause) + if (binary) + put_binary_lit (external_lit); + else + file->put (external_lit), file->put (' '); + if (binary) + put_binary_zero (), file->put ('l'); + else + file->put ("0 l "); + for (const auto &c : chain) + if (binary) + put_binary_id (c, true); // LRAT can have negative ids + else + file->put (c), file->put (' '); // in proof chain, so they get + if (binary) + put_binary_zero (); // since cadical has no rat-steps + else + file->put ("0\n"); // this is just 2c here +} + +void FratTracer::frat_delete_clause (int64_t id, + const vector &clause) { + if (binary) + file->put ('d'); + else + file->put ("d "); + if (binary) + put_binary_id (id); + else + file->put (id), file->put (" "); + for (const auto &external_lit : clause) + if (binary) + put_binary_lit (external_lit); + else + file->put (external_lit), file->put (' '); + if (binary) + put_binary_zero (); + else + file->put ("0\n"); +} + +void FratTracer::frat_finalize_clause (int64_t id, + const vector &clause) { + if (binary) + file->put ('f'); + else + file->put ("f "); + if (binary) + put_binary_id (id); + else + file->put (id), file->put (" "); + for (const auto &external_lit : clause) + if (binary) + put_binary_lit (external_lit); + else + file->put (external_lit), file->put (' '); + if (binary) + put_binary_zero (); + else + file->put ("0\n"); +} + +/*------------------------------------------------------------------------*/ + +void FratTracer::add_original_clause (int64_t id, bool, + const vector &clause, bool) { + if (file->closed ()) + return; + LOG ("FRAT TRACER tracing addition of original clause"); + frat_add_original_clause (id, clause); +} + +void FratTracer::add_derived_clause (int64_t id, bool, + const vector &clause, + const vector &chain) { + if (file->closed ()) + return; + LOG ("FRAT TRACER tracing addition of derived clause"); + if (with_antecedents) + frat_add_derived_clause (id, clause, chain); + else + frat_add_derived_clause (id, clause); +#ifndef QUIET + added++; +#endif +} + +void FratTracer::delete_clause (int64_t id, bool, + const vector &clause) { + if (file->closed ()) + return; + LOG ("FRAT TRACER tracing deletion of clause"); + frat_delete_clause (id, clause); +#ifndef QUIET + deleted++; +#endif +} + +void FratTracer::finalize_clause (int64_t id, const vector &clause) { + if (file->closed ()) + return; + LOG ("FRAT TRACER tracing finalization of clause"); + frat_finalize_clause (id, clause); +} + +/*------------------------------------------------------------------------*/ + +bool FratTracer::closed () { return file->closed (); } + +#ifndef QUIET + +void FratTracer::print_statistics () { + uint64_t bytes = file->bytes (); + uint64_t total = original + added + deleted + finalized; + MSG ("FRAT %" PRId64 " original clauses %.2f%%", original, + percent (original, total)); + MSG ("FRAT %" PRId64 " added clauses %.2f%%", added, + percent (added, total)); + MSG ("FRAT %" PRId64 " deleted clauses %.2f%%", deleted, + percent (deleted, total)); + MSG ("FRAT %" PRId64 " finalized clauses %.2f%%", finalized, + percent (finalized, total)); + MSG ("FRAT %" PRId64 " bytes (%.2f MB)", bytes, + bytes / (double) (1 << 20)); +} + +#endif + +void FratTracer::close (bool print) { + assert (!closed ()); + file->close (); +#ifndef QUIET + if (print) { + MSG ("FRAT proof file '%s' closed", file->name ()); + print_statistics (); + } +#else + (void) print; +#endif +} + +void FratTracer::flush (bool print) { + assert (!closed ()); + file->flush (); +#ifndef QUIET + if (print) { + MSG ("FRAT proof file '%s' flushed", file->name ()); + print_statistics (); + } +#else + (void) print; +#endif +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/frattracer.hpp b/src/sat/cadical/frattracer.hpp new file mode 100644 index 000000000..f9a1cccf3 --- /dev/null +++ b/src/sat/cadical/frattracer.hpp @@ -0,0 +1,62 @@ +#ifndef _frattracer_h_INCLUDED +#define _frattracer_h_INCLUDED + +namespace CaDiCaL { + +class FratTracer : public FileTracer { + + Internal *internal; + File *file; + bool binary; + bool with_antecedents; + +#ifndef QUIET + int64_t added, deleted; + int64_t finalized, original; +#endif + + vector delete_ids; + + void put_binary_zero (); + void put_binary_lit (int external_lit); + void put_binary_id (int64_t id, bool = false); + + // support FRAT + void frat_add_original_clause (int64_t, const vector &); + void frat_add_derived_clause (int64_t, const vector &); + void frat_add_derived_clause (int64_t, const vector &, + const vector &); + void frat_delete_clause (int64_t, const vector &); + void frat_finalize_clause (int64_t, const vector &); + +public: + // own and delete 'file' + FratTracer (Internal *, File *file, bool binary, bool antecedents); + ~FratTracer (); + + void connect_internal (Internal *i) override; + void begin_proof (int64_t) override {} // skip + + void add_original_clause (int64_t, bool, const vector &, + bool = false) override; + + void add_derived_clause (int64_t, bool, const vector &, + const vector &) override; + + void delete_clause (int64_t, bool, const vector &) override; + + void finalize_clause (int64_t, const vector &) override; + + void report_status (int, int64_t) override {} // skip + +#ifndef QUIET + void print_statistics (); +#endif + bool closed () override; + void close (bool) override; + void flush (bool) override; +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/gates.cpp b/src/sat/cadical/gates.cpp new file mode 100644 index 000000000..78c6d0a09 --- /dev/null +++ b/src/sat/cadical/gates.cpp @@ -0,0 +1,766 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// As in our original SATeLite published at SAT'05 we are trying to find +// gates in order to restrict the number of resolutions that need to be +// tried. If there is such a gate, we only need to consider resolvents +// among gate and one non-gate clauses. Resolvents between definitions will +// be tautological anyhow and resolvents among non-gates can actually be +// shown to be redundant too. + +/*------------------------------------------------------------------------*/ + +// The next function returns a non-zero if the clause 'c', which is assumed +// to contain the literal 'first', after removing falsified literals is a +// binary clause. Then the actual second literal is returned. + +int Internal::second_literal_in_binary_clause (Eliminator &eliminator, + Clause *c, int first) { + assert (!c->garbage); + int second = 0; + for (const auto &lit : *c) { + if (lit == first) + continue; + const signed char tmp = val (lit); + if (tmp < 0) + continue; + if (tmp > 0) { + mark_garbage (c); + elim_update_removed_clause (eliminator, c); + return 0; + } + if (second) { + second = INT_MIN; + break; + } + second = lit; + } + if (!second) + return 0; + if (second == INT_MIN) + return 0; + assert (active (second)); +#ifdef LOGGING + if (c->size == 2) + LOG (c, "found binary"); + else + LOG (c, "found actual binary %d %d", first, second); +#endif + return second; +} + +/*------------------------------------------------------------------------*/ + +// need a copy from above that does not care about garbage + +int Internal::second_literal_in_binary_clause_lrat (Clause *c, int first) { + if (c->garbage) + return 0; + int second = 0; + for (const auto &lit : *c) { + if (lit == first) + continue; + const signed char tmp = val (lit); + if (tmp < 0) + continue; + if (tmp > 0) + return 0; + if (!tmp) { + if (second) { + second = INT_MIN; + break; + } + second = lit; + } + } + if (!second) + return 0; + if (second == INT_MIN) + return 0; + return second; +} + +// I needed to find the second clause for hyper unary resolution to build +// LRAT this is not efficient but I could not find a better way then just +// finding the corresponding clause in all possible clauses +// +Clause *Internal::find_binary_clause (int first, int second) { + int best = first; + int other = second; + if (occs (first).size () > occs (second).size ()) { + best = second; + other = first; + } + for (auto c : occs (best)) + if (second_literal_in_binary_clause_lrat (c, best) == other) + return c; + return 0; +} + +/*------------------------------------------------------------------------*/ + +// Mark all other literals in binary clauses with 'first'. During this +// marking we might also detect hyper unary resolvents producing a unit. +// If such a unit is found we propagate it and return immediately. + +void Internal::mark_binary_literals (Eliminator &eliminator, int first) { + + if (unsat) + return; + if (val (first)) + return; + if (!eliminator.gates.empty ()) + return; + + assert (!marked (first)); + assert (eliminator.marked.empty ()); + + const Occs &os = occs (first); + for (const auto &c : os) { + if (c->garbage) + continue; + const int second = + second_literal_in_binary_clause (eliminator, c, first); + if (!second) + continue; + const int tmp = marked (second); + if (tmp < 0) { + // had a bug where units could occur multiple times here + // solved with flags + LOG ("found binary resolved unit %d", first); + if (lrat) { + Clause *d = find_binary_clause (first, -second); + assert (d); + for (auto &lit : *d) { + if (lit == first || lit == -second) + continue; + assert (val (lit) < 0); + Flags &f = flags (lit); + if (f.seen) + continue; + analyzed.push_back (lit); + f.seen = true; + int64_t id = unit_id (-lit); + lrat_chain.push_back (id); + // LOG ("gates added id %" PRId64, id); + } + for (auto &lit : *c) { + if (lit == first || lit == second) + continue; + assert (val (lit) < 0); + Flags &f = flags (lit); + if (f.seen) + continue; + analyzed.push_back (lit); + f.seen = true; + int64_t id = unit_id (-lit); + lrat_chain.push_back (id); + // LOG ("gates added id %" PRId64, id); + } + lrat_chain.push_back (c->id); + lrat_chain.push_back (d->id); + // LOG ("gates added id %" PRId64, c->id); + // LOG ("gates added id %" PRId64, d->id); + clear_analyzed_literals (); + } + assign_unit (first); + elim_propagate (eliminator, first); + return; + } + if (tmp > 0) { + LOG (c, "duplicated actual binary clause"); + elim_update_removed_clause (eliminator, c); + mark_garbage (c); + continue; + } + eliminator.marked.push_back (second); + mark (second); + LOG ("marked second literal %d in binary clause %d %d", second, first, + second); + } +} + +// Unmark all literals saved on the 'marked' stack. + +void Internal::unmark_binary_literals (Eliminator &eliminator) { + LOG ("unmarking %zd literals", eliminator.marked.size ()); + for (const auto &lit : eliminator.marked) + unmark (lit); + eliminator.marked.clear (); +} + +/*------------------------------------------------------------------------*/ + +// Find equivalence for 'pivot'. Requires that all other literals in binary +// clauses with 'pivot' are marked (through 'mark_binary_literals'); + +void Internal::find_equivalence (Eliminator &eliminator, int pivot) { + + if (!opts.elimequivs) + return; + + assert (opts.elimsubst); + + if (unsat) + return; + if (val (pivot)) + return; + if (!eliminator.gates.empty ()) + return; + + mark_binary_literals (eliminator, pivot); + if (unsat || val (pivot)) + goto DONE; + + for (const auto &c : occs (-pivot)) { + + if (c->garbage) + continue; + + const int second = + second_literal_in_binary_clause (eliminator, c, -pivot); + if (!second) + continue; + const int tmp = marked (second); + if (tmp > 0) { + LOG ("found binary resolved unit %d", second); + // did not find a bug where units could occur multiple times here + // still solved potential issues with flags + if (lrat) { + Clause *d = find_binary_clause (pivot, second); + assert (d); + for (auto &lit : *d) { + if (lit == pivot || lit == second) + continue; + assert (val (lit) < 0); + Flags &f = flags (lit); + if (f.seen) + continue; + analyzed.push_back (lit); + f.seen = true; + int64_t id = unit_id (-lit); + lrat_chain.push_back (id); + // LOG ("gates added id %" PRId64, id); + } + for (auto &lit : *c) { + if (lit == -pivot || lit == second) + continue; + assert (val (lit) < 0); + Flags &f = flags (lit); + if (f.seen) + continue; + analyzed.push_back (lit); + f.seen = true; + int64_t id = unit_id (-lit); + lrat_chain.push_back (id); + // LOG ("gates added id %" PRId64, id); + } + lrat_chain.push_back (c->id); + lrat_chain.push_back (d->id); + clear_analyzed_literals (); + // LOG ("gates added id %" PRId64, c->id); + // LOG ("gates added id %" PRId64, d->id); + } + assign_unit (second); + elim_propagate (eliminator, second); + if (val (pivot)) + break; + if (unsat) + break; + } + if (tmp >= 0) + continue; + + LOG ("found equivalence %d = %d", pivot, -second); + stats.elimequivs++; + stats.elimgates++; + + LOG (c, "first gate clause"); + assert (!c->gate); + c->gate = true; + eliminator.gates.push_back (c); + + Clause *d = 0; + const Occs &ps = occs (pivot); + for (const auto &e : ps) { + if (e->garbage) + continue; + const int other = + second_literal_in_binary_clause (eliminator, e, pivot); + if (other == -second) { + d = e; + break; + } + } + assert (d); + + LOG (d, "second gate clause"); + assert (!d->gate); + d->gate = true; + eliminator.gates.push_back (d); + eliminator.gatetype = EQUI; + + break; + } + +DONE: + unmark_binary_literals (eliminator); +} + +/*------------------------------------------------------------------------*/ + +// Find and gates for 'pivot' with a long clause, in which the pivot occurs +// positively. Requires that all other literals in binary clauses with +// 'pivot' are marked (through 'mark_binary_literals'); + +void Internal::find_and_gate (Eliminator &eliminator, int pivot) { + + if (!opts.elimands) + return; + + assert (opts.elimsubst); + + if (unsat) + return; + if (val (pivot)) + return; + if (!eliminator.gates.empty ()) + return; + + mark_binary_literals (eliminator, pivot); + if (unsat || val (pivot)) + goto DONE; + + for (const auto &c : occs (-pivot)) { + + if (c->garbage) + continue; + if (c->size < 3) + continue; + + bool all_literals_marked = true; + unsigned arity = 0; + int satisfied = 0; + + for (const auto &lit : *c) { + if (lit == -pivot) + continue; + assert (lit != pivot); + signed char tmp = val (lit); + if (tmp < 0) + continue; + if (tmp > 0) { + satisfied = lit; + break; + } + tmp = marked (lit); + if (tmp < 0) { + arity++; + continue; + } + all_literals_marked = false; + break; + } + + if (!all_literals_marked) + continue; + + if (satisfied) { + LOG (c, "satisfied by %d candidate base clause", satisfied); + mark_garbage (c); + continue; + } + +#ifdef LOGGING + if (opts.log) { + Logger::print_log_prefix (this); + tout.magenta (); + printf ("found arity %u AND gate %d = ", arity, -pivot); + bool first = true; + for (const auto &lit : *c) { + if (lit == -pivot) + continue; + assert (lit != pivot); + if (!first) + fputs (" & ", stdout); + printf ("%d", -lit); + first = false; + } + fputc ('\n', stdout); + tout.normal (); + fflush (stdout); + } +#endif + stats.elimands++; + stats.elimgates++; + eliminator.gatetype = AND; + + (void) arity; + assert (!c->gate); + c->gate = true; + eliminator.gates.push_back (c); + for (const auto &lit : *c) { + if (lit == -pivot) + continue; + assert (lit != pivot); + signed char tmp = val (lit); + if (tmp < 0) + continue; + assert (!tmp); + assert (marked (lit) < 0); + marks[vidx (lit)] *= 2; + } + + unsigned count = 0; + for (const auto &d : occs (pivot)) { + if (d->garbage) + continue; + const int other = + second_literal_in_binary_clause (eliminator, d, pivot); + if (!other) + continue; + const int tmp = marked (other); + if (tmp != 2) + continue; + LOG (d, "AND gate binary side clause"); + assert (!d->gate); + d->gate = true; + eliminator.gates.push_back (d); + count++; + } + assert (count >= arity); + (void) count; + + break; + } + +DONE: + unmark_binary_literals (eliminator); +} + +/*------------------------------------------------------------------------*/ + +// Find and extract ternary clauses. + +bool Internal::get_ternary_clause (Clause *d, int &a, int &b, int &c) { + if (d->garbage) + return false; + if (d->size < 3) + return false; + int found = 0; + a = b = c = 0; + for (const auto &lit : *d) { + if (val (lit)) + continue; + if (++found == 1) + a = lit; + else if (found == 2) + b = lit; + else if (found == 3) + c = lit; + else + return false; + } + return found == 3; +} + +// This function checks whether 'd' exists as ternary clause. + +bool Internal::match_ternary_clause (Clause *d, int a, int b, int c) { + if (d->garbage) + return false; + int found = 0; + for (const auto &lit : *d) { + if (val (lit)) + continue; + if (a != lit && b != lit && c != lit) + return false; + found++; + } + return found == 3; +} + +Clause *Internal::find_ternary_clause (int a, int b, int c) { + if (occs (b).size () > occs (c).size ()) + swap (b, c); + if (occs (a).size () > occs (b).size ()) + swap (a, b); + for (auto d : occs (a)) + if (match_ternary_clause (d, a, b, c)) + return d; + return 0; +} + +/*------------------------------------------------------------------------*/ + +// Find if-then-else gate. + +void Internal::find_if_then_else (Eliminator &eliminator, int pivot) { + + if (!opts.elimites) + return; + + assert (opts.elimsubst); + + if (unsat) + return; + if (val (pivot)) + return; + if (!eliminator.gates.empty ()) + return; + + const Occs &os = occs (pivot); + const auto end = os.end (); + for (auto i = os.begin (); i != end; i++) { + Clause *di = *i; + int ai, bi, ci; + if (!get_ternary_clause (di, ai, bi, ci)) + continue; + if (bi == pivot) + swap (ai, bi); + if (ci == pivot) + swap (ai, ci); + assert (ai == pivot); + for (auto j = i + 1; j != end; j++) { + Clause *dj = *j; + int aj, bj, cj; + if (!get_ternary_clause (dj, aj, bj, cj)) + continue; + if (bj == pivot) + swap (aj, bj); + if (cj == pivot) + swap (aj, cj); + assert (aj == pivot); + if (abs (bi) == abs (cj)) + swap (bj, cj); + if (abs (ci) == abs (cj)) + continue; + if (bi != -bj) + continue; + Clause *d1 = find_ternary_clause (-pivot, bi, -ci); + if (!d1) + continue; + Clause *d2 = find_ternary_clause (-pivot, bj, -cj); + if (!d2) + continue; + LOG (di, "1st if-then-else"); + LOG (dj, "2nd if-then-else"); + LOG (d1, "3rd if-then-else"); + LOG (d2, "4th if-then-else"); + LOG ("found ITE gate %d == (%d ? %d : %d)", pivot, -bi, -ci, -cj); + assert (!di->gate); + assert (!dj->gate); + assert (!d1->gate); + assert (!d2->gate); + di->gate = true; + dj->gate = true; + d1->gate = true; + d2->gate = true; + eliminator.gates.push_back (di); + eliminator.gates.push_back (dj); + eliminator.gates.push_back (d1); + eliminator.gates.push_back (d2); + stats.elimgates++; + stats.elimites++; + eliminator.gatetype = ITE; + return; + } + } +} + +/*------------------------------------------------------------------------*/ + +// Find and extract clause. + +bool Internal::get_clause (Clause *c, vector &l) { + if (c->garbage) + return false; + l.clear (); + for (const auto &lit : *c) { + if (val (lit) < 0) + continue; + if (val (lit) > 0) { + l.clear (); + return false; + } + l.push_back (lit); + } + return true; +} + +// Check whether 'c' contains only the literals in 'l'. + +bool Internal::is_clause (Clause *c, const vector &lits) { + if (c->garbage) + return false; + int size = lits.size (); + if (c->size < size) + return false; + int found = 0; + for (const auto &lit : *c) { + if (val (lit) < 0) + continue; + if (val (lit) > 0) + return false; + const auto it = find (lits.begin (), lits.end (), lit); + if (it == lits.end ()) + return false; + if (++found > size) + return false; + } + return found == size; +} + +Clause *Internal::find_clause (const vector &lits) { + int best = 0; + size_t len = 0; + for (const auto &lit : lits) { + size_t l = occs (lit).size (); + if (best && l >= len) + continue; + len = l, best = lit; + } + for (auto c : occs (best)) + if (is_clause (c, lits)) + return c; + return 0; +} + +void Internal::find_xor_gate (Eliminator &eliminator, int pivot) { + + if (!opts.elimxors) + return; + + assert (opts.elimsubst); + + if (unsat) + return; + if (val (pivot)) + return; + if (!eliminator.gates.empty ()) + return; + + vector lits; + + for (auto d : occs (pivot)) { + + if (!get_clause (d, lits)) + continue; + + const int size = lits.size (); // clause size + const int arity = size - 1; // arity of XOR + + if (size < 3) + continue; + if (arity > opts.elimxorlim) + continue; + + assert (eliminator.gates.empty ()); + + unsigned needed = (1u << arity) - 1; // additional clauses + unsigned signs = 0; // literals to negate + + do { + const unsigned prev = signs; + while (parity (++signs)) + ; + for (int j = 0; j < size; j++) { + const unsigned bit = 1u << j; + int lit = lits[j]; + if ((prev & bit) != (signs & bit)) + lits[j] = lit = -lit; + } + Clause *e = find_clause (lits); + if (!e) + break; + eliminator.gates.push_back (e); + } while (--needed); + + if (needed) { + eliminator.gates.clear (); + continue; + } + + eliminator.gates.push_back (d); + assert (eliminator.gates.size () == (1u << arity)); + +#ifdef LOGGING + if (opts.log) { + Logger::print_log_prefix (this); + tout.magenta (); + printf ("found arity %u XOR gate %d = ", arity, -pivot); + bool first = true; + for (const auto &lit : *d) { + if (lit == pivot) + continue; + assert (lit != -pivot); + if (!first) + fputs (" ^ ", stdout); + printf ("%d", lit); + first = false; + } + fputc ('\n', stdout); + tout.normal (); + fflush (stdout); + } +#endif + stats.elimgates++; + stats.elimxors++; + const auto end = eliminator.gates.end (); + auto j = eliminator.gates.begin (); + for (auto i = j; i != end; i++) { + Clause *e = *i; + if (e->gate) + continue; + e->gate = true; + LOG (e, "contributing"); + *j++ = e; + } + eliminator.gates.resize (j - eliminator.gates.begin ()); + eliminator.gatetype = XOR; + break; + } +} + +/*------------------------------------------------------------------------*/ + +// Find a gate for 'pivot'. If such a gate is found, the gate clauses are +// marked and pushed on the stack of gates. Further hyper unary resolution +// might detect units, which are propagated. This might assign the pivot or +// even produce the empty clause. + +void Internal::find_gate_clauses (Eliminator &eliminator, int pivot) { + if (!opts.elimsubst) + return; + + if (unsat) + return; + if (val (pivot)) + return; + + assert (eliminator.gates.empty ()); + + find_equivalence (eliminator, pivot); + find_and_gate (eliminator, pivot); + find_and_gate (eliminator, -pivot); + find_if_then_else (eliminator, pivot); + find_xor_gate (eliminator, pivot); + find_definition (eliminator, pivot); +} + +void Internal::unmark_gate_clauses (Eliminator &eliminator) { + LOG ("unmarking %zd gate clauses", eliminator.gates.size ()); + for (const auto &c : eliminator.gates) { + assert (c->gate); + c->gate = false; + } + eliminator.gates.clear (); + eliminator.definition_unit = 0; +} + +/*------------------------------------------------------------------------*/ + +} // namespace CaDiCaL diff --git a/src/sat/cadical/heap.hpp b/src/sat/cadical/heap.hpp new file mode 100644 index 000000000..f3f0ab0e4 --- /dev/null +++ b/src/sat/cadical/heap.hpp @@ -0,0 +1,212 @@ +#ifndef _heap_hpp_INCLUDED +#define _heap_hpp_INCLUDED + +#include "util.hpp" // Alphabetically after 'heap.hpp'. + +#include + +namespace CaDiCaL { + +using namespace std; + +// This is a priority queue with updates for unsigned integers implemented +// as binary heap. We need to map integer elements added (through +// 'push_back') to positions on the binary heap in 'array'. This map is +// stored in the 'pos' array. This approach is really wasteful (at least in +// terms of memory) if only few and a sparse set of integers is added. So +// it should not be used in this situation. A generic priority queue would +// implement the mapping externally provided by another template parameter. +// Since we use 'UINT_MAX' as 'not contained' flag, we can only have +// 'UINT_MAX - 1' elements in the heap. + +const unsigned invalid_heap_position = UINT_MAX; + +template class heap { + + vector array; // actual binary heap + vector pos; // positions of elements in array + C less; // less-than for elements + + // Map an element to its position entry in the 'pos' map. + // + unsigned &index (unsigned e) { + if (e >= pos.size ()) + pos.resize (1 + (size_t) e, invalid_heap_position); + unsigned &res = pos[e]; + assert (res == invalid_heap_position || (size_t) res < array.size ()); + return res; + } + + bool has_parent (unsigned e) { return index (e) > 0; } + bool has_left (unsigned e) { + return (size_t) 2 * index (e) + 1 < size (); + } + bool has_right (unsigned e) { + return (size_t) 2 * index (e) + 2 < size (); + } + + unsigned parent (unsigned e) { + assert (has_parent (e)); + return array[(index (e) - 1) / 2]; + } + + unsigned left (unsigned e) { + assert (has_left (e)); + return array[2 * index (e) + 1]; + } + + unsigned right (unsigned e) { + assert (has_right (e)); + return array[2 * index (e) + 2]; + } + + // Exchange elements 'a' and 'b' in 'array' and fix their positions. + // + void exchange (unsigned a, unsigned b) { + unsigned &i = index (a), &j = index (b); + swap (array[i], array[j]); + swap (i, j); + } + + // Bubble up an element as far as necessary. + // + void up (unsigned e) { + unsigned p; + while (has_parent (e) && less ((p = parent (e)), e)) + exchange (p, e); + } + + // Bubble down an element as far as necessary. + // + void down (unsigned e) { + while (has_left (e)) { + unsigned c = left (e); + if (has_right (e)) { + unsigned r = right (e); + if (less (c, r)) + c = r; + } + if (!less (e, c)) + break; + exchange (e, c); + } + } + + // Very expensive checker for the main 'heap' invariant. Can be enabled + // to find violations of antisymmetry in the client implementation of + // 'less' and as well of course bugs in this heap implementation. It + // should be enabled during testing applications of the heap. + // + void check () { +#if 0 // EXPENSIVE HEAP CHECKING IF ENABLED +#warning "expensive checking in heap enabled" + assert (array.size () <= invalid_heap_position); + for (size_t i = 0; i < array.size (); i++) { + size_t l = 2*i + 1, r = 2*i + 2; + if (l < array.size ()) assert (!less (array[i], array[l])); + if (r < array.size ()) assert (!less (array[i], array[r])); + assert (array[i] >= 0); + { + assert ((size_t) array[i] < pos.size ()); + assert (i == (size_t) pos[array[i]]); + } + } + for (size_t i = 0; i < pos.size (); i++) { + if (pos[i] == invalid_heap_position) continue; + assert (pos[i] < array.size ()); + assert (array[pos[i]] == (unsigned) i); + } +#endif + } + +public: + heap (const C &c) : less (c) {} + + // Number of elements in the heap. + // + size_t size () const { return array.size (); } + + // Check if no more elements are in the heap. + // + bool empty () const { return array.empty (); } + + // Check whether 'e' is already in the heap. + // + bool contains (unsigned e) const { + if ((size_t) e >= pos.size ()) + return false; + return pos[e] != invalid_heap_position; + } + + // Add a new (not contained) element 'e' to the heap. + // + void push_back (unsigned e) { + assert (!contains (e)); + size_t i = array.size (); + assert (i < (size_t) invalid_heap_position); + array.push_back (e); + index (e) = (unsigned) i; + up (e); + down (e); + check (); + } + + // Returns the maximum element in the heap. + // + unsigned front () const { + assert (!empty ()); + return array[0]; + } + + // Removes the maximum element in the heap. + // + unsigned pop_front () { + assert (!empty ()); + unsigned res = array[0], last = array.back (); + if (size () > 1) + exchange (res, last); + index (res) = invalid_heap_position; + array.pop_back (); + if (size () > 1) + down (last); + check (); + return res; + } + + // Notify the heap, that evaluation of 'less' has changed for 'e'. + // + void update (unsigned e) { + assert (contains (e)); + up (e); + down (e); + check (); + } + + void clear () { + array.clear (); + pos.clear (); + } + + void erase () { + erase_vector (array); + erase_vector (pos); + } + + void shrink () { + shrink_vector (array); + shrink_vector (pos); + } + + // Standard iterators 'inherited' from 'vector'. + // + typedef typename vector::iterator iterator; + typedef typename vector::const_iterator const_iterator; + iterator begin () { return array.begin (); } + iterator end () { return array.end (); } + const_iterator begin () const { return array.begin (); } + const_iterator end () const { return array.end (); } +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/idruptracer.cpp b/src/sat/cadical/idruptracer.cpp new file mode 100644 index 000000000..f4cc0f3f6 --- /dev/null +++ b/src/sat/cadical/idruptracer.cpp @@ -0,0 +1,566 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +IdrupTracer::IdrupTracer (Internal *i, File *f, bool b) + : internal (i), file (f), binary (b), num_clauses (0), size_clauses (0), + clauses (0), last_hash (0), last_id (0), last_clause (0) +#ifndef QUIET + , + added (0), deleted (0) +#endif +{ + (void) internal; + + // Initialize random number table for hash function. + // + Random random (42); + for (unsigned n = 0; n < num_nonces; n++) { + uint64_t nonce = random.next (); + if (!(nonce & 1)) + nonce++; + assert (nonce), assert (nonce & 1); + nonces[n] = nonce; + } +#ifndef NDEBUG + binary = b; +#else + (void) b; +#endif + piping = file->piping (); +} + +void IdrupTracer::connect_internal (Internal *i) { + internal = i; + file->connect_internal (internal); + LOG ("IDRUP TRACER connected to internal"); +} + +IdrupTracer::~IdrupTracer () { + LOG ("IDRUP TRACER delete"); + delete file; + for (size_t i = 0; i < size_clauses; i++) + for (IdrupClause *c = clauses[i], *next; c; c = next) + next = c->next, delete_clause (c); + delete[] clauses; +} + +/*------------------------------------------------------------------------*/ + +void IdrupTracer::enlarge_clauses () { + assert (num_clauses == size_clauses); + const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1; + LOG ("IDRUP Tracer enlarging clauses of tracer from %" PRIu64 + " to %" PRIu64, + (uint64_t) size_clauses, (uint64_t) new_size_clauses); + IdrupClause **new_clauses; + new_clauses = new IdrupClause *[new_size_clauses]; + clear_n (new_clauses, new_size_clauses); + for (uint64_t i = 0; i < size_clauses; i++) { + for (IdrupClause *c = clauses[i], *next; c; c = next) { + next = c->next; + const uint64_t h = reduce_hash (c->hash, new_size_clauses); + c->next = new_clauses[h]; + new_clauses[h] = c; + } + } + delete[] clauses; + clauses = new_clauses; + size_clauses = new_size_clauses; +} + +IdrupClause *IdrupTracer::new_clause () { + const size_t size = imported_clause.size (); + assert (size <= UINT_MAX); + const int off = size ? -1 : 0; + const size_t bytes = sizeof (IdrupClause) + (size - off) * sizeof (int); + IdrupClause *res = (IdrupClause *) new char[bytes]; + res->next = 0; + res->hash = last_hash; + res->id = last_id; + res->size = size; + int *literals = res->literals, *p = literals; + for (const auto &lit : imported_clause) { + *p++ = lit; + } + last_clause = res; + num_clauses++; + return res; +} + +void IdrupTracer::delete_clause (IdrupClause *c) { + assert (c); + num_clauses--; + delete[] (char *) c; +} + +uint64_t IdrupTracer::reduce_hash (uint64_t hash, uint64_t size) { + assert (size > 0); + unsigned shift = 32; + uint64_t res = hash; + while ((((uint64_t) 1) << shift) > size) { + res ^= res >> shift; + shift >>= 1; + } + res &= size - 1; + assert (res < size); + return res; +} + +uint64_t IdrupTracer::compute_hash (const int64_t id) { + assert (id > 0); + unsigned j = id % num_nonces; + uint64_t tmp = nonces[j] * (uint64_t) id; + return last_hash = tmp; +} + +bool IdrupTracer::find_and_delete (const int64_t id) { + if (!num_clauses) + return false; + IdrupClause **res = 0, *c; + const uint64_t hash = compute_hash (id); + const uint64_t h = reduce_hash (hash, size_clauses); + for (res = clauses + h; (c = *res); res = &c->next) { + if (c->hash == hash && c->id == id) { + break; + } + if (!c->next) + return false; + } + if (!c) + return false; + assert (c && res); + *res = c->next; + int *begin = c->literals; + for (size_t i = 0; i < c->size; i++) { + imported_clause.push_back (begin[i]); + } + delete_clause (c); + return true; +} + +void IdrupTracer::insert () { + if (num_clauses == size_clauses) + enlarge_clauses (); + const uint64_t h = reduce_hash (compute_hash (last_id), size_clauses); + IdrupClause *c = new_clause (); + c->next = clauses[h]; + clauses[h] = c; +} + +/*------------------------------------------------------------------------*/ + +inline void IdrupTracer::flush_if_piping () { + if (piping) + file->flush (); +} + +inline void IdrupTracer::put_binary_zero () { + assert (binary); + assert (file); + file->put ((unsigned char) 0); +} + +inline void IdrupTracer::put_binary_lit (int lit) { + assert (binary); + assert (file); + assert (lit != INT_MIN); + unsigned x = 2 * abs (lit) + (lit < 0); + unsigned char ch; + while (x & ~0x7f) { + ch = (x & 0x7f) | 0x80; + file->put (ch); + x >>= 7; + } + ch = x; + file->put (ch); +} + +inline void IdrupTracer::put_binary_id (int64_t id, bool can_be_negative) { + assert (binary); + assert (file); + uint64_t x = abs (id); + if (can_be_negative) { + x = 2 * x + (id < 0); + } + unsigned char ch; + while (x & ~0x7f) { + ch = (x & 0x7f) | 0x80; + file->put (ch); + x >>= 7; + } + ch = x; + file->put (ch); +} + +/*------------------------------------------------------------------------*/ + +void IdrupTracer::idrup_add_restored_clause (const vector &clause) { + if (binary) + file->put ('r'); + else + file->put ("r "); + for (const auto &external_lit : clause) + if (binary) + put_binary_lit (external_lit); + else + file->put (external_lit), file->put (' '); + if (binary) + put_binary_zero (); + else + file->put ("0\n"); + // flush_if_piping (); +} + +void IdrupTracer::idrup_add_derived_clause (const vector &clause) { + if (binary) + file->put ('l'); + else + file->put ("l "); + for (const auto &external_lit : clause) + if (binary) + put_binary_lit (external_lit); + else + file->put (external_lit), file->put (' '); + if (binary) + put_binary_zero (); + else + file->put ("0\n"); + // flush_if_piping (); +} + +void IdrupTracer::idrup_add_original_clause (const vector &clause) { + if (binary) + file->put ('i'); + else + file->put ("i "); + for (const auto &external_lit : clause) + if (binary) + put_binary_lit (external_lit); + else + file->put (external_lit), file->put (' '); + if (binary) + put_binary_zero (); + else + file->put ("0\n"); + // flush_if_piping (); +} + +void IdrupTracer::idrup_delete_clause (int64_t id, + const vector &clause) { + if (find_and_delete (id)) { + assert (imported_clause.empty ()); + if (binary) + file->put ('w'); + else + file->put ("w "); +#ifndef QUIET + weakened++; +#endif + } else { + if (binary) + file->put ('d'); + else + file->put ("d "); +#ifndef QUIET + deleted++; +#endif + } + for (const auto &external_lit : clause) + if (binary) + put_binary_lit (external_lit); + else + file->put (external_lit), file->put (' '); + if (binary) + put_binary_zero (); + else + file->put ("0\n"); + // flush_if_piping (); +} + +void IdrupTracer::idrup_conclude_and_delete ( + const vector &conclusion) { + uint64_t size = conclusion.size (); + if (size > 1) { + if (binary) { + file->put ('U'); + put_binary_id (size); + } else { + file->put ("U "); + file->put (size), file->put ("\n"); + } + } + for (auto &id : conclusion) { + if (binary) + file->put ('u'); + else + file->put ("u "); + (void) find_and_delete (id); + for (const auto &external_lit : imported_clause) { + // flip sign... + const auto not_elit = -external_lit; + if (binary) + put_binary_lit (not_elit); + else + file->put (not_elit), file->put (' '); + } + if (binary) + put_binary_zero (); + else + file->put ("0\n"); + imported_clause.clear (); + } + flush_if_piping (); +} + +void IdrupTracer::idrup_report_status (int status) { + if (binary) + file->put ('s'); + else + file->put ("s "); + if (status == SATISFIABLE) + file->put ("SATISFIABLE"); + else if (status == UNSATISFIABLE) + file->put ("UNSATISFIABLE"); + else + file->put ("UNKNOWN"); + if (!binary) + file->put ("\n"); + flush_if_piping (); +} + +void IdrupTracer::idrup_conclude_sat (const vector &model) { + if (binary) + file->put ('m'); + else + file->put ("m "); + for (auto &lit : model) { + if (binary) + put_binary_lit (lit); + else + file->put (lit), file->put (' '); + } + if (binary) + put_binary_zero (); + else + file->put ("0\n"); + flush_if_piping (); +} + +void IdrupTracer::idrup_conclude_unknown (const vector &trail) { + if (binary) + file->put ('e'); + else + file->put ("e "); + for (auto &lit : trail) { + if (binary) + put_binary_lit (lit); + else + file->put (lit), file->put (' '); + } + if (binary) + put_binary_zero (); + else + file->put ("0\n"); + flush_if_piping (); +} + +void IdrupTracer::idrup_solve_query () { + if (binary) + file->put ('q'); + else + file->put ("q "); + for (auto &lit : assumptions) { + if (binary) + put_binary_lit (lit); + else + file->put (lit), file->put (' '); + } + if (binary) + put_binary_zero (); + else + file->put ("0\n"); + flush_if_piping (); +} + +/*------------------------------------------------------------------------*/ + +void IdrupTracer::add_derived_clause (int64_t, bool, + const vector &clause, + const vector &) { + if (file->closed ()) + return; + assert (imported_clause.empty ()); + LOG (clause, "IDRUP TRACER tracing addition of derived clause"); + idrup_add_derived_clause (clause); +#ifndef QUIET + added++; +#endif +} + +void IdrupTracer::add_assumption_clause (int64_t id, + const vector &clause, + const vector &) { + if (file->closed ()) + return; + assert (imported_clause.empty ()); + LOG (clause, "IDRUP TRACER tracing addition of assumption clause"); + for (auto &lit : clause) + imported_clause.push_back (lit); + last_id = id; + insert (); + imported_clause.clear (); +} + +void IdrupTracer::delete_clause (int64_t id, bool, + const vector &clause) { + if (file->closed ()) + return; + assert (imported_clause.empty ()); + LOG ("IDRUP TRACER tracing deletion of clause[%" PRId64 "]", id); + idrup_delete_clause (id, clause); +} + +void IdrupTracer::weaken_minus (int64_t id, const vector &) { + if (file->closed ()) + return; + assert (imported_clause.empty ()); + LOG ("IDRUP TRACER tracing weaken minus of clause[%" PRId64 "]", id); + last_id = id; + insert (); +#ifndef QUIET + weakened++; +#endif +} + +void IdrupTracer::conclude_unsat (ConclusionType, + const vector &conclusion) { + if (file->closed ()) + return; + assert (imported_clause.empty ()); + LOG (conclusion, "IDRUP TRACER tracing conclusion of clause(s)"); + idrup_conclude_and_delete (conclusion); +} + +void IdrupTracer::add_original_clause (int64_t id, bool, + const vector &clause, + bool restored) { + if (file->closed ()) + return; + if (!restored) { + LOG (clause, "IDRUP TRACER tracing addition of original clause"); +#ifndef QUIET + original++; +#endif + return idrup_add_original_clause (clause); + } + assert (restored); + if (find_and_delete (id)) { + LOG (clause, + "IDRUP TRACER the clause was not yet weakened, so no restore"); + return; + } + LOG (clause, "IDRUP TRACER tracing addition of restored clause"); + idrup_add_restored_clause (clause); +#ifndef QUIET + restore++; +#endif +} + +void IdrupTracer::report_status (int status, int64_t) { + if (file->closed ()) + return; + LOG ("IDRUP TRACER tracing report of status %d", status); + idrup_report_status (status); +} + +void IdrupTracer::conclude_sat (const vector &model) { + if (file->closed ()) + return; + LOG (model, "IDRUP TRACER tracing conclusion of model"); + idrup_conclude_sat (model); +} + +void IdrupTracer::conclude_unknown (const vector &trail) { + if (file->closed ()) + return; + LOG (trail, "IDRUP TRACER tracing conclusion of unknown state"); + idrup_conclude_unknown (trail); +} + +void IdrupTracer::solve_query () { + if (file->closed ()) + return; + LOG (assumptions, "IDRUP TRACER tracing solve query with assumptions"); + idrup_solve_query (); +#ifndef QUIET + solved++; +#endif +} + +void IdrupTracer::add_assumption (int lit) { + LOG ("IDRUP TRACER tracing addition of assumption %d", lit); + assumptions.push_back (lit); +} + +void IdrupTracer::reset_assumptions () { + LOG (assumptions, "IDRUP TRACER tracing reset of assumptions"); + assumptions.clear (); +} + +/*------------------------------------------------------------------------*/ + +bool IdrupTracer::closed () { return file->closed (); } + +#ifndef QUIET + +void IdrupTracer::print_statistics () { + // TODO complete this. + uint64_t bytes = file->bytes (); + uint64_t total = added + deleted + weakened + restore + original; + MSG ("LIDRUP %" PRId64 " original clauses %.2f%%", original, + percent (original, total)); + MSG ("LIDRUP %" PRId64 " learned clauses %.2f%%", added, + percent (added, total)); + MSG ("LIDRUP %" PRId64 " deleted clauses %.2f%%", deleted, + percent (deleted, total)); + MSG ("LIDRUP %" PRId64 " weakened clauses %.2f%%", weakened, + percent (weakened, total)); + MSG ("LIDRUP %" PRId64 " restored clauses %.2f%%", restore, + percent (restore, total)); + MSG ("LIDRUP %" PRId64 " queries %.2f", solved, relative (solved, total)); + MSG ("IDRUP %" PRId64 " bytes (%.2f MB)", bytes, + bytes / (double) (1 << 20)); +} + +#endif + +void IdrupTracer::close (bool print) { + assert (!closed ()); + file->close (); +#ifndef QUIET + if (print) { + MSG ("IDRUP proof file '%s' closed", file->name ()); + print_statistics (); + } +#else + (void) print; +#endif +} + +void IdrupTracer::flush (bool print) { + assert (!closed ()); + file->flush (); +#ifndef QUIET + if (print) { + MSG ("IDRUP proof file '%s' flushed", file->name ()); + print_statistics (); + } +#else + (void) print; +#endif +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/idruptracer.hpp b/src/sat/cadical/idruptracer.hpp new file mode 100644 index 000000000..0b43e7a9c --- /dev/null +++ b/src/sat/cadical/idruptracer.hpp @@ -0,0 +1,110 @@ +#ifndef _idruptracer_h_INCLUDED +#define _idruptracer_h_INCLUDED + +class FileTracer; + +namespace CaDiCaL { + +struct IdrupClause { + IdrupClause *next; // collision chain link for hash table + uint64_t hash; // previously computed full 64-bit hash + int64_t id; // id of clause + unsigned size; + int literals[1]; +}; + +class IdrupTracer : public FileTracer { + + Internal *internal; + File *file; + bool binary; + bool piping; // The 'file' is a pipe and needs eagerly flushing. + + // hash table for conclusion + // + uint64_t num_clauses; // number of clauses in hash table + uint64_t size_clauses; // size of clause hash table + IdrupClause **clauses; // hash table of clauses + vector imported_clause; + vector assumptions; + + static const unsigned num_nonces = 4; + + uint64_t nonces[num_nonces]; // random numbers for hashing + uint64_t last_hash; // last computed hash value of clause + int64_t last_id; // id of the last added clause + IdrupClause *last_clause; + uint64_t compute_hash (int64_t); // compute and save hash value of clause + + IdrupClause *new_clause (); + void delete_clause (IdrupClause *); + + static uint64_t reduce_hash (uint64_t hash, uint64_t size); + + void enlarge_clauses (); // enlarge hash table for clauses + void insert (); // insert clause in hash table + bool + find_and_delete (const int64_t); // find clause position in hash table + +#ifndef QUIET + int64_t added, deleted, weakened, restore, original, solved; +#endif + + void flush_if_piping (); + + void put_binary_zero (); + void put_binary_lit (int external_lit); + void put_binary_id (int64_t id, bool = false); + + void idrup_add_derived_clause (const vector &clause); + void idrup_delete_clause (int64_t id, const vector &clause); + void idrup_add_restored_clause (const vector &clause); + void idrup_add_original_clause (const vector &clause); + void idrup_conclude_and_delete (const vector &conclusion); + void idrup_report_status (int status); + void idrup_conclude_sat (const vector &model); + void idrup_conclude_unknown (const vector &trail); + void idrup_solve_query (); + +public: + IdrupTracer (Internal *, File *file, bool); + ~IdrupTracer (); + + // proof section: + void add_derived_clause (int64_t, bool, const vector &, + const vector &) override; + void add_assumption_clause (int64_t, const vector &, + const vector &) override; + void weaken_minus (int64_t, const vector &) override; + void delete_clause (int64_t, bool, const vector &) override; + void add_original_clause (int64_t, bool, const vector &, + bool = false) override; + void report_status (int, int64_t) override; + void conclude_sat (const vector &) override; + void conclude_unsat (ConclusionType, const vector &) override; + void conclude_unknown (const vector &) override; + + void solve_query () override; + void add_assumption (int) override; + void reset_assumptions () override; + + // skip + void begin_proof (int64_t) override {} + void finalize_clause (int64_t, const vector &) override {} + void strengthen (int64_t) override {} + void add_constraint (const vector &) override {} + + // logging and file io + void connect_internal (Internal *i) override; + +#ifndef QUIET + void print_statistics (); +#endif + bool closed () override; + void close (bool) override; + void flush (bool) override; +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/instantiate.cpp b/src/sat/cadical/instantiate.cpp new file mode 100644 index 000000000..494686b81 --- /dev/null +++ b/src/sat/cadical/instantiate.cpp @@ -0,0 +1,365 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// This provides an implementation of variable instantiation, a technique +// for removing literals with few occurrence (see also 'instantiate.hpp'). + +/*------------------------------------------------------------------------*/ + +// Triggered at the end of a variable elimination round ('elim_round'). + +void Internal::collect_instantiation_candidates ( + Instantiator &instantiator) { + assert (occurring ()); + for (auto idx : vars) { + if (frozen (idx)) + continue; + if (!active (idx)) + continue; + if (flags (idx).elim) + continue; // BVE attempt pending + for (int sign = -1; sign <= 1; sign += 2) { + const int lit = sign * idx; + if (noccs (lit) > opts.instantiateocclim) + continue; + Occs &os = occs (lit); + for (const auto &c : os) { + if (c->garbage) + continue; + if (opts.instantiateonce && c->instantiated) + continue; + if (c->size < opts.instantiateclslim) + continue; + bool satisfied = false; + int unassigned = 0; + for (const auto &other : *c) { + const signed char tmp = val (other); + if (tmp > 0) + satisfied = true; + if (!tmp) + unassigned++; + } + if (satisfied) + continue; + if (unassigned < 3) + continue; // avoid learning units + size_t negoccs = occs (-lit).size (); + LOG (c, + "instantiation candidate literal %d " + "with %zu negative occurrences in", + lit, negoccs); + instantiator.candidate (lit, c, c->size, negoccs); + } + } + } +} + +/*------------------------------------------------------------------------*/ + +// Specialized propagation and assignment routines for instantiation. + +inline void Internal::inst_assign (int lit) { + LOG ("instantiate assign %d", lit); + assert (!val (lit)); + assert ((int) num_assigned < max_var); + num_assigned++; + set_val (lit, 1); + trail.push_back (lit); +} + +// Conflict analysis is only needed to do valid resolution proofs. +// We remember propagated clauses in order of assignment (in inst_chain) +// which allows us to do a variant of conflict analysis if the instantiation +// attempt succeeds. +// +bool Internal::inst_propagate () { // Adapted from 'propagate'. + START (propagate); + int64_t before = propagated; + bool ok = true; + while (ok && propagated != trail.size ()) { + const int lit = -trail[propagated++]; + LOG ("instantiate propagating %d", -lit); + Watches &ws = watches (lit); + const const_watch_iterator eow = ws.end (); + const_watch_iterator i = ws.begin (); + watch_iterator j = ws.begin (); + while (i != eow) { + const Watch w = *j++ = *i++; + const signed char b = val (w.blit); + if (b > 0) + continue; + if (w.binary ()) { + if (b < 0) { + ok = false; + LOG (w.clause, "conflict"); + if (lrat) { + inst_chain.push_back (w.clause); + } + break; + } else { + if (lrat) { + inst_chain.push_back (w.clause); + } + inst_assign (w.blit); + } + } else { + literal_iterator lits = w.clause->begin (); + const int other = lits[0] ^ lits[1] ^ lit; + lits[0] = other, lits[1] = lit; + const signed char u = val (other); + if (u > 0) + j[-1].blit = other; + else { + const int size = w.clause->size; + const const_literal_iterator end = lits + size; + const literal_iterator middle = lits + w.clause->pos; + literal_iterator k = middle; + signed char v = -1; + int r = 0; + while (k != end && (v = val (r = *k)) < 0) + k++; + if (v < 0) { + k = lits + 2; + assert (w.clause->pos <= size); + while (k != middle && (v = val (r = *k)) < 0) + k++; + } + w.clause->pos = k - lits; + assert (lits + 2 <= k), assert (k <= w.clause->end ()); + if (v > 0) { + j[-1].blit = r; + } else if (!v) { + LOG (w.clause, "unwatch %d in", r); + lits[1] = r; + *k = lit; + watch_literal (r, lit, w.clause); + j--; + } else if (!u) { + assert (v < 0); + if (lrat) { + inst_chain.push_back (w.clause); + } + inst_assign (other); + } else { + assert (u < 0); + assert (v < 0); + if (lrat) { + inst_chain.push_back (w.clause); + } + LOG (w.clause, "conflict"); + ok = false; + break; + } + } + } + } + if (j != i) { + while (i != eow) + *j++ = *i++; + ws.resize (j - ws.begin ()); + } + } + int64_t delta = propagated - before; + stats.propagations.instantiate += delta; + STOP (propagate); + return ok; +} + +/*------------------------------------------------------------------------*/ + +// This is the instantiation attempt. + +bool Internal::instantiate_candidate (int lit, Clause *c) { + stats.instried++; + if (c->garbage) + return false; + assert (!level); + bool found = false, satisfied = false, inactive = false; + int unassigned = 0; + for (const auto &other : *c) { + if (other == lit) + found = true; + const signed char tmp = val (other); + if (tmp > 0) { + satisfied = true; + break; + } + if (!tmp && !active (other)) { + inactive = true; + break; + } + if (!tmp) + unassigned++; + } + if (!found) + return false; + if (inactive) + return false; + if (satisfied) + return false; + if (unassigned < 3) + return false; + size_t before = trail.size (); + assert (propagated == before); + assert (active (lit)); + assert (inst_chain.empty ()); + LOG (c, "trying to instantiate %d in", lit); + assert (!c->garbage); + c->instantiated = true; + assert (lrat_chain.empty ()); + level++; + inst_assign (lit); // Assume 'lit' to true. + for (const auto &other : *c) { + if (other == lit) + continue; + const signed char tmp = val (other); + if (tmp) { + assert (tmp < 0); + continue; + } + inst_assign (-other); // Assume other to false. + } + bool ok = inst_propagate (); // Propagate. + assert (lrat_chain.empty ()); // chain will be built here + if (ok) { + inst_chain.clear (); + } else if (lrat) { // analyze conflict for lrat + assert (inst_chain.size ()); + Clause *reason = inst_chain.back (); + inst_chain.pop_back (); + lrat_chain.push_back (reason->id); + for (const auto &other : *reason) { + Flags &f = flags (other); + assert (!f.seen); + f.seen = true; + analyzed.push_back (other); + } + } + while (trail.size () > before) { // Backtrack. + const int other = trail.back (); + LOG ("instantiate unassign %d", other); + trail.pop_back (); + assert (val (other) > 0); + num_assigned--; + set_val (other, 0); + // this is a variant of conflict analysis which is only needed for lrat + if (!ok && inst_chain.size () && lrat) { + Flags &f = flags (other); + if (f.seen) { + Clause *reason = inst_chain.back (); + lrat_chain.push_back (reason->id); + for (const auto &other : *reason) { + Flags &f = flags (other); + if (f.seen) + continue; + f.seen = true; + analyzed.push_back (other); + } + f.seen = false; + } + inst_chain.pop_back (); + } + } + assert (inst_chain.empty ()); + // post processing step for lrat + if (!ok && lrat) { + if (flags (lit).seen) + lrat_chain.push_back (c->id); + for (const auto &other : *c) { + Flags &f = flags (other); + f.seen = false; + } + for (int other : analyzed) { + Flags &f = flags (other); + if (!f.seen) { + f.seen = true; + continue; + } + int64_t id = unit_id (-other); + lrat_chain.push_back (id); + } + clear_analyzed_literals (); + reverse (lrat_chain.begin (), lrat_chain.end ()); + } + assert (analyzed.empty ()); + propagated = before; + assert (level == 1); + level = 0; + if (ok) { + assert (lrat_chain.empty ()); + LOG ("instantiation failed"); + return false; + } + unwatch_clause (c); + LOG (lrat_chain, "instantiate proof chain"); + strengthen_clause (c, lit); + watch_clause (c); + lrat_chain.clear (); + assert (c->size > 1); + LOG ("instantiation succeeded"); + stats.instantiated++; + return true; +} + +/*------------------------------------------------------------------------*/ + +// Try to instantiate all candidates collected before through the +// 'collect_instantiation_candidates' routine. + +void Internal::instantiate (Instantiator &instantiator) { + assert (opts.instantiate); + START (instantiate); + stats.instrounds++; +#ifndef QUIET + const int64_t candidates = instantiator.candidates.size (); + int64_t tried = 0; +#endif + int64_t instantiated = 0; + init_watches (); + connect_watches (); + if (propagated < trail.size ()) { + if (!propagate ()) { + LOG ("propagation after connecting watches failed"); + learn_empty_clause (); + assert (unsat); + } + } + PHASE ("instantiate", stats.instrounds, + "attempting to instantiate %" PRId64 + " candidate literal clause pairs", + candidates); + while (!unsat && !terminated_asynchronously () && + !instantiator.candidates.empty ()) { + Instantiator::Candidate cand = instantiator.candidates.back (); + instantiator.candidates.pop_back (); +#ifndef QUIET + tried++; +#endif + if (!active (cand.lit)) + continue; + LOG (cand.clause, + "trying to instantiate %d with " + "%zd negative occurrences in", + cand.lit, cand.negoccs); + if (!instantiate_candidate (cand.lit, cand.clause)) + continue; + instantiated++; + VERBOSE (2, + "instantiation %" PRId64 " (%.1f%%) succeeded " + "(%.1f%%) with %zd negative occurrences in size %d clause", + tried, percent (tried, candidates), + percent (instantiated, tried), cand.negoccs, cand.size); + } + PHASE ("instantiate", stats.instrounds, + "instantiated %" PRId64 " candidate successfully " + "out of %" PRId64 " tried %.1f%%", + instantiated, tried, percent (instantiated, tried)); + report ('I', !instantiated); + reset_watches (); + STOP (instantiate); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/instantiate.hpp b/src/sat/cadical/instantiate.hpp new file mode 100644 index 000000000..fef7532e7 --- /dev/null +++ b/src/sat/cadical/instantiate.hpp @@ -0,0 +1,45 @@ +#ifndef _instantiate_hpp_INCLUDED +#define _instantiate_hpp_INCLUDED + +namespace CaDiCaL { + +// We are trying to remove literals in clauses, which occur in few clauses +// and further restrict this removal to variables for which variable +// elimination failed. Thus if for instance we succeed in removing the +// single occurrence of a literal, pure literal elimination can +// eliminate the corresponding variable in the next variable elimination +// round. The set of such literal clause candidate pairs is collected at +// the end of a variable elimination round and tried before returning. The +// name of this technique is inspired by 'variable instantiation' as +// described in [AnderssonBjesseCookHanna-DAC'02] and apparently +// successfully used in the 'Oepir' SAT solver. + +struct Clause; +struct Internal; + +class Instantiator { + + friend struct Internal; + + struct Candidate { + int lit; + int size; + size_t negoccs; + Clause *clause; + Candidate (int l, Clause *c, int s, size_t n) + : lit (l), size (s), negoccs (n), clause (c) {} + }; + + vector candidates; + +public: + void candidate (int l, Clause *c, int s, size_t n) { + candidates.push_back (Candidate (l, c, s, n)); + } + + operator bool () const { return !candidates.empty (); } +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/internal.cpp b/src/sat/cadical/internal.cpp new file mode 100644 index 000000000..4ef1df058 --- /dev/null +++ b/src/sat/cadical/internal.cpp @@ -0,0 +1,1190 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ +static Clause external_reason_clause; + +Internal::Internal () + : mode (SEARCH), unsat (false), iterating (false), + localsearching (false), lookingahead (false), preprocessing (false), + protected_reasons (false), force_saved_phase (false), + searching_lucky_phases (false), stable (false), reported (false), + external_prop (false), did_external_prop (false), + external_prop_is_lazy (true), forced_backt_allowed (false), + + private_steps (false), rephased (0), vsize (0), max_var (0), + clause_id (0), original_id (0), reserved_ids (0), conflict_id (0), + saved_decisions (0), concluded (false), lrat (false), frat (false), + level (0), vals (0), score_inc (1.0), scores (this), conflict (0), + ignore (0), external_reason (&external_reason_clause), + newest_clause (0), force_no_backtrack (false), + from_propagator (false), ext_clause_forgettable (false), + tainted_literal (0), notified (0), probe_reason (0), propagated (0), + propagated2 (0), propergated (0), best_assigned (0), + target_assigned (0), no_conflict_until (0), unsat_constraint (false), + marked_failed (true), sweep_incomplete (false), citten (0), + num_assigned (0), proof (0), opts (this), +#ifndef QUIET + profiles (this), force_phase_messages (false), +#endif + arena (this), prefix ("c "), internal (this), external (0), + termination_forced (false), vars (this->max_var), + lits (this->max_var) { + control.push_back (Level (0, 0)); + + // The 'dummy_binary' is used in 'try_to_subsume_clause' to fake a real + // clause, which then can be used to subsume or strengthen the given + // clause in one routine for both binary and non binary clauses. This + // fake binary clause is always kept non-redundant (and not-moved etc.) + // due to the following 'memset'. Only literals will be changed. + + // In a previous version we used local automatic allocated 'Clause' on the + // stack, which became incompatible with several compilers (see the + // discussion on flexible array member in 'Clause.cpp'). + + size_t bytes = Clause::bytes (2); + dummy_binary = (Clause *) new char[bytes]; + memset (dummy_binary, 0, bytes); + dummy_binary->size = 2; +} + +Internal::~Internal () { + // If a memory exception ocurred a profile might still be active. +#ifndef QUIET +#define PROFILE(NAME, LEVEL) \ + if (PROFILE_ACTIVE (NAME)) \ + STOP (NAME); + PROFILES +#undef PROFILE +#endif + delete[] (char *) dummy_binary; + for (const auto &c : clauses) + delete_clause (c); + if (proof) + delete proof; + for (auto &tracer : tracers) + delete tracer; + for (auto &filetracer : file_tracers) + delete filetracer; + for (auto &stattracer : stat_tracers) + delete stattracer; + if (vals) { + vals -= vsize; + delete[] vals; + } +} + +/*------------------------------------------------------------------------*/ + +// Values in 'vals' can be accessed in the range '[-max_var,max_var]' that +// is directly by a literal. This is crucial for performance. By shifting +// the start of 'vals' appropriately, we achieve that negative offsets from +// the start of 'vals' can be used. We also need to set both values at +// 'lit' and '-lit' during assignments. In MiniSAT integer literals are +// encoded, using the least significant bit as negation. This avoids taking +// the 'abs ()' (as in our solution) and thus also avoids a branch in the +// hot-spot of the solver (clause traversal in propagation). That solution +// requires another (branch less) negation of the values though and +// debugging is harder since literals occur only encoded in clauses. +// The main draw-back of our solution is that we have to shift the memory +// and access it through negative indices, which looks less clean (but still +// as far I can tell is properly defined C / C++). You might get a warning +// by static analyzers though. Clang with '--analyze' thought that this +// idiom would generate a memory leak thus we use the following dummy. + +static signed char *ignore_clang_analyze_memory_leak_warning; + +void Internal::enlarge_vals (size_t new_vsize) { + signed char *new_vals; + const size_t bytes = 2u * new_vsize; + new_vals = new signed char[bytes]; // g++-4.8 does not like ... { 0 }; + memset (new_vals, 0, bytes); + ignore_clang_analyze_memory_leak_warning = new_vals; + new_vals += new_vsize; + + if (vals) { + memcpy (new_vals - max_var, vals - max_var, 2u * max_var + 1u); + vals -= vsize; + delete[] vals; + } else + assert (!vsize); + vals = new_vals; +} + +/*------------------------------------------------------------------------*/ + +void Internal::enlarge (int new_max_var) { + // New variables can be created that can invoke enlarge anytime (via calls + // during ipasir-up call-backs), thus assuming (!level) is not correct + size_t new_vsize = vsize ? 2 * vsize : 1 + (size_t) new_max_var; + while (new_vsize <= (size_t) new_max_var) + new_vsize *= 2; + LOG ("enlarge internal size from %zd to new size %zd", vsize, new_vsize); + // Ordered in the size of allocated memory (larger block first). + if (lrat || frat) + enlarge_zero (unit_clauses_idx, 2 * new_vsize); + enlarge_only (wtab, 2 * new_vsize); + enlarge_only (vtab, new_vsize); + enlarge_zero (parents, new_vsize); + enlarge_only (links, new_vsize); + enlarge_zero (btab, new_vsize); + enlarge_zero (gtab, new_vsize); + enlarge_zero (stab, new_vsize); + enlarge_init (ptab, 2 * new_vsize, -1); + enlarge_only (ftab, new_vsize); + enlarge_vals (new_vsize); + vsize = new_vsize; + if (external) + enlarge_zero (relevanttab, new_vsize); + const signed char val = opts.phase ? 1 : -1; + enlarge_init (phases.saved, new_vsize, val); + enlarge_zero (phases.forced, new_vsize); + enlarge_zero (phases.target, new_vsize); + enlarge_zero (phases.best, new_vsize); + enlarge_zero (phases.prev, new_vsize); + enlarge_zero (phases.min, new_vsize); + enlarge_zero (marks, new_vsize); +} + +void Internal::init_vars (int new_max_var) { + if (new_max_var <= max_var) + return; + // New variables can be created that can invoke enlarge anytime (via calls + // during ipasir-up call-backs), thus assuming (!level) is not correct + LOG ("initializing %d internal variables from %d to %d", + new_max_var - max_var, max_var + 1, new_max_var); + if ((size_t) new_max_var >= vsize) + enlarge (new_max_var); +#ifndef NDEBUG + for (int64_t i = -new_max_var; i < -max_var; i++) + assert (!vals[i]); + for (unsigned i = max_var + 1; i <= (unsigned) new_max_var; i++) + assert (!vals[i]), assert (!btab[i]), assert (!gtab[i]); + for (uint64_t i = 2 * ((uint64_t) max_var + 1); + i <= 2 * (uint64_t) new_max_var + 1; i++) + assert (ptab[i] == -1); +#endif + assert (!btab[0]); + int old_max_var = max_var; + max_var = new_max_var; + init_queue (old_max_var, new_max_var); + init_scores (old_max_var, new_max_var); + int initialized = new_max_var - old_max_var; + stats.vars += initialized; + stats.unused += initialized; + stats.inactive += initialized; + LOG ("finished initializing %d internal variables", initialized); +} + +void Internal::add_original_lit (int lit) { + assert (abs (lit) <= max_var); + if (lit) { + original.push_back (lit); + } else { + const int64_t id = + original_id < reserved_ids ? ++original_id : ++clause_id; + if (proof) { + // Use the external form of the clause for printing in proof + // Externalize(internalized literal) != external literal + assert (!original.size () || !external->eclause.empty ()); + proof->add_external_original_clause (id, false, external->eclause); + } + if (internal->opts.check && + (internal->opts.checkwitness || internal->opts.checkfailed)) { + bool forgettable = from_propagator && ext_clause_forgettable; + if (forgettable && opts.check) { + assert (!original.size () || !external->eclause.empty ()); + + // First integer is the presence-flag (even if the clause is empty) + external->forgettable_original[id] = {1}; + + for (auto const &elit : external->eclause) + external->forgettable_original[id].push_back (elit); + + LOG (external->eclause, + "clause added to external forgettable map:"); + } + } + + add_new_original_clause (id); + original.clear (); + } +} + +void Internal::finish_added_clause_with_id (int64_t id, bool restore) { + if (proof) { + // Use the external form of the clause for printing in proof + // Externalize(internalized literal) != external literal + assert (!original.size () || !external->eclause.empty ()); + proof->add_external_original_clause (id, false, external->eclause, + restore); + } + add_new_original_clause (id); + original.clear (); +} + +/*------------------------------------------------------------------------*/ + +void Internal::reserve_ids (int number) { + // return; + LOG ("reserving %d ids", number); + assert (number >= 0); + assert (!clause_id && !reserved_ids && !original_id); + clause_id = reserved_ids = number; + if (proof) + proof->begin_proof (reserved_ids); +} + +/*------------------------------------------------------------------------*/ + +#ifdef PROFILE_MODE + +// Separating these makes it easier to profile stable and unstable search. + +bool Internal::propagate_wrapper () { + if (stable) + return propagate_stable (); + else + return propagate_unstable (); +} + +void Internal::analyze_wrapper () { + if (stable) + analyze_stable (); + else + analyze_unstable (); +} + +int Internal::decide_wrapper () { + if (stable) + return decide_stable (); + else + return decide_unstable (); +} + +#endif + +/*------------------------------------------------------------------------*/ + +// This is the main CDCL loop with interleaved inprocessing. + +int Internal::cdcl_loop_with_inprocessing () { + + int res = 0; + + START (search); + + if (stable) { + START (stable); + report ('['); + } else { + START (unstable); + report ('{'); + } + + while (!res) { + if (unsat) + res = 20; + else if (unsat_constraint) + res = 20; + else if (!propagate_wrapper ()) + analyze_wrapper (); // propagate and analyze + else if (iterating) + iterate (); // report learned unit + else if (!external_propagate () || unsat) { // external propagation + if (unsat) + continue; + else + analyze (); + } else if (satisfied ()) { // found model + if (!external_check_solution () || unsat) { + if (unsat) + continue; + else + analyze (); + } else if (satisfied ()) + res = 10; + } else if (search_limits_hit ()) + break; // decision or conflict limit + else if (terminated_asynchronously ()) // externally terminated + break; + else if (restarting ()) + restart (); // restart by backtracking + else if (rephasing ()) + rephase (); // reset variable phases + else if (reducing ()) + reduce (); // collect useless clauses + else if (inprobing ()) + inprobe (); // schedule of inprocessing + else if (ineliminating ()) + elim (); // variable elimination + else if (compacting ()) + compact (); // collect variables + else if (conditioning ()) + condition (); // globally blocked clauses + else + res = decide (); // next decision + } + + if (stable) { + STOP (stable); + report (']'); + } else { + STOP (unstable); + report ('}'); + } + + STOP (search); + + return res; +} + +int Internal::propagate_assumptions () { + if (proof) + proof->solve_query (); + if (opts.ilb) { + if (opts.ilbassumptions) + sort_and_reuse_assumptions (); + stats.ilbtriggers++; + stats.ilbsuccess += (level > 0); + stats.levelsreused += level; + if (level) { + assert (control.size () > 1); + stats.literalsreused += num_assigned - control[1].trail; + } + } + init_search_limits (); + init_report_limits (); + + int res = already_solved (); // root-level propagation is done here + + int last_assumption_level = assumptions.size (); + if (constraint.size ()) + last_assumption_level++; + + if (!res) { + restore_clauses (); + while (!res) { + if (unsat) + res = 20; + else if (unsat_constraint) + res = 20; + else if (!propagate ()) { + // let analyze run to get failed assumptions + analyze (); + } else if (!external_propagate () || unsat) { // external propagation + if (unsat) + continue; + else + analyze (); + } else if (satisfied ()) { // found model + if (!external_check_solution () || unsat) { + if (unsat) + continue; + else + analyze (); + } else if (satisfied ()) + res = 10; + } else if (search_limits_hit ()) + break; // decision or conflict limit + else if (terminated_asynchronously ()) // externally terminated + break; + else { + if (level >= last_assumption_level) + break; + res = decide (); + } + } + } + + if (unsat || unsat_constraint) + res = 20; + + if (!res && satisfied ()) + res = 10; + + finalize (res); + reset_solving (); + report_solving (res); + + return res; +} + +void Internal::implied (std::vector &entrailed) { + int last_assumption_level = assumptions.size (); + if (constraint.size ()) + last_assumption_level++; + + size_t trail_limit = trail.size(); + if (level > last_assumption_level) + trail_limit = control[last_assumption_level + 1].trail; + + for (size_t i = 0; i < trail_limit; i++) + entrailed.push_back (trail[i]); +} + +/*------------------------------------------------------------------------*/ + +// Most of the limits are only initialized in the first 'solve' call and +// increased as in a stand-alone non-incremental SAT call except for those +// explicitly marked as being reset below. + +void Internal::init_report_limits () { + reported = false; + lim.report = 0; + lim.recompute_tier = 5000; +} + +void Internal::init_preprocessing_limits () { + + const bool incremental = lim.initialized; + if (incremental) + LOG ("reinitializing preprocessing limits incrementally"); + else + LOG ("initializing preprocessing limits and increments"); + + const char *mode = 0; + + /*----------------------------------------------------------------------*/ + + if (incremental) + mode = "keeping"; + else { + last.elim.marked = -1; + lim.elim = stats.conflicts + scale (opts.elimint); + mode = "initial"; + } + (void) mode; + LOG ("%s elim limit %" PRId64 " after %" PRId64 " conflicts", mode, + lim.elim, lim.elim - stats.conflicts); + + // Initialize and reset elimination bounds in any case. + + lim.elimbound = opts.elimboundmin; + LOG ("elimination bound %" PRId64 "", lim.elimbound); + + /*----------------------------------------------------------------------*/ + + if (!incremental) { + + last.ternary.marked = -1; // TODO this should not be necessary... + + lim.compact = stats.conflicts + opts.compactint; + LOG ("initial compact limit %" PRId64 " increment %" PRId64 "", + lim.compact, lim.compact - stats.conflicts); + } + + /*----------------------------------------------------------------------*/ + + if (incremental) + mode = "keeping"; + else { + double delta = log10 (stats.added.irredundant); + delta = delta * delta; + lim.inprobe = stats.conflicts + opts.inprobeint * delta; + mode = "initial"; + } + (void) mode; + LOG ("%s probe limit %" PRId64 " after %" PRId64 " conflicts", mode, + lim.inprobe, lim.inprobe - stats.conflicts); + + /*----------------------------------------------------------------------*/ + + if (incremental) + mode = "keeping"; + else { + lim.condition = stats.conflicts + opts.conditionint; + mode = "initial"; + } + LOG ("%s condition limit %" PRId64 " increment %" PRId64, mode, + lim.condition, lim.condition - stats.conflicts); + + /*----------------------------------------------------------------------*/ + + // Initial preprocessing rounds. + + if (inc.preprocessing <= 0) { + lim.preprocessing = 0; + LOG ("no preprocessing"); + } else { + lim.preprocessing = inc.preprocessing; + LOG ("limiting to %" PRId64 " preprocessing rounds", lim.preprocessing); + } +} + +void Internal::init_search_limits () { + + const bool incremental = lim.initialized; + if (incremental) + LOG ("reinitializing search limits incrementally"); + else + LOG ("initializing search limits and increments"); + + const char *mode = 0; + + /*----------------------------------------------------------------------*/ + + if (incremental) + mode = "keeping"; + else { + last.reduce.conflicts = -1; + lim.reduce = stats.conflicts + opts.reduceinit; + mode = "initial"; + } + (void) mode; + LOG ("%s reduce limit %" PRId64 " after %" PRId64 " conflicts", mode, + lim.reduce, lim.reduce - stats.conflicts); + + /*----------------------------------------------------------------------*/ + + if (incremental) + mode = "keeping"; + else { + lim.flush = opts.flushint; + inc.flush = opts.flushint; + mode = "initial"; + } + (void) mode; + LOG ("%s flush limit %" PRId64 " interval %" PRId64 "", mode, lim.flush, + inc.flush); + + /*----------------------------------------------------------------------*/ + + // Initialize or reset 'rephase' limits in any case. + + lim.rephase = stats.conflicts + opts.rephaseint; + lim.rephased[0] = lim.rephased[1] = 0; + LOG ("new rephase limit %" PRId64 " after %" PRId64 " conflicts", + lim.rephase, lim.rephase - stats.conflicts); + + /*----------------------------------------------------------------------*/ + + // Initialize or reset 'restart' limits in any case. + + lim.restart = stats.conflicts + opts.restartint; + LOG ("new restart limit %" PRId64 " increment %" PRId64 "", lim.restart, + lim.restart - stats.conflicts); + + /*----------------------------------------------------------------------*/ + + if (!incremental) { + stable = opts.stabilize && opts.stabilizeonly; + if (stable) + LOG ("starting in always forced stable phase"); + else + LOG ("starting in default non-stable phase"); + init_averages (); + } else if (opts.stabilize && opts.stabilizeonly) { + LOG ("keeping always forced stable phase"); + assert (stable); + } else if (stable) { + LOG ("switching back to default non-stable phase"); + stable = false; + swap_averages (); + } else + LOG ("keeping non-stable phase"); + + if (!incremental) { + inc.stabilize = 0; + lim.stabilize = stats.conflicts + opts.stabilizeinit; + LOG ("initial stabilize limit %" PRId64 " after %d conflicts", + lim.stabilize, (int) opts.stabilizeinit); + } + + if (opts.stabilize && opts.reluctant) { + LOG ("new restart reluctant doubling sequence period %d", + opts.reluctant); + reluctant.enable (opts.reluctant, opts.reluctantmax); + } else + reluctant.disable (); + + /*----------------------------------------------------------------------*/ + + // Conflict and decision limits. + + if (inc.conflicts < 0) { + lim.conflicts = -1; + LOG ("no limit on conflicts"); + } else { + lim.conflicts = stats.conflicts + inc.conflicts; + LOG ("conflict limit after %" PRId64 " conflicts at %" PRId64 + " conflicts", + inc.conflicts, lim.conflicts); + } + + if (inc.decisions < 0) { + lim.decisions = -1; + LOG ("no limit on decisions"); + } else { + lim.decisions = stats.decisions + inc.decisions; + LOG ("conflict limit after %" PRId64 " decisions at %" PRId64 + " decisions", + inc.decisions, lim.decisions); + } + + /*----------------------------------------------------------------------*/ + + // Initial preprocessing rounds. + + if (inc.localsearch <= 0) { + lim.localsearch = 0; + LOG ("no local search"); + } else { + lim.localsearch = inc.localsearch; + LOG ("limiting to %" PRId64 " local search rounds", lim.localsearch); + } + + /*----------------------------------------------------------------------*/ + + lim.initialized = true; +} + +/*------------------------------------------------------------------------*/ + +bool Internal::preprocess_round (int round) { + (void) round; + if (unsat) + return false; + if (!max_var) + return false; + START (preprocess); + struct { + int64_t vars, clauses; + } before, after; + before.vars = active (); + before.clauses = stats.current.irredundant; + stats.preprocessings++; + assert (!preprocessing); + preprocessing = true; + PHASE ("preprocessing", stats.preprocessings, + "starting round %d with %" PRId64 " variables and %" PRId64 + " clauses", + round, before.vars, before.clauses); + int old_elimbound = lim.elimbound; + if (opts.inprobing) + inprobe (false); + if (opts.elim) + elim (false); + if (opts.condition) + condition (false); + + after.vars = active (); + after.clauses = stats.current.irredundant; + assert (preprocessing); + preprocessing = false; + PHASE ("preprocessing", stats.preprocessings, + "finished round %d with %" PRId64 " variables and %" PRId64 + " clauses", + round, after.vars, after.clauses); + STOP (preprocess); + report ('P'); + if (unsat) + return false; + if (after.vars < before.vars) + return true; + if (old_elimbound < lim.elimbound) + return true; + return false; +} + +// for now counts as one of the preprocessing rounds TODO: change this? +void Internal::preprocess_quickly () { + if (unsat) + return; + if (!max_var) + return; + if (!opts.preprocesslight) + return; + START (preprocess); + struct { + int64_t vars, clauses; + } before, after; + before.vars = active (); + before.clauses = stats.current.irredundant; + // stats.preprocessings++; + assert (!preprocessing); + preprocessing = true; + PHASE ("preprocessing", stats.preprocessings, + "starting with %" PRId64 " variables and %" PRId64 " clauses", + before.vars, before.clauses); + + if (extract_gates ()) + decompose (); + + if (sweep ()) + decompose (); + + if (opts.factor) + factor (); + + if (opts.fastelim) + elimfast (); + // if (opts.condition) + // condition (false); + after.vars = active (); + after.clauses = stats.current.irredundant; + assert (preprocessing); + preprocessing = false; + PHASE ("preprocessing", stats.preprocessings, + "finished with %" PRId64 " variables and %" PRId64 " clauses", + after.vars, after.clauses); + STOP (preprocess); + report ('P'); +} + +int Internal::preprocess () { + preprocess_quickly (); + for (int i = 0; i < lim.preprocessing; i++) + if (!preprocess_round (i)) + break; + if (unsat) + return 20; + return 0; +} + +/*------------------------------------------------------------------------*/ + +int Internal::try_to_satisfy_formula_by_saved_phases () { + LOG ("satisfying formula by saved phases"); + assert (!level); + assert (!force_saved_phase); + assert (propagated == trail.size ()); + force_saved_phase = true; + if (external_prop) { + assert (!level); + LOG ("external notifications are turned off during preprocessing."); + private_steps = true; + } + int res = 0; + while (!res) { + if (satisfied ()) { + LOG ("formula indeed satisfied by saved phases"); + res = 10; + } else if (decide ()) { + LOG ("inconsistent assumptions with redundant clauses and phases"); + res = 20; + } else if (!propagate ()) { + LOG ("saved phases do not satisfy redundant clauses"); + assert (level > 0); + backtrack (); + conflict = 0; // ignore conflict + assert (!res); + break; + } + } + assert (force_saved_phase); + force_saved_phase = false; + if (external_prop) { + private_steps = false; + LOG ("external notifications are turned back on."); + if (!level) + notify_assignments (); // In case fixed assignments were found. + else { + renotify_trail_after_local_search (); + } + } + return res; +} + +/*------------------------------------------------------------------------*/ + +void Internal::produce_failed_assumptions () { + LOG ("producing failed assumptions"); + assert (!level); + assert (!assumptions.empty ()); + while (!unsat) { + assert (!satisfied ()); + notify_assignments (); + if (decide ()) + break; + while (!unsat && !propagate ()) + analyze (); + } + notify_assignments (); + if (unsat) + LOG ("formula is actually unsatisfiable unconditionally"); + else + LOG ("assumptions indeed failing"); +} + +/*------------------------------------------------------------------------*/ + +int Internal::local_search_round (int round) { + + assert (round > 0); + + if (unsat) + return false; + if (!max_var) + return false; + + START_OUTER_WALK (); + assert (!localsearching); + localsearching = true; + + // Determine propagation limit quadratically scaled with rounds. + // + int64_t limit = opts.walkmineff; + limit *= round; + if (LONG_MAX / round > limit) + limit *= round; + else + limit = LONG_MAX; + + int res = walk_round (limit, true); + + assert (localsearching); + localsearching = false; + STOP_OUTER_WALK (); + + report ('L'); + + return res; +} + +int Internal::local_search () { + + if (unsat) + return 0; + if (!max_var) + return 0; + if (!opts.walk) + return 0; + if (constraint.size ()) + return 0; + + int res = 0; + + for (int i = 1; !res && i <= lim.localsearch; i++) + res = local_search_round (i); + + if (res == 10) { + LOG ("local search determined formula to be satisfiable"); + assert (!stats.walk.minimum); + res = try_to_satisfy_formula_by_saved_phases (); + } else if (res == 20) { + LOG ("local search determined assumptions to be inconsistent"); + assert (!assumptions.empty ()); + produce_failed_assumptions (); + } + + return res; +} + +/*------------------------------------------------------------------------*/ + +// if preprocess_only is false and opts.ilb is true we do not preprocess +// such that we do not have to backtrack to level 0. +// +int Internal::solve (bool preprocess_only) { + assert (clause.empty ()); + START (solve); + if (proof) + proof->solve_query (); + if (opts.ilb) { + if (opts.ilbassumptions) + sort_and_reuse_assumptions (); + stats.ilbtriggers++; + stats.ilbsuccess += (level > 0); + stats.levelsreused += level; + if (level) { + assert (control.size () > 1); + stats.literalsreused += num_assigned - control[1].trail; + } + if (external->propagator) + renotify_trail_after_ilb (); + } + if (preprocess_only) + LOG ("internal solving in preprocessing only mode"); + else + LOG ("internal solving in full mode"); + init_report_limits (); + int res = already_solved (); + if (!res && preprocess_only && level) + backtrack (); + if (!res) + res = restore_clauses (); + if (!res || (res == 10 && external_prop)) { + init_preprocessing_limits (); + if (!preprocess_only) + init_search_limits (); + } + if (!preprocess_only) { + if (!res && !level) + res = local_search (); + } + if (!res && !level) + res = preprocess (); + if (!preprocess_only) { + if (!res && !level) + res = local_search (); + if (!res && !level) + res = lucky_phases (); + if (!res || (res == 10 && external_prop)) { + if (res == 10 && external_prop && level) + backtrack (); + res = cdcl_loop_with_inprocessing (); + } + } + finalize (res); + reset_solving (); + report_solving (res); + STOP (solve); + return res; +} + +int Internal::already_solved () { + int res = 0; + if (unsat || unsat_constraint) { + LOG ("already inconsistent"); + res = 20; + } else { + if (level && !opts.ilb) + backtrack (); + if (!level && !propagate ()) { + LOG ("root level propagation produces conflict"); + learn_empty_clause (); + res = 20; + } + if (max_var == 0 && res == 0) + res = 10; + } + return res; +} +void Internal::report_solving (int res) { + if (res == 10) + report ('1'); + else if (res == 20) + report ('0'); + else + report ('?'); +} + +void Internal::reset_solving () { + if (termination_forced) { + + // TODO this leads potentially to a data race if the external + // user is calling 'terminate' twice within one 'solve' call. + // A proper solution would be to guard / protect setting the + // 'termination_forced' flag and only allow it during solving and + // ignore it otherwise thus also the second time it is called during a + // 'solve' call. We could move resetting it also the start of + // 'solve'. + // + termination_forced = false; + + LOG ("reset forced termination"); + } +} + +int Internal::restore_clauses () { + int res = 0; + if (opts.restoreall <= 1 && external->tainted.empty ()) { + LOG ("no tainted literals and nothing to restore"); + report ('*'); + } else { + report ('+'); + // remove_garbage_binaries (); + external->restore_clauses (); + internal->report ('r'); + if (!unsat && !level && !propagate ()) { + LOG ("root level propagation after restore produces conflict"); + learn_empty_clause (); + res = 20; + } + } + return res; +} + +int Internal::lookahead () { + assert (clause.empty ()); + START (lookahead); + assert (!lookingahead); + lookingahead = true; + if (external_prop) { + if (level) { + // Combining lookahead with external propagator is limited + // Note that lookahead_probing (); would also force backtrack anyway + backtrack (); + } + LOG ("external notifications are turned off during preprocessing."); + private_steps = true; + } + int tmp = already_solved (); + if (!tmp) + tmp = restore_clauses (); + int res = 0; + if (!tmp) + res = lookahead_probing (); + if (res == INT_MIN) + res = 0; + reset_solving (); + report_solving (tmp); + assert (lookingahead); + lookingahead = false; + STOP (lookahead); + if (external_prop) { + private_steps = false; + LOG ("external notifications are turned back on."); + notify_assignments (); // In case fixed assignments were found. + } + return res; +} + +/*------------------------------------------------------------------------*/ + +void Internal::finalize (int res) { + if (!proof) + return; + LOG ("finalizing"); + // finalize external units + if (frat) { + for (const auto &evar : external->vars) { + assert (evar > 0); + const auto eidx = 2 * evar; + int sign = 1; + int64_t id = external->ext_units[eidx]; + if (!id) { + sign = -1; + id = external->ext_units[eidx + 1]; + } + if (id) { + proof->finalize_external_unit (id, evar * sign); + } + } + // finalize internal units + for (const auto &lit : lits) { + const auto elit = externalize (lit); + if (elit) { + const unsigned eidx = (elit < 0) + 2u * (unsigned) abs (elit); + const int64_t id = external->ext_units[eidx]; + if (id) { + assert (unit_clauses (vlit (lit)) == id); + continue; + } + } + const int64_t id = unit_clauses (vlit (lit)); + if (!id) + continue; + proof->finalize_unit (id, lit); + } + // See the discussion in 'propagate' on why garbage binary clauses stick + // around. + for (const auto &c : clauses) + if (!c->garbage || (c->size == 2 && !c->flushed)) + proof->finalize_clause (c); + + // finalize conflict and proof + if (conflict_id) { + proof->finalize_clause (conflict_id, {}); + } + } + proof->report_status (res, conflict_id); + if (res == 10) + external->conclude_sat (); + else if (res == 20) + conclude_unsat (); + else if (!res) + external->conclude_unknown (); +} + +/*------------------------------------------------------------------------*/ + +void Internal::print_statistics () { + stats.print (this); + for (auto &st : stat_tracers) + st->print_stats (); +} + +/*------------------------------------------------------------------------*/ + +// Only useful for debugging purposes. + +void Internal::dump (Clause *c) { + for (const auto &lit : *c) + printf ("%d ", lit); + printf ("0\n"); +} + +void Internal::dump () { + int64_t m = assumptions.size (); + for (auto idx : vars) + if (fixed (idx)) + m++; + for (const auto &c : clauses) + if (!c->garbage) + m++; + printf ("p cnf %d %" PRId64 "\n", max_var, m); + for (auto idx : vars) { + const int tmp = fixed (idx); + if (tmp) + printf ("%d 0\n", tmp < 0 ? -idx : idx); + } + for (const auto &c : clauses) + if (!c->garbage) + dump (c); + for (const auto &lit : assumptions) + printf ("%d 0\n", lit); + fflush (stdout); +} + +/*------------------------------------------------------------------------*/ + +bool Internal::traverse_constraint (ClauseIterator &it) { + if (constraint.empty () && !unsat_constraint) + return true; + + vector eclause; + if (unsat) + return it.clause (eclause); + + LOG (constraint, "traversing constraint"); + bool satisfied = false; + for (auto ilit : constraint) { + const int tmp = fixed (ilit); + if (tmp > 0) { + satisfied = true; + break; + } + if (tmp < 0) + continue; + const int elit = externalize (ilit); + eclause.push_back (elit); + } + if (!satisfied && !it.clause (eclause)) + return false; + + return true; +} +/*------------------------------------------------------------------------*/ + +bool Internal::traverse_clauses (ClauseIterator &it) { + vector eclause; + if (unsat) + return it.clause (eclause); + for (const auto &c : clauses) { + if (c->garbage) + continue; + if (c->redundant) + continue; + bool satisfied = false; + for (const auto &ilit : *c) { + const int tmp = fixed (ilit); + if (tmp > 0) { + satisfied = true; + break; + } + if (tmp < 0) + continue; + const int elit = externalize (ilit); + eclause.push_back (elit); + } + if (!satisfied && !it.clause (eclause)) + return false; + eclause.clear (); + } + return true; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/internal.hpp b/src/sat/cadical/internal.hpp new file mode 100644 index 000000000..8dfa2cd8d --- /dev/null +++ b/src/sat/cadical/internal.hpp @@ -0,0 +1,1858 @@ +#ifndef _internal_hpp_INCLUDED +#define _internal_hpp_INCLUDED + +/*------------------------------------------------------------------------*/ + +// Wrapped build specific headers which should go first. + +#include "inttypes.hpp" + +/*------------------------------------------------------------------------*/ + +// Common 'C' headers. + +#include +#include +#include +#include +#include +#include +#include +#include + +// Less common 'C' header. + +extern "C" { +#include +} + +/*------------------------------------------------------------------------*/ + +// Common 'C++' headers. + +#include +#include +#include +#include +#include + +/*------------------------------------------------------------------------*/ + +// All internal headers are included here. This gives a nice overview on +// what is needed altogether. The 'Internal' class needs almost all the +// headers anyhow (since the idea is to avoid pointer references as much as +// possible). Most implementation files need to see the definition of the +// 'Internal' too. Thus there is no real advantage in trying to reduce the +// number of header files included here. The other benefit of having all +// header files here is that '.cpp' files then only need to include this. + +#include "arena.hpp" +#include "averages.hpp" +#include "bins.hpp" +#include "block.hpp" +#include "cadical.hpp" +#include "checker.hpp" +#include "clause.hpp" +#include "config.hpp" +#include "congruence.hpp" +#include "contract.hpp" +#include "cover.hpp" +#include "decompose.hpp" +#include "drattracer.hpp" +#include "elim.hpp" +#include "ema.hpp" +#include "external.hpp" +#include "factor.hpp" +#include "file.hpp" +#include "flags.hpp" +#include "format.hpp" +#include "frattracer.hpp" +#include "heap.hpp" +#include "idruptracer.hpp" +#include "instantiate.hpp" +#include "internal.hpp" +#include "level.hpp" +#include "lidruptracer.hpp" +#include "limit.hpp" +#include "logging.hpp" +#include "lratchecker.hpp" +#include "lrattracer.hpp" +#include "message.hpp" +#include "occs.hpp" +#include "options.hpp" +#include "parse.hpp" +#include "phases.hpp" +#include "profile.hpp" +#include "proof.hpp" +#include "queue.hpp" +#include "radix.hpp" +#include "random.hpp" +#include "range.hpp" +#include "reap.hpp" +#include "reluctant.hpp" +#include "resources.hpp" +#include "score.hpp" +#include "stats.hpp" +#include "sweep.hpp" +#include "terminal.hpp" +#include "tracer.hpp" +#include "util.hpp" +#include "var.hpp" +#include "veripbtracer.hpp" +#include "version.hpp" +#include "vivify.hpp" +#include "watch.hpp" + +// c headers +extern "C" { +#include "kitten.h" +} +/*------------------------------------------------------------------------*/ + +namespace CaDiCaL { + +using namespace std; + +struct Coveror; +struct External; +struct Walker; +class Tracer; +class FileTracer; +class StatTracer; + +struct CubesWithStatus { + int status = 0; + std::vector> cubes; +}; + +/*------------------------------------------------------------------------*/ + +struct Internal { + + /*----------------------------------------------------------------------*/ + + // The actual internal state of the solver is set and maintained in this + // section. This is currently only used for debugging and testing. + + enum Mode { + BLOCK = (1 << 0), + CONDITION = (1 << 1), + CONGRUENCE = (1 << 2), + COVER = (1 << 3), + DECOMP = (1 << 4), + DEDUP = (1 << 5), + ELIM = (1 << 6), + FACTOR = (1 << 7), + LUCKY = (1 << 8), + PROBE = (1 << 9), + SEARCH = (1 << 10), + SIMPLIFY = (1 << 11), + SUBSUME = (1 << 12), + SWEEP = (1 << 13), + TERNARY = (1 << 14), + TRANSRED = (1 << 15), + VIVIFY = (1 << 16), + WALK = (1 << 17), + }; + + bool in_mode (Mode m) const { return (mode & m) != 0; } + void set_mode (Mode m) { + assert (!(mode & m)); + mode |= m; + } + void reset_mode (Mode m) { + assert (mode & m); + mode &= ~m; + } + void require_mode (Mode m) const { assert (mode & m), (void) m; } + + /*----------------------------------------------------------------------*/ + + int mode; // current internal state + int tier1[2] = { + 2, 2}; // tier1 limit for 0=focused, 1=stable; aka tier1[stable] + int tier2[2] = { + 6, 6}; // tier2 limit for 0=focused, 1=stable; aka tier1[stable] + bool unsat; // empty clause found or learned + bool iterating; // report learned unit ('i' line) + bool localsearching; // true during local search + bool lookingahead; // true during look ahead + bool preprocessing; // true during preprocessing + bool protected_reasons; // referenced reasons are protected + bool force_saved_phase; // force saved phase in decision + bool searching_lucky_phases; // during 'lucky_phases' + bool stable; // true during stabilization phase + bool reported; // reported in this solving call + bool external_prop; // true if an external propagator is connected + bool did_external_prop; // true if ext. propagation happened + bool external_prop_is_lazy; // true if the external propagator is lazy + bool forced_backt_allowed; // external propagator can force backtracking + bool private_steps; // no notification of ext. prop during these steps + char rephased; // last type of resetting phases + Reluctant reluctant; // restart counter in stable mode + size_t vsize; // actually allocated variable data size + int max_var; // internal maximum variable index + int64_t clause_id; // last used id for clauses + int64_t original_id; // ids for original clauses to produce LRAT + int64_t reserved_ids; // number of reserved ids for original clauses + int64_t conflict_id; // store conflict id for finalize (frat) + int64_t saved_decisions; // to compute decision rate average + bool concluded; // keeps track of conclude + vector conclusion; // store ids of conclusion clauses + vector + unit_clauses_idx; // keep track of unit_clauses (LRAT/FRAT) + vector lrat_chain; // create LRAT in solver: option lratdirect + vector mini_chain; // used to create LRAT in minimize + vector minimize_chain; // used to create LRAT in minimize + vector unit_chain; // used to avoid duplicate units + vector inst_chain; // for LRAT in instantiate + vector>> + probehbr_chains; // only used if opts.probehbr=false + bool lrat; // generate LRAT internally + bool frat; // finalize non-deleted clauses in proof + int level; // decision level ('control.size () - 1') + Phases phases; // saved, target and best phases + signed char *vals; // assignment [-max_var,max_var] + vector marks; // signed marks [1,max_var] + vector frozentab; // frozen counters [1,max_var] + vector i2e; // maps internal 'idx' to external 'lit' + vector relevanttab; // Reference counts for observed variables. + Queue queue; // variable move to front decision queue + Links links; // table of links for decision queue + double score_inc; // current score increment + ScoreSchedule scores; // score based decision priority queue + vector stab; // table of variable scores [1,max_var] + vector vtab; // variable table [1,max_var] + vector parents; // parent literals during probing + vector ftab; // variable and literal flags + vector btab; // enqueue time stamps for queue + vector gtab; // time stamp table to recompute glue + vector otab; // table of occurrences for all literals + vector rtab; // table of redundant occurrences + vector ptab; // table for caching probing attempts + vector ntab; // number of one-sided occurrences table + vector big; // binary implication graph + vector wtab; // table of watches for all literals + Clause *conflict; // set in 'propagation', reset in 'analyze' + Clause *ignore; // ignored during 'vivify_propagate' + Clause *dummy_binary; // Dummy binary clause for subsumption + Clause *external_reason; // used as reason at external propagations + Clause *newest_clause; // used in external_propagate + bool force_no_backtrack; // for new clauses with external propagator + bool from_propagator; // differentiate new clauses... + bool ext_clause_forgettable; // Is new clause from propagator forgettable + int tainted_literal; // used for ILB + size_t notified; // next trail position to notify external prop + Clause *probe_reason; // set during probing + size_t propagated; // next trail position to propagate + size_t propagated2; // next binary trail position to propagate + size_t propergated; // propagated without blocking literals + size_t best_assigned; // best maximum assigned ever + size_t target_assigned; // maximum assigned without conflict + size_t no_conflict_until; // largest trail prefix without conflict + vector trail; // currently assigned literals + vector clause; // simplified in parsing & learning + vector assumptions; // assumed literals + vector constraint; // literals of the constraint + bool unsat_constraint; // constraint used for unsatisfiability? + bool marked_failed; // are the failed assumptions marked? + vector original; // original added literals + vector levels; // decision levels in learned clause + vector analyzed; // analyzed literals in 'analyze' + vector unit_analyzed; // to avoid duplicate units in lrat_chain + vector sign_marked; // literals skipped in 'decompose' + vector minimized; // removable or poison in 'minimize' + vector shrinkable; // removable or poison in 'shrink' + Reap reap; // radix heap for shrink + + vector sweep_schedule; // remember sweep varibles to reschedule + bool sweep_incomplete; // sweep + + kitten *citten; + + size_t num_assigned; // check for satisfied + + vector probes; // remaining scheduled probes + vector control; // 'level + 1 == control.size ()' + vector clauses; // ordered collection of all clauses + Averages averages; // glue, size, jump moving averages + Delay delay[2]; // Delay certain functions + Delay congruence_delay; // Delay congruence if not successful recently + Limit lim; // limits for various phases + Last last; // statistics at last occurrence + Inc inc; // increments on limits + + Delay delaying_vivify_irredundant; + Delay delaying_sweep; + + Proof *proof; // abstraction layer between solver and tracers + vector + tracers; // proof tracing objects (ie interpolant calculator) + vector + file_tracers; // file proof tracers (ie DRAT, LRAT...) + vector stat_tracers; // checkers + + Options opts; // run-time options + Stats stats; // statistics +#ifndef QUIET + Profiles profiles; // time profiles for various functions + bool force_phase_messages; // force 'phase (...)' messages +#endif + Arena arena; // memory arena for moving garbage collector + Format error_message; // provide persistent error message + string prefix; // verbose messages prefix + + Internal *internal; // proxy to 'this' in macros + External *external; // proxy to 'external' buddy in 'Solver' + + const unsigned max_used = 255; // must fix into the header of the clause! + /*----------------------------------------------------------------------*/ + + // Asynchronous termination flag written by 'terminate' and read by + // 'terminated_asynchronously' (the latter at the end of this header). + // + volatile bool termination_forced; + + /*----------------------------------------------------------------------*/ + + const Range vars; // Provides safe variable iteration. + const Sange lits; // Provides safe literal iteration. + + /*----------------------------------------------------------------------*/ + + Internal (); + ~Internal (); + + /*----------------------------------------------------------------------*/ + + // Internal delegates and helpers for corresponding functions in + // 'External' and 'Solver'. The 'init_vars' function initializes + // variables up to and including the requested variable index. + // + void init_vars (int new_max_var); + + void init_enqueue (int idx); + void init_queue (int old_max_var, int new_max_var); + + void init_scores (int old_max_var, int new_max_var); + + void add_original_lit (int lit); + + // only able to restore irredundant clause + void finish_added_clause_with_id (int64_t id, bool restore = false); + + // Reserve ids for original clauses to produce lrat + void reserve_ids (int number); + + // Enlarge tables. + // + void enlarge_vals (size_t new_vsize); + void enlarge (int new_max_var); + + // A variable is 'active' if it is not eliminated nor fixed. + // + bool active (int lit) { return flags (lit).active (); } + + int active () const { + int res = stats.active; +#ifndef NDEBUG + int tmp = max_var; + tmp -= stats.unused; + tmp -= stats.now.fixed; + tmp -= stats.now.eliminated; + tmp -= stats.now.substituted; + tmp -= stats.now.pure; + assert (tmp >= 0); + assert (tmp == res); +#endif + return res; + } + + void reactivate (int lit); // During 'restore'. + + // Currently remaining active redundant and irredundant clauses. + + int64_t redundant () const { return stats.current.redundant; } + + int64_t irredundant () const { return stats.current.irredundant; } + + double clause_variable_ratio () const { + return relative (irredundant (), active ()); + } + + // Scale values relative to clause variable ratio. + // + double scale (double v) const; + + // Unsigned literals (abs) with checks. + // + int vidx (int lit) const { + int idx; + assert (lit); + assert (lit != INT_MIN); + idx = abs (lit); + assert (idx <= max_var); + return idx; + } + + // Unsigned version with LSB denoting sign. This is used in indexing + // arrays by literals. The idea is to keep the elements in such an array + // for both the positive and negated version of a literal close together. + // + unsigned vlit (int lit) const { + return (lit < 0) + 2u * (unsigned) vidx (lit); + } + + int u2i (unsigned u) { + assert (u > 1); + int res = u / 2; + assert (res <= max_var); + if (u & 1) + res = -res; + return res; + } + + int citten2lit (unsigned ulit) { + int res = (ulit / 2) + 1; + assert (res <= max_var); + if (ulit & 1) + res = -res; + return res; + } + + unsigned lit2citten (int lit) { + int idx = vidx (lit) - 1; + return (lit < 0) + 2u * (unsigned) idx; + } + + int64_t unit_id (int lit) const { + assert (lrat || frat); + assert (val (lit) > 0); + const unsigned uidx = vlit (lit); + int64_t id = unit_clauses_idx[uidx]; + assert (id); + return id; + } + + inline int64_t &unit_clauses (int uidx) { + assert (lrat || frat); + assert (uidx > 0); + assert ((size_t) uidx < unit_clauses_idx.size ()); + return unit_clauses_idx[uidx]; + } + + // Helper functions to access variable and literal data. + // + Var &var (int lit) { return vtab[vidx (lit)]; } + Link &link (int lit) { return links[vidx (lit)]; } + Flags &flags (int lit) { return ftab[vidx (lit)]; } + int64_t &bumped (int lit) { return btab[vidx (lit)]; } + int &propfixed (int lit) { return ptab[vlit (lit)]; } + double &score (int lit) { return stab[vidx (lit)]; } + + const Flags &flags (int lit) const { return ftab[vidx (lit)]; } + + bool occurring () const { return !otab.empty (); } + bool watching () const { return !wtab.empty (); } + + Bins &bins (int lit) { return big[vlit (lit)]; } + Occs &occs (int lit) { return otab[vlit (lit)]; } + int64_t &noccs (int lit) { return ntab[vlit (lit)]; } + Watches &watches (int lit) { return wtab[vlit (lit)]; } + + // Variable bumping through exponential VSIDS (EVSIDS) as in MiniSAT. + // + bool use_scores () const { return opts.score && stable; } + void bump_variable_score (int lit); + void bump_variable_score_inc (); + void rescale_variable_scores (); + + // Marking variables with a sign (positive or negative). + // + signed char marked (int lit) const { + signed char res = marks[vidx (lit)]; + if (lit < 0) + res = -res; + return res; + } + void mark (int lit) { + assert (!marked (lit)); + marks[vidx (lit)] = sign (lit); + assert (marked (lit) > 0); + assert (marked (-lit) < 0); + } + void unmark (int lit) { + marks[vidx (lit)] = 0; + assert (!marked (lit)); + } + + // Use only bits 6 and 7 to store the sign or zero. The remaining + // bits can be used as additional flags. + // + signed char marked67 (int lit) const { + signed char res = marks[vidx (lit)] >> 6; + if (lit < 0) + res = -res; + return res; + } + void mark67 (int lit) { + signed char &m = marks[vidx (lit)]; + const signed char mask = 0x3f; +#ifndef NDEBUG + const signed char bits = m & mask; +#endif + m = (m & mask) | (sign (lit) << 6); + assert (marked (lit) > 0); + assert (marked (-lit) < 0); + assert ((m & mask) == bits); + assert (marked67 (lit) > 0); + assert (marked67 (-lit) < 0); + } + void unmark67 (int lit) { + signed char &m = marks[vidx (lit)]; + const signed char mask = 0x3f; +#ifndef NDEBUG + const signed bits = m & mask; +#endif + m &= mask; + assert ((m & mask) == bits); + } + + void unmark (vector &lits) { + for (const auto &lit : lits) + unmark (lit); + } + + // The other 6 bits of the 'marks' bytes can be used as additional + // (unsigned) marking bits. Currently we only use the least significant + // bit in 'condition' to mark variables in the conditional part. + // + bool getbit (int lit, int bit) const { + assert (0 <= bit), assert (bit < 6); + return marks[vidx (lit)] & (1 << bit); + } + void setbit (int lit, int bit) { + assert (0 <= bit), assert (bit < 6); + assert (!getbit (lit, bit)); + marks[vidx (lit)] |= (1 << bit); + assert (getbit (lit, bit)); + } + void unsetbit (int lit, int bit) { + assert (0 <= bit), assert (bit < 6); + assert (getbit (lit, bit)); + marks[vidx (lit)] &= ~(1 << bit); + assert (!getbit (lit, bit)); + } + + // Marking individual literals. + // + bool marked2 (int lit) const { + unsigned res = marks[vidx (lit)]; + assert (res <= 3); + unsigned bit = bign (lit); + return (res & bit) != 0; + } + void mark2 (int lit) { + marks[vidx (lit)] |= bign (lit); + assert (marked2 (lit)); + } + + // marks bits 1,2,3 and 4,5,6 depending on fact and sign of lit + // + bool getfact (int lit, int fact) const { + assert (fact == 1 || fact == 2 || fact == 4); + int res = marks[vidx (lit)]; + if (lit < 0) { + res >>= 3; + } else { + res &= 7; + } + // assert (!res || res == 1 || res == 2 || res == 4); + return res & fact; + } + + void markfact (int lit, int fact) { + assert (fact == 1 || fact == 2 || fact == 4); + assert (!getfact (lit, fact)); +#ifndef NDEBUG + int before = getfact (-lit, fact); +#endif + int res = marks[vidx (lit)]; + if (lit < 0) { + res |= fact << 3; + } else { + res |= fact; + } + marks[vidx (lit)] = res; + assert (getfact (lit, fact)); +#ifndef NDEBUG + assert (getfact (-lit, fact) == before); +#endif + } + + void unmarkfact (int lit, int fact) { + assert (fact == 1 || fact == 2 || fact == 4); + assert (getfact (lit, fact)); + int res = marks[vidx (lit)]; + if (lit < 0) { + res &= ~(fact << 3); + } else { + res &= ~fact; + } + marks[vidx (lit)] = res; + assert (!getfact (lit, fact)); + } + + // Marking and unmarking of all literals in a clause. + // + void mark_clause (); // mark 'this->clause' + void mark (Clause *); + void mark2 (Clause *); + void unmark_clause (); // unmark 'this->clause' + void unmark (Clause *); + + // Watch literal 'lit' in clause with blocking literal 'blit'. + // Inlined here, since it occurs in the tight inner loop of 'propagate'. + // + inline void watch_literal (int lit, int blit, Clause *c) { + assert (lit != blit); + Watches &ws = watches (lit); + ws.push_back (Watch (blit, c)); + LOG (c, "watch %d blit %d in", lit, blit); + } + + // Add two watches to a clause. This is used initially during allocation + // of a clause and during connecting back all watches after preprocessing. + // + inline void watch_clause (Clause *c) { + const int l0 = c->literals[0]; + const int l1 = c->literals[1]; + watch_literal (l0, l1, c); + watch_literal (l1, l0, c); + } + + inline void unwatch_clause (Clause *c) { + const int l0 = c->literals[0]; + const int l1 = c->literals[1]; + remove_watch (watches (l0), c); + remove_watch (watches (l1), c); + } + + // Update queue to point to last potentially still unassigned variable. + // All variables after 'queue.unassigned' in bump order are assumed to be + // assigned. Then update the 'queue.bumped' field and log it. This is + // inlined here since it occurs in several inner loops. + // + inline void update_queue_unassigned (int idx) { + assert (0 < idx); + assert (idx <= max_var); + queue.unassigned = idx; + queue.bumped = btab[idx]; + LOG ("queue unassigned now %d bumped %" PRId64 "", idx, btab[idx]); + } + + void bump_queue (int idx); + + // Mark (active) variables as eliminated, substituted, pure or fixed, + // which turns them into inactive variables. + // + void mark_eliminated (int); + void mark_substituted (int); + void mark_active (int); + void mark_fixed (int); + void mark_pure (int); + + // Managing clauses in 'clause.cpp'. Without explicit 'Clause' argument + // these functions work on the global temporary 'clause'. + // + Clause *new_clause (bool red, int glue = 0); + void promote_clause (Clause *, int new_glue); + void promote_clause_glue_only (Clause *, int new_glue); + size_t shrink_clause (Clause *, int new_size); + void minimize_sort_clause (); + void shrink_and_minimize_clause (); + void reset_shrinkable (); + void mark_shrinkable_as_removable (int, std::vector::size_type); + int shrink_literal (int, int, unsigned); + unsigned shrunken_block_uip (int, int, + std::vector::reverse_iterator &, + std::vector::reverse_iterator &, + std::vector::size_type, const int); + void shrunken_block_no_uip (const std::vector::reverse_iterator &, + const std::vector::reverse_iterator &, + unsigned &, const int); + void push_literals_of_block (const std::vector::reverse_iterator &, + const std::vector::reverse_iterator &, + int, unsigned); + unsigned shrink_next (int, unsigned &, unsigned &); + std::vector::reverse_iterator + minimize_and_shrink_block (std::vector::reverse_iterator &, + unsigned int &, unsigned int &, const int); + unsigned shrink_block (std::vector::reverse_iterator &, + std::vector::reverse_iterator &, int, + unsigned &, unsigned &, const int, unsigned); + unsigned shrink_along_reason (int, int, bool, bool &, unsigned); + + void deallocate_clause (Clause *); + void delete_clause (Clause *); + void mark_garbage (Clause *); + void assign_original_unit (int64_t, int); + void add_new_original_clause (int64_t); + Clause *new_learned_redundant_clause (int glue); + Clause *new_hyper_binary_resolved_clause (bool red, int glue); + Clause *new_clause_as (const Clause *orig); + Clause *new_resolved_irredundant_clause (); + + // Forward reasoning through propagation in 'propagate.cpp'. + // + int assignment_level (int lit, Clause *); + void build_chain_for_units (int lit, Clause *reason, bool forced); + void build_chain_for_empty (); + void search_assign (int lit, Clause *); + void search_assign_driving (int lit, Clause *reason); + void search_assign_external (int lit); + void search_assume_decision (int decision); + void assign_unit (int lit); + int64_t cache_lines (size_t bytes) { return (bytes + 127) / 128; } + int64_t cache_lines (size_t n, size_t bytes) { + return cache_lines (n * bytes); + } + bool propagate (); + +#ifdef PROFILE_MODE + bool propagate_wrapper (); + bool propagate_unstable (); + bool propagate_stable (); + void analyze_wrapper (); + void analyze_unstable (); + void analyze_stable (); + int decide_wrapper (); + int decide_stable (); + int decide_unstable (); +#else +#define propagate_wrapper propagate +#define analyze_wrapper analyze +#define decide_wrapper decide +#endif + + void propergate (); // Repropagate without blocking literals. + + // Undo and restart in 'backtrack.cpp'. + // + void unassign (int lit); + void update_target_and_best (); + void backtrack (int target_level = 0); + void backtrack_without_updating_phases (int target_level = 0); + + // Minimized learned clauses in 'minimize.cpp'. + // + bool minimize_literal (int lit, int depth = 0); + void minimize_clause (); + void calculate_minimize_chain (int lit, std::vector &stack); + + // Learning from conflicts in 'analyze.cc'. + // + void learn_empty_clause (); + void learn_unit_clause (int lit); + + void bump_variable (int lit); + void bump_variables (); + int recompute_glue (Clause *); + void bump_clause (Clause *); + void bump_clause2 (Clause *); + void clear_unit_analyzed_literals (); + void clear_analyzed_literals (); + void clear_analyzed_levels (); + void clear_minimized_literals (); + bool bump_also_reason_literal (int lit); + void bump_also_reason_literals (int lit, int depth_limit, + size_t size_limit); + void bump_also_all_reason_literals (); + void analyze_literal (int lit, int &open, int &resolvent_size, + int &antecedent_size); + void analyze_reason (int lit, Clause *, int &open, int &resolvent_size, + int &antecedent_size); + Clause *new_driving_clause (const int glue, int &jump); + int find_conflict_level (int &forced); + int determine_actual_backtrack_level (int jump); + void otfs_strengthen_clause (Clause *, int, int, + const std::vector &); + void otfs_subsume_clause (Clause *subsuming, Clause *subsumed); + int otfs_find_backtrack_level (int &forced); + Clause *on_the_fly_strengthen (Clause *conflict, int lit); + void update_decision_rate_average (); + void analyze (); + void iterate (); // report learned unit clause + + // Learning from external propagator in 'external_propagate.cpp' + // + bool external_propagate (); + bool external_check_solution (); + void add_external_clause (int propagated_lit = 0, + bool no_backtrack = false); + Clause *learn_external_reason_clause (int lit, int falsified_elit = 0, + bool no_backtrack = false); + Clause *wrapped_learn_external_reason_clause (int lit); + void explain_external_propagations (); + void explain_reason (int lit, Clause *, int &open); + void move_literals_to_watch (); + void handle_external_clause (Clause *); + void notify_assignments (); + void notify_decision (); + void notify_backtrack (size_t new_level); + void force_backtrack (size_t new_level); + int ask_decision (); + bool ask_external_clause (); + void add_observed_var (int ilit); + void remove_observed_var (int ilit); + bool observed (int ilit) const; + bool is_decision (int ilit); + void check_watched_literal_invariants (); + void set_tainted_literal (); + void renotify_trail_after_ilb (); + void renotify_trail_after_local_search (); + void renotify_full_trail (); + void connect_propagator (); + void mark_garbage_external_forgettable (int64_t id); + bool is_external_forgettable (int64_t id); +#ifndef NDEBUG + bool get_merged_literals (std::vector &); + void get_all_fixed_literals (std::vector &); +#endif + + void recompute_tier (); + // Use last learned clause to subsume some more. + // + void eagerly_subsume_recently_learned_clauses (Clause *); + + // Restarting policy in 'restart.cc'. + // + bool stabilizing (); + bool restarting (); + int reuse_trail (); + void restart (); + + // Functions to set and reset certain 'phases'. + // + void clear_phases (vector &); // reset argument to zero + void copy_phases (vector &); // copy 'saved' to argument + + // Resetting the saved phased in 'rephase.cpp'. + // + bool rephasing (); + char rephase_best (); + char rephase_flipping (); + char rephase_inverted (); + char rephase_original (); + char rephase_random (); + char rephase_walk (); + void shuffle_scores (); + void shuffle_queue (); + void rephase (); + + // Lucky feasible case checking. + // + int unlucky (int res); + bool lucky_propagate_discrepency (int); + int trivially_false_satisfiable (); + int trivially_true_satisfiable (); + int forward_false_satisfiable (); + int forward_true_satisfiable (); + int backward_false_satisfiable (); + int backward_true_satisfiable (); + int positive_horn_satisfiable (); + int negative_horn_satisfiable (); + + // Asynchronous terminating check. + // + bool terminated_asynchronously (int factor = 1); + + bool search_limits_hit (); + + void terminate () { + LOG ("forcing asynchronous termination"); + termination_forced = true; + } + + // Reducing means determining useless clauses with 'reduce' in + // 'reduce.cpp' as well as root level satisfied clause and then removing + // those which are not used as reason anymore with garbage collection. + // + bool flushing (); + bool reducing (); + void protect_reasons (); + void mark_clauses_to_be_flushed (); + void mark_useless_redundant_clauses_as_garbage (); + bool propagate_out_of_order_units (); + void unprotect_reasons (); + void reduce (); + + // Garbage collection in 'collect.cpp' called from 'reduce' and during + // inprocessing and preprocessing. + // + int clause_contains_fixed_literal (Clause *); + void remove_falsified_literals (Clause *); + void mark_satisfied_clauses_as_garbage (); + void copy_clause (Clause *); + void flush_watches (int lit, Watches &); + size_t flush_occs (int lit); + void flush_all_occs_and_watches (); + void update_reason_references (); + void copy_non_garbage_clauses (); + void delete_garbage_clauses (); + void check_clause_stats (); + void check_var_stats (); + bool arenaing (); + void garbage_collection (); + + // only remove binary clauses from the watches + void remove_garbage_binaries (); + + // Set-up occurrence list counters and containers. + // + void init_occs (); + void init_bins (); + void init_noccs (); + void clear_noccs (); + void clear_occs (); + void reset_occs (); + void reset_bins (); + void reset_noccs (); + + // Operators on watches. + // + void init_watches (); + void connect_watches (bool irredundant_only = false); + void connect_binary_watches (); + void sort_watches (); + void clear_watches (); + void reset_watches (); + + // Regular forward subsumption checking in 'subsume.cpp'. + // + void strengthen_clause (Clause *, int); + void subsume_clause (Clause *subsuming, Clause *subsumed); + int subsume_check (Clause *subsuming, Clause *subsumed); + int try_to_subsume_clause (Clause *, vector &shrunken); + void reset_subsume_bits (); + bool subsume_round (); + void subsume (); + + // Covered clause elimination of large clauses. + // + void covered_literal_addition (int lit, Coveror &); + void asymmetric_literal_addition (int lit, Coveror &); + void cover_push_extension (int lit, Coveror &); + bool cover_propagate_asymmetric (int lit, Clause *ignore, Coveror &); + bool cover_propagate_covered (int lit, Coveror &); + bool cover_clause (Clause *c, Coveror &); + int64_t cover_round (); + bool cover (); + + // Strengthening through vivification in 'vivify.cpp'. + // + void demote_clause (Clause *); + void flush_vivification_schedule (std::vector &, int64_t &); + void vivify_increment_stats (const Vivifier &vivifier); + void vivify_subsume_clause (Clause *subsuming, Clause *subsumed); + void compute_tier_limits (Vivifier &); + void vivify_initialize (Vivifier &vivifier, int64_t &ticks); + inline void vivify_prioritize_leftovers (char, size_t prioritized, + std::vector &schedule); + bool consider_to_vivify_clause (Clause *candidate); + void vivify_sort_watched (Clause *c); + bool vivify_instantiate ( + const std::vector &, Clause *, + std::vector> &lrat_stack, + int64_t &ticks); + void vivify_analyze_redundant (Vivifier &, Clause *start, bool &); + void vivify_build_lrat (int, Clause *, + std::vector> &); + void vivify_chain_for_units (int lit, Clause *reason); + void vivify_strengthen (Clause *candidate); + void vivify_assign (int lit, Clause *); + void vivify_assume (int lit); + bool vivify_propagate (int64_t &); + void vivify_deduce (Clause *candidate, Clause *conflct, int implied, + Clause **, bool &); + bool vivify_clause (Vivifier &, Clause *candidate); + void vivify_analyze (Clause *start, bool &, Clause **, + const Clause *const, int implied, bool &); + bool vivify_shrinkable (const std::vector &sorted, Clause *c); + void vivify_round (Vivifier &, int64_t delta); + bool vivify (); + + // Compacting (shrinking internal variable tables) in 'compact.cpp' + // + bool compacting (); + void compact (); + + // Transitive reduction of binary implication graph in 'transred.cpp' + // + void transred (); + + // We monitor the maximum size and glue of clauses during 'reduce' and + // thus can predict if a redundant extended clause is likely to be kept in + // the next 'reduce' phase. These clauses are target of subsumption and + // vivification checks, in addition to irredundant clauses. Their + // variables are also marked as being 'added'. + // + bool likely_to_be_kept_clause (Clause *c) { + if (!c->redundant) + return true; + if (c->glue <= tier2[false]) + return true; + if (c->glue > lim.keptglue) + return false; + if (c->size > lim.keptsize) + return false; + return true; + } + + // We mark variables in added or shrunken clauses as 'subsume' candidates + // if the clause is likely to be kept in the next 'reduce' phase (see last + // function above). This gives a persistent (across consecutive + // interleaved search and inprocessing phases) set of variables which have + // to be reconsidered in subsumption checks, i.e., only clauses with + // 'subsume' marked variables are checked to be forward subsumed. + // A similar technique is used to reduce the effort in hyper ternary + // resolution to focus on variables in new ternary clauses. + // + void mark_subsume (int lit) { + Flags &f = flags (lit); + if (f.subsume) + return; + LOG ("marking %d as subsuming literal candidate", abs (lit)); + stats.mark.subsume++; + f.subsume = true; + } + void mark_ternary (int lit) { + Flags &f = flags (lit); + if (f.ternary) + return; + LOG ("marking %d as ternary resolution literal candidate", abs (lit)); + stats.mark.ternary++; + f.ternary = true; + } + void mark_factor (int lit) { + Flags &f = flags (lit); + const unsigned bit = bign (lit); + if (f.factor & bit) + return; + LOG ("marking %d as factor literal candidate", lit); + stats.mark.factor++; + f.factor |= bit; + } + void mark_added (int lit, int size, bool redundant); + void mark_added (Clause *); + + bool marked_subsume (int lit) const { return flags (lit).subsume; } + + // If irredundant clauses are removed or literals in clauses are removed, + // then variables in such clauses should be reconsidered to be eliminated + // through bounded variable elimination. In contrast to 'subsume' the + // 'elim' flag is restricted to 'irredundant' clauses only. For blocked + // clause elimination it is better to have a more precise signed version, + // which allows to independently mark positive and negative literals. + // + void mark_elim (int lit) { + Flags &f = flags (lit); + if (f.elim) + return; + LOG ("marking %d as elimination literal candidate", lit); + stats.mark.elim++; + f.elim = true; + } + void mark_block (int lit) { + Flags &f = flags (lit); + const unsigned bit = bign (lit); + if (f.block & bit) + return; + LOG ("marking %d as blocking literal candidate", lit); + stats.mark.block++; + f.block |= bit; + } + void mark_removed (int lit) { + mark_elim (lit); + mark_block (-lit); + } + void mark_removed (Clause *, int except = 0); + + // The following two functions are only used for testing & debugging. + + bool marked_block (int lit) const { + const Flags &f = flags (lit); + const unsigned bit = bign (lit); + return (f.block & bit) != 0; + } + void unmark_block (int lit) { + Flags &f = flags (lit); + const unsigned bit = bign (lit); + f.block &= ~bit; + } + + // During scheduling literals for blocked clause elimination we skip those + // literals which occur negated in a too large clause. + // + void mark_skip (int lit) { + Flags &f = flags (lit); + const unsigned bit = bign (lit); + if (f.skip & bit) + return; + LOG ("marking %d to be skipped as blocking literal", lit); + f.skip |= bit; + } + bool marked_skip (int lit) { + const Flags &f = flags (lit); + const unsigned bit = bign (lit); + return (f.skip & bit) != 0; + } + + // During decompose ignore literals where we already built LRAT chains + // + void mark_decomposed (int lit) { + Flags &f = flags (lit); + const unsigned bit = bign (lit); + assert ((f.marked_signed & bit) == 0); + sign_marked.push_back (lit); + f.marked_signed |= bit; + } + void unmark_decomposed (int lit) { + Flags &f = flags (lit); + const unsigned bit = bign (lit); + f.marked_signed &= ~bit; + } + bool marked_decomposed (int lit) { + const Flags &f = flags (lit); + const unsigned bit = bign (lit); + return (f.marked_signed & bit) != 0; + } + void clear_sign_marked_literals (); + + // Blocked Clause elimination in 'block.cpp'. + // + bool is_blocked_clause (Clause *c, int pivot); + void block_schedule (Blocker &); + size_t block_candidates (Blocker &, int lit); + Clause *block_impossible (Blocker &, int lit); + void block_literal_with_at_least_two_negative_occs (Blocker &, int lit); + void block_literal_with_one_negative_occ (Blocker &, int lit); + void block_pure_literal (Blocker &, int lit); + void block_reschedule_clause (Blocker &, int lit, Clause *); + void block_reschedule (Blocker &, int lit); + void block_literal (Blocker &, int lit); + bool block (); + + // Find gates in 'gates.cpp' for bounded variable substitution. + // + int second_literal_in_binary_clause_lrat (Clause *, int first); + int second_literal_in_binary_clause (Eliminator &, Clause *, int first); + void mark_binary_literals (Eliminator &, int pivot); + void find_and_gate (Eliminator &, int pivot); + void find_equivalence (Eliminator &, int pivot); + + bool get_ternary_clause (Clause *, int &, int &, int &); + bool match_ternary_clause (Clause *, int, int, int); + Clause *find_ternary_clause (int, int, int); + + bool get_clause (Clause *, vector &); + bool is_clause (Clause *, const vector &); + Clause *find_clause (const vector &); + void find_xor_gate (Eliminator &, int pivot); + + void find_if_then_else (Eliminator &, int pivot); + + Clause *find_binary_clause (int, int); + void find_gate_clauses (Eliminator &, int pivot); + void unmark_gate_clauses (Eliminator &); + + // mine definitions for kitten in 'definition.cpp' + // + void find_definition (Eliminator &, int); + void init_citten (); + void reset_citten (); + void citten_clear_track_log_terminate (); + + // Bounded variable elimination in 'elim.cpp'. + // + bool ineliminating (); + double compute_elim_score (unsigned lit); + void mark_redundant_clauses_with_eliminated_variables_as_garbage (); + void unmark_binary_literals (Eliminator &); + bool resolve_clauses (Eliminator &, Clause *, int pivot, Clause *, bool); + void mark_eliminated_clauses_as_garbage (Eliminator &, int pivot, bool &); + bool elim_resolvents_are_bounded (Eliminator &, int pivot); + void elim_update_removed_lit (Eliminator &, int lit); + void elim_update_removed_clause (Eliminator &, Clause *, int except = 0); + void elim_update_added_clause (Eliminator &, Clause *); + void elim_add_resolvents (Eliminator &, int pivot); + void elim_backward_clause (Eliminator &, Clause *); + void elim_backward_clauses (Eliminator &); + void elim_propagate (Eliminator &, int unit); + void elim_on_the_fly_self_subsumption (Eliminator &, Clause *, int); + void try_to_eliminate_variable (Eliminator &, int pivot, bool &); + void increase_elimination_bound (); + int elim_round (bool &completed, bool &); + void elim (bool update_limits = true); + + int64_t flush_elimfast_occs (int lit); + void elimfast_add_resolvents (Eliminator &, int pivot); + bool elimfast_resolvents_are_bounded (Eliminator &, int pivot); + void try_to_fasteliminate_variable (Eliminator &, int pivot, bool &); + int elimfast_round (bool &completed, bool &); + void elimfast (); + + // sweeping in 'sweep.cpp' + int sweep_solve (); + void sweep_set_kitten_ticks_limit (Sweeper &sweeper); + bool kitten_ticks_limit_hit (Sweeper &sweeper, const char *when); + void init_sweeper (Sweeper &sweeper); + void release_sweeper (Sweeper &sweeper); + void clear_sweeper (Sweeper &sweeper); + int sweep_repr (Sweeper &sweeper, int lit); + void add_literal_to_environment (Sweeper &sweeper, unsigned depth, int); + void sweep_clause (Sweeper &sweeper, unsigned depth, Clause *); + void sweep_add_clause (Sweeper &sweeper, unsigned depth); + void add_core (Sweeper &sweeper, unsigned core_idx); + void save_core (Sweeper &sweeper, unsigned core); + void clear_core (Sweeper &sweeper, unsigned core_idx); + void save_add_clear_core (Sweeper &sweeper); + void init_backbone_and_partition (Sweeper &sweeper); + void sweep_empty_clause (Sweeper &sweeper); + void sweep_refine_partition (Sweeper &sweeper); + void sweep_refine_backbone (Sweeper &sweeper); + void sweep_refine (Sweeper &sweeper); + void flip_backbone_literals (struct Sweeper &sweeper); + bool sweep_backbone_candidate (Sweeper &sweeper, int lit); + int64_t add_sweep_binary (sweep_proof_clause, int lit, int other); + bool scheduled_variable (Sweeper &sweeper, int idx); + void schedule_inner (Sweeper &sweeper, int idx); + void schedule_outer (Sweeper &sweeper, int idx); + int next_scheduled (Sweeper &sweeper); + void substitute_connected_clauses (Sweeper &sweeper, int lit, int other, + int64_t id); + void sweep_remove (Sweeper &sweeper, int lit); + void flip_partition_literals (struct Sweeper &sweeper); + const char *sweep_variable (Sweeper &sweeper, int idx); + bool scheduable_variable (Sweeper &sweeper, int idx, size_t *occ_ptr); + unsigned schedule_all_other_not_scheduled_yet (Sweeper &sweeper); + bool sweep_equivalence_candidates (Sweeper &sweeper, int lit, int other); + unsigned reschedule_previously_remaining (Sweeper &sweeper); + unsigned incomplete_variables (); + void mark_incomplete (Sweeper &sweeper); + unsigned schedule_sweeping (Sweeper &sweeper); + void unschedule_sweeping (Sweeper &sweeper, unsigned swept, + unsigned scheduled); + bool sweep (); + void sweep_dense_propagate (Sweeper &sweeper); + void sweep_sparse_mode (); + void sweep_dense_mode_and_watch_irredundant (); + void sweep_substitute_lrat (Clause *c, int64_t id); + void sweep_substitute_new_equivalences (Sweeper &sweeper); + void sweep_update_noccs (Clause *c); + void delete_sweep_binary (const sweep_binary &sb); + bool can_sweep_clause (Clause *c); + bool sweep_flip (int); + int sweep_flip_and_implicant (int); + bool sweep_extract_fixed (Sweeper &sweeper, int lit); + + // factor + void factor_mode (); + void reset_factor_mode (); + double tied_next_factor_score (int); + Quotient *new_quotient (Factoring &, int); + void release_quotients (Factoring &); + size_t first_factor (Factoring &, int); + void clear_nounted (vector &); + void clear_flauses (vector &); + Quotient *best_quotient (Factoring &, size_t *); + int next_factor (Factoring &, unsigned *); + void factorize_next (Factoring &, int, unsigned); + void resize_factoring (Factoring &factoring, int lit); + void flush_unmatched_clauses (Quotient *); + void add_self_subsuming_factor (Quotient *, Quotient *); + bool self_subsuming_factor (Quotient *); + void add_factored_divider (Quotient *, int); + void blocked_clause (Quotient *q, int); + void add_factored_quotient (Quotient *, int not_fresh); + void eagerly_remove_from_occurences (Clause *c); + void delete_unfactored (Quotient *q); + void update_factored (Factoring &factoring, Quotient *q); + bool apply_factoring (Factoring &factoring, Quotient *q); + void update_factor_candidate (Factoring &, int); + void schedule_factorization (Factoring &); + bool run_factorization (int64_t limit); + bool factor (); + int get_new_extension_variable (); + Clause *new_factor_clause (); + + // instantiate + // + void inst_assign (int lit); + bool inst_propagate (); + void collect_instantiation_candidates (Instantiator &); + bool instantiate_candidate (int lit, Clause *); + void instantiate (Instantiator &); + + void new_trail_level (int lit); + + // Hyper ternary resolution. + // + bool ternary_find_binary_clause (int, int); + bool ternary_find_ternary_clause (int, int, int); + Clause *new_hyper_ternary_resolved_clause (bool red); + Clause *new_hyper_ternary_resolved_clause_and_watch (bool red, bool); + bool hyper_ternary_resolve (Clause *, int, Clause *); + void ternary_lit (int pivot, int64_t &steps, int64_t &htrs); + void ternary_idx (int idx, int64_t &steps, int64_t &htrs); + bool ternary_round (int64_t &steps, int64_t &htrs); + bool ternary (); + + // Probing in 'probe.cpp'. + // + bool inprobing (); + void failed_literal (int lit); + void probe_lrat_for_units (int lit); + void probe_assign_unit (int lit); + void probe_assign_decision (int lit); + void probe_assign (int lit, int parent); + void mark_duplicated_binary_clauses_as_garbage (); + int get_parent_reason_literal (int lit); + void set_parent_reason_literal (int lit, int reason); + void clean_probehbr_lrat (); + void init_probehbr_lrat (); + void get_probehbr_lrat (int lit, int uip); + void set_probehbr_lrat (int lit, int uip); + void probe_post_dominator_lrat (vector &, int, int); + void probe_dominator_lrat (int dom, Clause *reason); + int probe_dominator (int a, int b); + int hyper_binary_resolve (Clause *); + void probe_propagate2 (); + bool probe_propagate (); + bool is_binary_clause (Clause *c, int &, int &); + void generate_probes (); + void flush_probes (); + int next_probe (); + bool probe (); + void inprobe (bool update_limits = true); + + // ProbSAT/WalkSAT implementation called initially or from 'rephase'. + // + void walk_save_minimum (Walker &); + Clause *walk_pick_clause (Walker &); + unsigned walk_break_value (int lit); + int walk_pick_lit (Walker &, Clause *); + void walk_flip_lit (Walker &, int lit); + int walk_round (int64_t limit, bool prev); + void walk (); + + // Detect strongly connected components in the binary implication graph + // (BIG) and equivalent literal substitution (ELS) in 'decompose.cpp'. + // + void decompose_conflicting_scc_lrat (DFS *dfs, vector &); + void build_lrat_for_clause (const vector> &dfs_chains, + bool invert = false); + vector decompose_analyze_binary_clauses (DFS *dfs, int from); + void decompose_analyze_binary_chain (DFS *dfs, int); + bool decompose_round (); + void decompose (); + + void reset_limits (); // Reset after 'solve' call. + + // Try flipping a literal while not falsifying a model. + + bool flip (int lit); + bool flippable (int lit); + + // Assumption handling. + // + void assume_analyze_literal (int lit); + void assume_analyze_reason (int lit, Clause *reason); + void assume (int); // New assumption literal. + bool failed (int lit); // Literal failed assumption? + void reset_assumptions (); // Reset after 'solve' call. + void sort_and_reuse_assumptions (); // reorder the assumptions in order to + // reuse parts of the trail + void failing (); // Prepare failed assumptions. + + bool assumed (int lit) { // Marked as assumption. + Flags &f = flags (lit); + const unsigned bit = bign (lit); + return (f.assumed & bit) != 0; + } + + // Add temporary clause as constraint. + // + void constrain (int); // Add literal to constraint. + bool + failed_constraint (); // Was constraint used to proof unsatisfiablity? + void reset_constraint (); // Reset after 'solve' call. + + // Propagate the current set of assumptions and return the + // non-witness assigned literals + int propagate_assumptions (); + void implied (std::vector &entrailed); + + // Forcing decision variables to a certain phase. + // + void phase (int lit); + void unphase (int lit); + + // Globally blocked clause elimination. + // + bool is_autarky_literal (int lit) const; + bool is_conditional_literal (int lit) const; + void mark_as_conditional_literal (int lit); + void unmark_as_conditional_literal (int lit); + // + bool is_in_candidate_clause (int lit) const; + void mark_in_candidate_clause (int lit); + void unmark_in_candidate_clause (int lit); + // + void condition_assign (int lit); + void condition_unassign (int lit); + // + bool conditioning (); + long condition_round (long unassigned_literal_propagation_limit); + void condition (bool update_limits = true); + + // Part on picking the next decision in 'decide.cpp'. + // + bool satisfied (); + int next_decision_variable_on_queue (); + int next_decision_variable_with_best_score (); + int next_decision_variable (); + int decide_phase (int idx, bool target); + int likely_phase (int idx); + bool better_decision (int lit, int other); + int decide (); // 0=decision, 20=failed + + // Internal functions to enable explicit search limits. + // + void limit_terminate (int); + void limit_decisions (int); // Force decision limit. + void limit_conflicts (int); // Force conflict limit. + void limit_preprocessing (int); // Enable 'n' preprocessing rounds. + void limit_local_search (int); // Enable 'n' local search rounds. + + // External versions can access limits by 'name'. + // + static bool is_valid_limit (const char *name); + bool limit (const char *name, int); // 'true' if 'name' valid + + // Set all the CDCL search limits and increments for scheduling + // inprocessing, restarts, clause database reductions, etc. + // + void init_report_limits (); + void init_preprocessing_limits (); + void init_search_limits (); + + // The computed averages are local to the 'stable' and 'unstable' phase. + // Their main use is to be reported in 'report', except for the 'glue' + // averages, which are used to schedule (prohibit actually) restarts + // during 'unstable' phases ('stable' phases use reluctant doubling). + // + void init_averages (); + void swap_averages (); + + int try_to_satisfy_formula_by_saved_phases (); + void produce_failed_assumptions (); + + // Main solve & search functions in 'internal.cpp'. + // + // We have three pre-solving techniques. These consist of preprocessing, + // local search and searching for lucky phases, which in full solving + // mode except for the last are usually optional and then followed by + // the main CDCL search loop with inprocessing. If only preprocessing + // is requested from 'External::simplify' only preprocessing is called + // though. This is all orchestrated by the 'solve' function. + // + int already_solved (); + int restore_clauses (); + bool preprocess_round (int round); + void preprocess_quickly (); + int preprocess (); + int local_search_round (int round); + int local_search (); + int lucky_phases (); + int cdcl_loop_with_inprocessing (); + void reset_solving (); + int solve (bool preprocess_only = false); + void finalize (int); + + // + int lookahead (); + CubesWithStatus generate_cubes (int, int); + int most_occurring_literal (); + int lookahead_probing (); + int lookahead_next_probe (); + void lookahead_flush_probes (); + void lookahead_generate_probes (); + std::vector lookahead_populate_locc (); + int lookahead_locc (const std::vector &); + + bool terminating_asked (); + +#ifndef QUIET + // Built in profiling in 'profile.cpp' (see also 'profile.hpp'). + // + void start_profiling (Profile &p, double); + void stop_profiling (Profile &p, double); + + double update_profiles (); // Returns 'time ()'. + void print_profile (); +#endif + + // Get the value of an internal literal: -1=false, 0=unassigned, 1=true. + // We use a redundant table for both negative and positive literals. This + // allows a branch-less check for the value of literal and is considered + // substantially faster than negating the result if the argument is + // negative. We also avoid taking the absolute value. + // + signed char val (int lit) const { + assert (-max_var <= lit); + assert (lit); + assert (lit <= max_var); + return vals[lit]; + } + + // As suggested by Matt Ginsberg it might be useful to factor-out a common + // setter function for setting and resetting the value of a literal. + // + void set_val (int lit, signed char val) { + assert (-1 <= val); + assert (val <= 1); + assert (-max_var <= lit); + assert (lit); + assert (lit <= max_var); + vals[lit] = val; + vals[-lit] = -val; + } + + // As 'val' but restricted to the root-level value of a literal. + // It is not that time critical and also needs to check the decision level + // of the variable anyhow. + // + int fixed (int lit) { + assert (-max_var <= lit); + assert (lit); + assert (lit <= max_var); + const int idx = vidx (lit); + int res = vals[idx]; + if (res && vtab[idx].level) + res = 0; + if (lit < 0) + res = -res; + return res; + } + + // Map back an internal literal to an external. + // + int externalize (int lit) { + assert (lit != INT_MIN); + const int idx = abs (lit); + assert (idx); + assert (idx <= max_var); + int res = i2e[idx]; + if (lit < 0) + res = -res; + return res; + } + + // Explicit freezing and melting of variables. + // + void freeze (int lit) { + int idx = vidx (lit); + if ((size_t) idx >= frozentab.size ()) { + size_t new_vsize = vsize ? 2 * vsize : 1 + (size_t) max_var; + while (new_vsize <= (size_t) max_var) + new_vsize *= 2; + frozentab.resize (new_vsize); + } + unsigned &ref = frozentab[idx]; + if (ref < UINT_MAX) { + ref++; + LOG ("variable %d frozen %u times", idx, ref); + } else + LOG ("variable %d remains frozen forever", idx); + } + void melt (int lit) { + int idx = vidx (lit); + unsigned &ref = frozentab[idx]; + if (ref < UINT_MAX) { + if (!--ref) { + if (relevanttab[idx]) { + LOG ("variable %d is observed, can not be completely molten", + idx); + ref++; + } else + LOG ("variable %d completely molten", idx); + } else + LOG ("variable %d melted once but remains frozen %u times", lit, + ref); + } else + LOG ("variable %d remains frozen forever", idx); + } + bool frozen (int lit) { + return (size_t) vidx (lit) < frozentab.size () && + frozentab[vidx (lit)] > 0; + } + + // Congruence closure + bool extract_gates (); + + // Parsing functions in 'parse.cpp'. + // + const char *parse_dimacs (FILE *); + const char *parse_dimacs (const char *); + const char *parse_solution (const char *); + + // Enable and disable proof logging and checking. + // + void new_proof_on_demand (); + void force_lrat (); // sets lrat=true + void resize_unit_clauses_idx (); // resizes unit_clauses_idx + void close_trace (bool stats = false); // Stop proof tracing. + void flush_trace (bool stats = false); // Flush proof trace file. + void trace (File *); // Start write proof file. + void check (); // Enable online proof checking. + + void connect_proof_tracer (Tracer *tracer, bool antecedents, + bool finalize_clauses = false); + void connect_proof_tracer (InternalTracer *tracer, bool antecedents, + bool finalize_clauses = false); + void connect_proof_tracer (StatTracer *tracer, bool antecedents, + bool finalize_clauses = false); + void connect_proof_tracer (FileTracer *tracer, bool antecedents, + bool finalize_clauses = false); + bool disconnect_proof_tracer (Tracer *tracer); + bool disconnect_proof_tracer (StatTracer *tracer); + bool disconnect_proof_tracer (FileTracer *tracer); + void conclude_unsat (); + void reset_concluded (); + + // Dump to '' as DIMACS for debugging. + // + void dump (Clause *); + void dump (); + + // Export and traverse all irredundant (non-unit) clauses. + // + bool traverse_clauses (ClauseIterator &); + + // Export and traverse all irredundant (non-unit) clauses. + // + bool traverse_constraint (ClauseIterator &); + + /*----------------------------------------------------------------------*/ + + double solve_time (); // accumulated time spent in 'solve ()' + + double process_time () const; // since solver was initialized + double real_time () const; // since solver was initialized + + double time () { return opts.realtime ? real_time () : process_time (); } + + // Regularly reports what is going on in 'report.cpp'. + // + void report (char type, int verbose_level = 0); + void report_solving (int); + + void print_statistics (); + void print_resource_usage (); + + /*----------------------------------------------------------------------*/ + +#ifndef QUIET + + void print_prefix (); + + // Non-verbose messages and warnings, i.e., always printed unless 'quiet' + // is set, which disables messages at run-time, or even 'QUIET' is defined + // through the configuration option './configure --quiet', which disables + // such messages completely at compile-time. + // + void vmessage (const char *, va_list &); + void message (const char *, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); + void message (); // empty line + + // Verbose messages with explicit verbose 'level' controlled by + // 'opts.verbose' (verbose level '0' gives the same as 'message'). + // + void vverbose (int level, const char *fmt, va_list &); + void verbose (int level, const char *fmt, ...) + CADICAL_ATTRIBUTE_FORMAT (3, 4); + void verbose (int level); + + // This is for printing section headers in the form + // + // c ---- [ ] --------------------- + // + // nicely aligned (and of course is ignored if 'quiet' is set). + // + void section (const char *title); + + // Print verbose message about phases if 'opts.verbose > 1' (but not if + // 'quiet' is set). Note that setting 'log' or '-l' forces all verbose + // output (and also ignores 'quiet' set to true'). The 'phase' argument + // is used to print a 'phase' prefix for the message as follows: + // + // c [<phase>] ... + // + void phase (const char *phase, const char *, ...) + CADICAL_ATTRIBUTE_FORMAT (3, 4); + + // Same as the last 'phase' above except that the prefix gets a count: + // + // c [<phase>-<count>] ... + // + void phase (const char *phase, int64_t count, const char *, ...) + CADICAL_ATTRIBUTE_FORMAT (4, 5); +#endif + + // Print error messages which are really always printed (even if 'quiet' + // is set). This leads to exit the current process with exit status '1'. + // + // TODO add possibility to use a call back instead of calling exit. + // + void error_message_end (); + void verror (const char *, va_list &); + void error (const char *, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); + void error_message_start (); + + // Warning messages. + // + void warning (const char *, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); +}; + +// Fatal internal error which leads to abort. +// +void fatal_message_start (); +void fatal_message_end (); +void fatal (const char *, ...) CADICAL_ATTRIBUTE_FORMAT (1, 2); + +/*------------------------------------------------------------------------*/ + +// Has to be put here, i.e., not into 'score.hpp', since we need the +// definition of 'Internal::score' above (after '#include "score.hpp"'). + +inline bool score_smaller::operator() (unsigned a, unsigned b) { + + // Avoid computing twice 'abs' in 'score ()'. + // + assert (1 <= a); + assert (a <= (unsigned) internal->max_var); + assert (1 <= b); + assert (b <= (unsigned) internal->max_var); + double s = internal->stab[a]; + double t = internal->stab[b]; + + if (s < t) + return true; + if (s > t) + return false; + + return a > b; +} + +/*------------------------------------------------------------------------*/ + +// Implemented here for keeping it all inline (requires Internal::fixed). + +inline int External::fixed (int elit) const { + assert (elit); + assert (elit != INT_MIN); + int eidx = abs (elit); + if (eidx > max_var) + return 0; + int ilit = e2i[eidx]; + if (!ilit) + return 0; + if (elit < 0) + ilit = -ilit; + return internal->fixed (ilit); +} + +/*------------------------------------------------------------------------*/ + +// We want to have termination checks inlined, particularly the first +// function which appears in preprocessor loops. Even though this first +// 'termination_forced' is set asynchronously, this should not lead to a +// data race issue (it also has been declared 'volatile'). + +inline bool Internal::terminated_asynchronously (int factor) { + // First way of asynchronous termination is through 'terminate' which sets + // the 'termination_forced' flag directly. The second way is through a + // call back to a 'terminator' if it is non-zero, which however is costly. + // + if (termination_forced) { + LOG ("termination asynchronously forced"); + return true; + } + + // This is only for testing and debugging asynchronous termination calls. + // In production code this could be removed but then should not be costly + // and keeping it will allow to test correctness of asynchronous + // termination on the production platform too. After this triggers we + // have to set the 'termination_forced' flag, such that subsequent calls + // to this function do not check this again. + // + if (lim.terminate.forced) { + assert (lim.terminate.forced > 0); + if (lim.terminate.forced-- == 1) { + LOG ("internally forcing termination"); + termination_forced = true; + return true; + } + LOG ("decremented internal forced termination limit to %d", + lim.terminate.forced); + } + + // The second way of asynchronous termination is through registering and + // calling an external 'Terminator' object. This is of course more costly + // than just checking a (volatile though) boolean flag, particularly in + // tight loops. To avoid this cost we only call the terminator in + // intervals of 'opts.terminateint', which in addition can be scaled up by + // the argument 'factor'. If the terminator returns 'true' we set the + // 'termination_forced' flag to 'true' in order to remember the + // termination status and to avoid the terminator again. Setting this + // flag leads to the first test above to succeed in subsequent calls. + // + if (external->terminator && !lim.terminate.check--) { + assert (factor > 0); + assert (INT_MAX / factor > opts.terminateint); + lim.terminate.check = factor * opts.terminateint; + if (external->terminator->terminate ()) { + termination_forced = true; // Cache it. + LOG ("connected terminator forces termination"); + return true; + } + } + + return false; +} + +/*------------------------------------------------------------------------*/ + +inline bool Internal::search_limits_hit () { + assert (!preprocessing); + assert (!localsearching); + + if (lim.conflicts >= 0 && stats.conflicts >= lim.conflicts) { + LOG ("conflict limit %" PRId64 " reached", lim.conflicts); + return true; + } + + if (lim.decisions >= 0 && stats.decisions >= lim.decisions) { + LOG ("decision limit %" PRId64 " reached", lim.decisions); + return true; + } + + return false; +} + +/*------------------------------------------------------------------------*/ + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/inttypes.hpp b/src/sat/cadical/inttypes.hpp new file mode 100644 index 000000000..b1b95872e --- /dev/null +++ b/src/sat/cadical/inttypes.hpp @@ -0,0 +1,28 @@ +#ifndef _inttypes_h_INCLUDED +#define _inttypes_h_INCLUDED + +// This is an essence a wrapper around '<cinttypes>' respectively +// 'inttypes.h' in order to please the 'MinGW' cross-compiler (we are using +// 'i686-w64-mingw32-gcc') to produce correct 'printf' style formatting for +// 64-bit numbers as this does not work out-of-the-box (which is also very +// annoying). This also produces lots of warnings (through '-Wformat' and +// the corresponding 'attribute' declaration for 'printf' style functions). +// Again 'MinGW' is not fully standard compliant here and we have to cover +// up for that manually. + +// We repeat the code on making this work which is also contained in +// 'cadical.hpp' as we do not want to require users of the library to +// include another header file (like this one) beside 'cadical.hpp'. + +#ifndef PRINTF_FORMAT +#ifdef __MINGW32__ +#define __USE_MINGW_ANSI_STDIO 1 +#define PRINTF_FORMAT __MINGW_PRINTF_FORMAT +#else +#define PRINTF_FORMAT printf +#endif +#endif + +#include <cinttypes> + +#endif diff --git a/src/sat/cadical/ipasir.cpp b/src/sat/cadical/ipasir.cpp new file mode 100644 index 000000000..0eae5e95f --- /dev/null +++ b/src/sat/cadical/ipasir.cpp @@ -0,0 +1,43 @@ +#include "ipasir.h" +#include "ccadical.h" + +extern "C" { + +const char *ipasir_signature () { return ccadical_signature (); } + +void *ipasir_init () { return ccadical_init (); } + +void ipasir_release (void *solver) { + ccadical_release ((CCaDiCaL *) solver); +} + +void ipasir_add (void *solver, int lit) { + ccadical_add ((CCaDiCaL *) solver, lit); +} + +void ipasir_assume (void *solver, int lit) { + ccadical_assume ((CCaDiCaL *) solver, lit); +} + +int ipasir_solve (void *solver) { + return ccadical_solve ((CCaDiCaL *) solver); +} + +int ipasir_val (void *solver, int lit) { + return ccadical_val ((CCaDiCaL *) solver, lit); +} + +int ipasir_failed (void *solver, int lit) { + return ccadical_failed ((CCaDiCaL *) solver, lit); +} + +void ipasir_set_terminate (void *solver, void *state, + int (*terminate) (void *state)) { + ccadical_set_terminate ((CCaDiCaL *) solver, state, terminate); +} + +void ipasir_set_learn (void *solver, void *state, int max_length, + void (*learn) (void *state, int *clause)) { + ccadical_set_learn ((CCaDiCaL *) solver, state, max_length, learn); +} +} diff --git a/src/sat/cadical/ipasir.h b/src/sat/cadical/ipasir.h new file mode 100644 index 000000000..0da79fe14 --- /dev/null +++ b/src/sat/cadical/ipasir.h @@ -0,0 +1,37 @@ +#ifndef _ipasir_h_INCLUDED +#define _ipasir_h_INCLUDED + +/*------------------------------------------------------------------------*/ +#ifdef __cplusplus +extern "C" { +#endif +/*------------------------------------------------------------------------*/ + +// Here are the declarations for the actual IPASIR functions, which is the +// generic incremental reentrant SAT solver API used for instance in the SAT +// competition. The other 'C' API in 'ccadical.h' is (more) type safe and +// has additional functions only supported by the CaDiCaL library. Please +// also refer to our SAT Race 2015 article in the Journal of AI from 2016. + +const char *ipasir_signature (void); +void *ipasir_init (void); +void ipasir_release (void *solver); +void ipasir_add (void *solver, int lit); +void ipasir_assume (void *solver, int lit); +int ipasir_solve (void *solver); +int ipasir_val (void *solver, int lit); +int ipasir_failed (void *solver, int lit); + +void ipasir_set_terminate (void *solver, void *state, + int (*terminate) (void *state)); + +void ipasir_set_learn (void *solver, void *state, int max_length, + void (*learn) (void *state, int *clause)); + +/*------------------------------------------------------------------------*/ +#ifdef __cplusplus +} +#endif +/*------------------------------------------------------------------------*/ + +#endif diff --git a/src/sat/cadical/kitten.c b/src/sat/cadical/kitten.c new file mode 100644 index 000000000..7cce86c5b --- /dev/null +++ b/src/sat/cadical/kitten.c @@ -0,0 +1,2593 @@ +#include "kitten.h" +#include "random.h" +#include "stack.h" + +#include <assert.h> +#include <inttypes.h> +#include <limits.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +typedef signed char value; + +static void die (const char *fmt, ...) { + fputs ("kitten: error: ", stderr); + va_list ap; + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + fputc ('\n', stderr); + exit (1); +} + +static inline void *kitten_calloc (size_t n, size_t size) { + void *res = calloc (n, size); + if (n && size && !res) + die ("out of memory allocating '%zu * %zu' bytes", n, size); + return res; +} + +#define CALLOC(P, N) \ + do { \ + (P) = kitten_calloc (N, sizeof *(P)); \ + } while (0) +#define DEALLOC(P, N) free (P) + +#undef ENLARGE_STACK + +#define ENLARGE_STACK(S) \ + do { \ + assert (FULL_STACK (S)); \ + const size_t SIZE = SIZE_STACK (S); \ + const size_t OLD_CAPACITY = CAPACITY_STACK (S); \ + const size_t NEW_CAPACITY = OLD_CAPACITY ? 2 * OLD_CAPACITY : 1; \ + const size_t BYTES = NEW_CAPACITY * sizeof *(S).begin; \ + (S).begin = realloc ((S).begin, BYTES); \ + if (!(S).begin) \ + die ("out of memory reallocating '%zu' bytes", BYTES); \ + (S).allocated = (S).begin + NEW_CAPACITY; \ + (S).end = (S).begin + SIZE; \ + } while (0) + +// Beside allocators above also use stand alone statistics counters. + +#define INC(NAME) \ + do { \ + statistics *statistics = &kitten->statistics; \ + assert (statistics->NAME < UINT64_MAX); \ + statistics->NAME++; \ + } while (0) + +#define ADD(NAME, DELTA) \ + do { \ + statistics *statistics = &kitten->statistics; \ + assert (statistics->NAME <= UINT64_MAX - (DELTA)); \ + statistics->NAME += (DELTA); \ + } while (0) + +#define KITTEN_TICKS (kitten->statistics.kitten_ticks) + +#define INVALID UINT_MAX +#define MAX_VARS ((1u << 31) - 1) + +#define CORE_FLAG (1u) +#define LEARNED_FLAG (2u) + +// clang-format off + +typedef struct kar kar; +typedef struct kink kink; +typedef struct klause klause; +typedef STACK (unsigned) klauses; +typedef unsigneds katches; + +// clang-format on + +struct kar { + unsigned level; + unsigned reason; +}; + +struct kink { + unsigned next; + unsigned prev; + uint64_t stamp; +}; + +struct klause { + unsigned aux; + unsigned size; + unsigned flags; + unsigned lits[1]; +}; + +typedef struct statistics statistics; + +struct statistics { + uint64_t learned; + uint64_t original; + uint64_t kitten_flip; + uint64_t kitten_flipped; + uint64_t kitten_sat; + uint64_t kitten_solved; + uint64_t kitten_conflicts; + uint64_t kitten_decisions; + uint64_t kitten_propagations; + uint64_t kitten_ticks; + uint64_t kitten_unknown; + uint64_t kitten_unsat; +}; + +typedef struct kimits kimits; + +struct kimits { + uint64_t ticks; +}; + +struct kitten { + // First zero initialized field in 'clear_kitten' is 'status'. + // + int status; + +#if defined(LOGGING) + bool logging; +#endif + bool antecedents; + bool learned; + + unsigned level; + unsigned propagated; + unsigned unassigned; + unsigned inconsistent; + unsigned failing; + + uint64_t generator; + + size_t lits; + size_t evars; + + size_t end_original_ref; + + struct { + unsigned first, last; + uint64_t stamp; + unsigned search; + } queue; + + // The 'size' field below is the first not zero reinitialized field + // by 'memset' in 'clear_kitten' (after 'kissat'). + + size_t size; + size_t esize; + + kar *vars; + kink *links; + value *marks; + value *values; + bool *failed; + unsigned char *phases; + unsigned *import; + katches *watches; + + unsigneds analyzed; + unsigneds assumptions; + unsigneds core; + unsigneds rcore; + unsigneds eclause; + unsigneds export; + unsigneds klause; + unsigneds klauses; + unsigneds resolved; + unsigneds trail; + unsigneds units; + unsigneds prime[2]; + + kimits limits; + int (*terminator) (void *); + void *terminator_data; + unsigneds clause; + uint64_t initialized; + statistics statistics; +}; + +/*------------------------------------------------------------------------*/ + +static inline bool is_core_klause (klause *c) { + return c->flags & CORE_FLAG; +} + +static inline bool is_learned_klause (klause *c) { + return c->flags & LEARNED_FLAG; +} + +static inline void set_core_klause (klause *c) { c->flags |= CORE_FLAG; } + +static inline void unset_core_klause (klause *c) { c->flags &= ~CORE_FLAG; } + +static inline klause *dereference_klause (kitten *kitten, unsigned ref) { + unsigned *res = BEGIN_STACK (kitten->klauses) + ref; + assert (res < END_STACK (kitten->klauses)); + return (klause *) res; +} + +static inline unsigned reference_klause (kitten *kitten, const klause *c) { + const unsigned *const begin = BEGIN_STACK (kitten->klauses); + const unsigned *p = (const unsigned *) c; + assert (begin <= p); + assert (p < END_STACK (kitten->klauses)); + const unsigned res = p - begin; + return res; +} + +/*------------------------------------------------------------------------*/ + +#define KATCHES(KIT) (kitten->watches[assert ((KIT) < kitten->lits), (KIT)]) + +#define all_klauses(C) \ + klause *C = begin_klauses (kitten), *end_##C = end_klauses (kitten); \ + (C) != end_##C; \ + (C) = next_klause (kitten, C) + +#define all_original_klauses(C) \ + klause *C = begin_klauses (kitten), \ + *end_##C = end_original_klauses (kitten); \ + (C) != end_##C; \ + (C) = next_klause (kitten, C) + +#define all_learned_klauses(C) \ + klause *C = begin_learned_klauses (kitten), \ + *end_##C = end_klauses (kitten); \ + (C) != end_##C; \ + (C) = next_klause (kitten, C) + +#define all_kits(KIT) \ + size_t KIT = 0, KIT_##END = kitten->lits; \ + KIT != KIT_##END; \ + KIT++ + +#define BEGIN_KLAUSE(C) (C)->lits + +#define END_KLAUSE(C) (BEGIN_KLAUSE (C) + (C)->size) + +#define all_literals_in_klause(KIT, C) \ + unsigned KIT, *KIT##_PTR = BEGIN_KLAUSE (C), \ + *KIT##_END = END_KLAUSE (C); \ + KIT##_PTR != KIT##_END && ((KIT = *KIT##_PTR), true); \ + ++KIT##_PTR + +#define all_antecedents(REF, C) \ + unsigned REF, *REF##_PTR = antecedents (C), \ + *REF##_END = REF##_PTR + (C)->aux; \ + REF##_PTR != REF##_END && ((REF = *REF##_PTR), true); \ + ++REF##_PTR + +#ifdef LOGGING + +#define logging (kitten->logging) + +static void log_basic (kitten *, const char *, ...) + __attribute__ ((format (printf, 2, 3))); + +static void log_basic (kitten *kitten, const char *fmt, ...) { + assert (logging); + printf ("c KITTEN %u ", kitten->level); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + fputc ('\n', stdout); + fflush (stdout); +} + +static void log_reference (kitten *, unsigned, const char *, ...) + __attribute__ ((format (printf, 3, 4))); + +static void log_reference (kitten *kitten, unsigned ref, const char *fmt, + ...) { + klause *c = dereference_klause (kitten, ref); + assert (logging); + printf ("c KITTEN %u ", kitten->level); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + if (is_learned_klause (c)) { + fputs (" learned", stdout); + if (c->aux) + printf ("[%u]", c->aux); + } else { + fputs (" original", stdout); + if (c->aux != INVALID) + printf ("[%u]", c->aux); + } + printf (" size %u clause[%u]", c->size, ref); + value *values = kitten->values; + kar *vars = kitten->vars; + for (all_literals_in_klause (lit, c)) { + printf (" %u", lit); + const value value = values[lit]; + if (value) + printf ("@%u=%d", vars[lit / 2].level, (int) value); + } + fputc ('\n', stdout); + fflush (stdout); +} + +static void log_literals (kitten *, unsigned *, unsigned, const char *, ...) + __attribute__ ((format (printf, 4, 5))); + +static void log_literals (kitten *kitten, unsigned *lits, unsigned size, + const char *fmt, ...) { + assert (logging); + printf ("c KITTEN %u ", kitten->level); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + value *values = kitten->values; + kar *vars = kitten->vars; + for (unsigned i = 0; i < size; i++) { + const unsigned lit = lits[i]; + printf (" %u", lit); + const value value = values[lit]; + if (value) + printf ("@%u=%d", vars[lit / 2].level, (int) value); + } + fputc ('\n', stdout); + fflush (stdout); +} + +#define LOG(...) \ + do { \ + if (logging) \ + log_basic (kitten, __VA_ARGS__); \ + } while (0) + +#define ROG(...) \ + do { \ + if (logging) \ + log_reference (kitten, __VA_ARGS__); \ + } while (0) + +#define LOGLITS(...) \ + do { \ + if (logging) \ + log_literals (kitten, __VA_ARGS__); \ + } while (0) + +#else + +#define LOG(...) \ + do { \ + } while (0) +#define ROG(...) \ + do { \ + } while (0) +#define LOGLITS(...) \ + do { \ + } while (0) + +#endif + +static void check_queue (kitten *kitten) { +#ifdef CHECK_KITTEN + const unsigned vars = kitten->lits / 2; + unsigned found = 0, prev = INVALID; + kink *links = kitten->links; + uint64_t stamp = 0; + for (unsigned idx = kitten->queue.first, next; idx != INVALID; + idx = next) { + kink *link = links + idx; + assert (link->prev == prev); + assert (!found || stamp < link->stamp); + assert (link->stamp < kitten->queue.stamp); + stamp = link->stamp; + next = link->next; + prev = idx; + found++; + } + assert (found == vars); + unsigned next = INVALID; + found = 0; + for (unsigned idx = kitten->queue.last, prev; idx != INVALID; + idx = prev) { + kink *link = links + idx; + assert (link->next == next); + prev = link->prev; + next = idx; + found++; + } + assert (found == vars); + value *values = kitten->values; + bool first = true; + for (unsigned idx = kitten->queue.search, next; idx != INVALID; + idx = next) { + kink *link = links + idx; + next = link->next; + const unsigned lit = 2 * idx; + assert (first || values[lit]); + first = false; + } +#else + (void) kitten; +#endif +} + +static void update_search (kitten *kitten, unsigned idx) { + if (kitten->queue.search == idx) + return; + kitten->queue.search = idx; + LOG ("search updated to %u stamped %" PRIu64, idx, + kitten->links[idx].stamp); +} + +static void enqueue (kitten *kitten, unsigned idx) { + LOG ("enqueue %u", idx); + kink *links = kitten->links; + kink *l = links + idx; + const unsigned last = kitten->queue.last; + if (last == INVALID) + kitten->queue.first = idx; + else + links[last].next = idx; + l->prev = last; + l->next = INVALID; + kitten->queue.last = idx; + l->stamp = kitten->queue.stamp++; + LOG ("stamp %" PRIu64, l->stamp); +} + +static void dequeue (kitten *kitten, unsigned idx) { + LOG ("dequeue %u", idx); + kink *links = kitten->links; + kink *l = links + idx; + const unsigned prev = l->prev; + const unsigned next = l->next; + if (prev == INVALID) + kitten->queue.first = next; + else + links[prev].next = next; + if (next == INVALID) + kitten->queue.last = prev; + else + links[next].prev = prev; +} + +static void init_queue (kitten *kitten, size_t old_vars, size_t new_vars) { + for (size_t idx = old_vars; idx < new_vars; idx++) { + assert (!kitten->values[2 * idx]); + assert (kitten->unassigned < UINT_MAX); + kitten->unassigned++; + enqueue (kitten, idx); + } + LOG ("initialized decision queue from %zu to %zu", old_vars, new_vars); + update_search (kitten, kitten->queue.last); + check_queue (kitten); +} + +static void initialize_kitten (kitten *kitten) { + kitten->queue.first = INVALID; + kitten->queue.last = INVALID; + kitten->inconsistent = INVALID; + kitten->failing = INVALID; + kitten->queue.search = INVALID; + kitten->terminator = 0; + kitten->terminator_data = 0; + kitten->limits.ticks = UINT64_MAX; + kitten->generator = kitten->initialized++; +} + +static void clear_kitten (kitten *kitten) { + size_t bytes = (char *) &kitten->size - (char *) &kitten->status; + memset (&kitten->status, 0, bytes); + memset (&kitten->statistics, 0, sizeof (statistics)); + initialize_kitten (kitten); +} + +#define RESIZE1(P) \ + do { \ + void *OLD_PTR = (P); \ + CALLOC ((P), new_size / 2); \ + const size_t BYTES = old_vars * sizeof *(P); \ + memcpy ((P), OLD_PTR, BYTES); \ + void *NEW_PTR = (P); \ + (P) = OLD_PTR; \ + DEALLOC ((P), old_size / 2); \ + (P) = NEW_PTR; \ + } while (0) + +#define RESIZE2(P) \ + do { \ + void *OLD_PTR = (P); \ + CALLOC ((P), new_size); \ + const size_t BYTES = old_lits * sizeof *(P); \ + memcpy ((P), OLD_PTR, BYTES); \ + void *NEW_PTR = (P); \ + (P) = OLD_PTR; \ + DEALLOC ((P), old_size); \ + (P) = NEW_PTR; \ + } while (0) + +static void enlarge_internal (kitten *kitten, size_t lit) { + const size_t new_lits = (lit | 1) + 1; + const size_t old_lits = kitten->lits; + assert (old_lits <= lit); + assert (old_lits < new_lits); + assert ((lit ^ 1) < new_lits); + assert (lit < new_lits); + const size_t old_size = kitten->size; + const unsigned new_vars = new_lits / 2; + const unsigned old_vars = old_lits / 2; + if (old_size < new_lits) { + size_t new_size = old_size ? 2 * old_size : 2; + while (new_size <= lit) + new_size *= 2; + LOG ("internal literals resized to %zu from %zu (requested %zu)", + new_size, old_size, new_lits); + + RESIZE1 (kitten->marks); + RESIZE1 (kitten->phases); + RESIZE2 (kitten->values); + RESIZE2 (kitten->failed); + RESIZE1 (kitten->vars); + RESIZE1 (kitten->links); + RESIZE2 (kitten->watches); + + kitten->size = new_size; + } + kitten->lits = new_lits; + init_queue (kitten, old_vars, new_vars); + LOG ("internal literals activated until %zu literals", new_lits); + return; +} + +static const char *status_to_string (int status) { + switch (status) { + case 10: + return "formula satisfied"; + case 11: + return "formula satisfied and prime implicant computed"; + case 20: + return "formula inconsistent"; + case 21: + return "formula inconsistent and core computed"; + default: + assert (!status); + return "formula unsolved"; + } +} + +static void invalid_api_usage (const char *fun, const char *fmt, ...) { + fprintf (stderr, "kitten: fatal error: invalid API usage in '%s': ", fun); + va_list ap; + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + fputc ('\n', stderr); + fflush (stderr); + abort (); +} + +#define INVALID_API_USAGE(...) invalid_api_usage (__func__, __VA_ARGS__) + +#define REQUIRE_INITIALIZED() \ + do { \ + if (!kitten) \ + INVALID_API_USAGE ("solver argument zero"); \ + } while (0) + +#define REQUIRE_STATUS(EXPECTED) \ + do { \ + REQUIRE_INITIALIZED (); \ + if (kitten->status != (EXPECTED)) \ + INVALID_API_USAGE ("invalid status '%s' (expected '%s')", \ + status_to_string (kitten->status), \ + status_to_string (EXPECTED)); \ + } while (0) + +#define UPDATE_STATUS(STATUS) \ + do { \ + if (kitten->status != (STATUS)) \ + LOG ("updating status from '%s' to '%s'", \ + status_to_string (kitten->status), status_to_string (STATUS)); \ + else \ + LOG ("keeping status at '%s'", status_to_string (STATUS)); \ + kitten->status = (STATUS); \ + } while (0) + +kitten *kitten_init (void) { + kitten *kitten; + CALLOC (kitten, 1); + initialize_kitten (kitten); + return kitten; +} + +#ifdef LOGGING +void kitten_set_logging (kitten *kitten) { logging = true; } +#endif + +void kitten_track_antecedents (kitten *kitten) { + REQUIRE_STATUS (0); + + if (kitten->learned) + INVALID_API_USAGE ("can not start tracking antecedents after learning"); + + LOG ("enabling antecedents tracking"); + kitten->antecedents = true; +} + +void kitten_randomize_phases (kitten *kitten) { + REQUIRE_INITIALIZED (); + + LOG ("randomizing phases"); + + unsigned char *phases = kitten->phases; + const unsigned vars = kitten->size / 2; + + uint64_t random = kissat_next_random64 (&kitten->generator); + + unsigned i = 0; + const unsigned rest = vars & ~63u; + + while (i != rest) { + uint64_t *p = (uint64_t *) (phases + i); + p[0] = (random >> 0) & 0x0101010101010101; + p[1] = (random >> 1) & 0x0101010101010101; + p[2] = (random >> 2) & 0x0101010101010101; + p[3] = (random >> 3) & 0x0101010101010101; + p[4] = (random >> 4) & 0x0101010101010101; + p[5] = (random >> 5) & 0x0101010101010101; + p[6] = (random >> 6) & 0x0101010101010101; + p[7] = (random >> 7) & 0x0101010101010101; + random = kissat_next_random64 (&kitten->generator); + i += 64; + } + + unsigned shift = 0; + while (i != vars) + phases[i++] = (random >> shift++) & 1; +} + +void kitten_flip_phases (kitten *kitten) { + REQUIRE_INITIALIZED (); + + LOG ("flipping phases"); + + unsigned char *phases = kitten->phases; + const unsigned vars = kitten->size / 2; + + unsigned i = 0; + const unsigned rest = vars & ~7u; + + while (i != rest) { + uint64_t *p = (uint64_t *) (phases + i); + *p ^= 0x0101010101010101; + i += 8; + } + + while (i != vars) + phases[i++] ^= 1; +} + +void kitten_no_ticks_limit (kitten *kitten) { + REQUIRE_INITIALIZED (); + LOG ("forcing no ticks limit"); + kitten->limits.ticks = UINT64_MAX; +} + +uint64_t kitten_current_ticks (kitten *kitten) { + REQUIRE_INITIALIZED (); + const uint64_t current = KITTEN_TICKS; + return current; +} + +void kitten_set_ticks_limit (kitten *kitten, uint64_t delta) { + REQUIRE_INITIALIZED (); + const uint64_t current = KITTEN_TICKS; + uint64_t limit; + if (UINT64_MAX - delta <= current) { + LOG ("forcing unlimited ticks limit"); + limit = UINT64_MAX; + } else { + limit = current + delta; + LOG ("new limit of %" PRIu64 " ticks after %" PRIu64, limit, delta); + } + + kitten->limits.ticks = limit; +} + +void kitten_no_terminator (kitten *kitten) { + REQUIRE_INITIALIZED (); + LOG ("removing terminator"); + kitten->terminator = 0; + kitten->terminator_data = 0; +} + +void kitten_set_terminator (kitten *kitten, void *data, + int (*terminator) (void *)) { + REQUIRE_INITIALIZED (); + LOG ("setting terminator"); + kitten->terminator = terminator; + kitten->terminator_data = data; +} + +static void shuffle_unsigned_array (kitten *kitten, size_t size, + unsigned *a) { + for (size_t i = 0; i < size; i++) { + const size_t j = kissat_pick_random (&kitten->generator, 0, i); + if (j == i) + continue; + const unsigned first = a[i]; + const unsigned second = a[j]; + a[i] = second; + a[j] = first; + } +} + +static void shuffle_unsigned_stack (kitten *kitten, unsigneds *stack) { + const size_t size = SIZE_STACK (*stack); + unsigned *a = BEGIN_STACK (*stack); + shuffle_unsigned_array (kitten, size, a); +} + +static void shuffle_katches (kitten *kitten) { + LOG ("shuffling watch lists"); + for (size_t lit = 0; lit < kitten->lits; lit++) + shuffle_unsigned_stack (kitten, &KATCHES (lit)); +} + +static void shuffle_queue (kitten *kitten) { + LOG ("shuffling variable decision order"); + + const unsigned vars = kitten->lits / 2; + for (unsigned i = 0; i < vars; i++) { + const unsigned idx = kissat_pick_random (&kitten->generator, 0, vars); + dequeue (kitten, idx); + enqueue (kitten, idx); + } + update_search (kitten, kitten->queue.last); +} + +static void shuffle_units (kitten *kitten) { + LOG ("shuffling units"); + shuffle_unsigned_stack (kitten, &kitten->units); +} + +void kitten_shuffle_clauses (kitten *kitten) { + REQUIRE_STATUS (0); + shuffle_queue (kitten); + shuffle_katches (kitten); + shuffle_units (kitten); +} + +static inline unsigned *antecedents (klause *c) { + assert (is_learned_klause (c)); + return c->lits + c->size; +} + +static inline void watch_klause (kitten *kitten, unsigned lit, + unsigned ref) { + ROG (ref, "watching %u in", lit); + katches *watches = &KATCHES (lit); + PUSH_STACK (*watches, ref); +} + +static inline void connect_new_klause (kitten *kitten, unsigned ref) { + ROG (ref, "new"); + + klause *c = dereference_klause (kitten, ref); + + if (!c->size) { + if (kitten->inconsistent == INVALID) { + ROG (ref, "registering inconsistent empty"); + kitten->inconsistent = ref; + } else + ROG (ref, "ignoring inconsistent empty"); + } else if (c->size == 1) { + ROG (ref, "watching unit"); + PUSH_STACK (kitten->units, ref); + } else { + watch_klause (kitten, c->lits[0], ref); + watch_klause (kitten, c->lits[1], ref); + } +} + +static unsigned new_reference (kitten *kitten) { + size_t ref = SIZE_STACK (kitten->klauses); + if (ref >= INVALID) { + die ("maximum number of literals exhausted"); + } + const unsigned res = (unsigned) ref; + assert (res != INVALID); + INC (kitten_ticks); + return res; +} + +static void new_original_klause (kitten *kitten, unsigned id) { + unsigned res = new_reference (kitten); + unsigned size = SIZE_STACK (kitten->klause); + unsigneds *klauses = &kitten->klauses; + PUSH_STACK (*klauses, id); + PUSH_STACK (*klauses, size); + PUSH_STACK (*klauses, 0); + for (all_stack (unsigned, lit, kitten->klause)) + PUSH_STACK (*klauses, lit); + connect_new_klause (kitten, res); + kitten->end_original_ref = SIZE_STACK (*klauses); + kitten->statistics.original++; +} + +static void enlarge_external (kitten *kitten, size_t eidx) { + const size_t old_size = kitten->esize; + const unsigned old_evars = kitten->evars; + assert (old_evars <= eidx); + const unsigned new_evars = eidx + 1; + if (old_size <= eidx) { + size_t new_size = old_size ? 2 * old_size : 1; + while (new_size <= eidx) + new_size *= 2; + LOG ("external resizing to %zu variables from %zu (requested %u)", + new_size, old_size, new_evars); + unsigned *old_import = kitten->import; + CALLOC (kitten->import, new_size); + const size_t bytes = old_evars * sizeof *kitten->import; + memcpy (kitten->import, old_import, bytes); + DEALLOC (old_import, old_size); + kitten->esize = new_size; + } + kitten->evars = new_evars; + LOG ("external variables enlarged to %u", new_evars); +} + +static unsigned import_literal (kitten *kitten, unsigned elit) { + const unsigned eidx = elit / 2; + if (eidx >= kitten->evars) + enlarge_external (kitten, eidx); + + unsigned iidx = kitten->import[eidx]; + if (!iidx) { + iidx = SIZE_STACK (kitten->export); + PUSH_STACK (kitten->export, eidx); + kitten->import[eidx] = iidx + 1; + } else + iidx--; + unsigned ilit = 2 * iidx + (elit & 1); + LOG ("imported external literal %u as internal literal %u", elit, ilit); + if (ilit >= kitten->lits) + enlarge_internal (kitten, ilit); + return ilit; +} + +static unsigned export_literal (kitten *kitten, unsigned ilit) { + const unsigned iidx = ilit / 2; + assert (iidx < SIZE_STACK (kitten->export)); + const unsigned eidx = PEEK_STACK (kitten->export, iidx); + const unsigned elit = 2 * eidx + (ilit & 1); + return elit; +} + +unsigned new_learned_klause (kitten *kitten) { + unsigned res = new_reference (kitten); + unsigneds *klauses = &kitten->klauses; + const size_t size = SIZE_STACK (kitten->klause); + assert (size <= UINT_MAX); + const size_t aux = + kitten->antecedents ? SIZE_STACK (kitten->resolved) : 0; + assert (aux <= UINT_MAX); + PUSH_STACK (*klauses, (unsigned) aux); + PUSH_STACK (*klauses, (unsigned) size); + PUSH_STACK (*klauses, LEARNED_FLAG); + for (all_stack (unsigned, lit, kitten->klause)) + PUSH_STACK (*klauses, lit); + if (aux) + for (all_stack (unsigned, ref, kitten->resolved)) + PUSH_STACK (*klauses, ref); + connect_new_klause (kitten, res); + kitten->learned = true; + kitten->statistics.learned++; + return res; +} + +void kitten_clear (kitten *kitten) { + LOG ("clear kitten of size %zu", kitten->size); + + assert (EMPTY_STACK (kitten->analyzed)); + assert (EMPTY_STACK (kitten->eclause)); + assert (EMPTY_STACK (kitten->resolved)); + + CLEAR_STACK (kitten->assumptions); + CLEAR_STACK (kitten->core); + CLEAR_STACK (kitten->klause); + CLEAR_STACK (kitten->klauses); + CLEAR_STACK (kitten->trail); + CLEAR_STACK (kitten->units); + CLEAR_STACK (kitten->clause); + CLEAR_STACK (kitten->prime[0]); + CLEAR_STACK (kitten->prime[1]); + + for (all_kits (kit)) + CLEAR_STACK (KATCHES (kit)); + + while (!EMPTY_STACK (kitten->export)) + kitten->import[POP_STACK (kitten->export)] = 0; + + const size_t lits = kitten->size; + const unsigned vars = lits / 2; + +#ifndef NDEBUG + for (unsigned i = 0; i < vars; i++) + assert (!kitten->marks[i]); +#endif + + memset (kitten->phases, 0, vars); + memset (kitten->values, 0, lits); + memset (kitten->failed, 0, lits); + memset (kitten->vars, 0, vars); + + clear_kitten (kitten); +} + +void kitten_release (kitten *kitten) { + RELEASE_STACK (kitten->analyzed); + RELEASE_STACK (kitten->assumptions); + RELEASE_STACK (kitten->core); + RELEASE_STACK (kitten->eclause); + RELEASE_STACK (kitten->export); + RELEASE_STACK (kitten->klause); + RELEASE_STACK (kitten->klauses); + RELEASE_STACK (kitten->resolved); + RELEASE_STACK (kitten->trail); + RELEASE_STACK (kitten->units); + RELEASE_STACK (kitten->clause); + RELEASE_STACK (kitten->prime[0]); + RELEASE_STACK (kitten->prime[1]); + + for (size_t lit = 0; lit < kitten->size; lit++) + RELEASE_STACK (kitten->watches[lit]); + + const size_t lits = kitten->size; + const unsigned vars = lits / 2; + DEALLOC (kitten->marks, vars); + DEALLOC (kitten->phases, vars); + DEALLOC (kitten->values, lits); + DEALLOC (kitten->failed, lits); + DEALLOC (kitten->vars, vars); + DEALLOC (kitten->links, vars); + DEALLOC (kitten->watches, lits); + DEALLOC (kitten->import, kitten->esize); + (void) vars; + + free (kitten); +} + +static inline void move_to_front (kitten *kitten, unsigned idx) { + if (idx == kitten->queue.last) + return; + LOG ("move to front variable %u", idx); + dequeue (kitten, idx); + enqueue (kitten, idx); + assert (kitten->values[2 * idx]); +} + +static inline void assign (kitten *kitten, unsigned lit, unsigned reason) { +#ifdef LOGGING + if (reason == INVALID) + LOG ("assign %u as decision", lit); + else + ROG (reason, "assign %u reason", lit); +#endif + value *values = kitten->values; + const unsigned not_lit = lit ^ 1; + assert (!values[lit]); + assert (!values[not_lit]); + values[lit] = 1; + values[not_lit] = -1; + const unsigned idx = lit / 2; + const unsigned sign = lit & 1; + kitten->phases[idx] = sign; + PUSH_STACK (kitten->trail, lit); + kar *v = kitten->vars + idx; + v->level = kitten->level; + if (!v->level) { + assert (reason != INVALID); + klause *c = dereference_klause (kitten, reason); + if (c->size > 1) { + if (kitten->antecedents) { + PUSH_STACK (kitten->resolved, reason); + for (all_literals_in_klause (other, c)) + if (other != lit) { + const unsigned other_idx = other / 2; + const unsigned other_ref = kitten->vars[other_idx].reason; + assert (other_ref != INVALID); + PUSH_STACK (kitten->resolved, other_ref); + } + } + PUSH_STACK (kitten->klause, lit); + reason = new_learned_klause (kitten); + CLEAR_STACK (kitten->resolved); + CLEAR_STACK (kitten->klause); + } + } + v->reason = reason; + assert (kitten->unassigned); + kitten->unassigned--; +} + +static inline unsigned propagate_literal (kitten *kitten, unsigned lit) { + LOG ("propagating %u", lit); + value *values = kitten->values; + assert (values[lit] > 0); + const unsigned not_lit = lit ^ 1; + katches *watches = kitten->watches + not_lit; + unsigned conflict = INVALID; + unsigned *q = BEGIN_STACK (*watches); + const unsigned *const end_watches = END_STACK (*watches); + unsigned const *p = q; + uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; + while (p != end_watches) { + const unsigned ref = *q++ = *p++; + klause *c = dereference_klause (kitten, ref); + assert (c->size > 1); + unsigned *lits = c->lits; + const unsigned other = lits[0] ^ lits[1] ^ not_lit; + const value other_value = values[other]; + ticks++; + if (other_value > 0) + continue; + value replacement_value = -1; + unsigned replacement = INVALID; + const unsigned *const end_lits = lits + c->size; + unsigned *r; + for (r = lits + 2; r != end_lits; r++) { + replacement = *r; + replacement_value = values[replacement]; + if (replacement_value >= 0) + break; + } + if (replacement_value >= 0) { + assert (replacement != INVALID); + ROG (ref, "unwatching %u in", not_lit); + lits[0] = other; + lits[1] = replacement; + *r = not_lit; + watch_klause (kitten, replacement, ref); + q--; + } else if (other_value < 0) { + ROG (ref, "conflict"); + INC (kitten_conflicts); + conflict = ref; + break; + } else { + assert (!other_value); + assign (kitten, other, ref); + } + } + while (p != end_watches) + *q++ = *p++; + SET_END_OF_STACK (*watches, q); + ADD (kitten_ticks, ticks); + return conflict; +} + +static inline unsigned propagate (kitten *kitten) { + assert (kitten->inconsistent == INVALID); + unsigned propagated = 0; + unsigned conflict = INVALID; + while (conflict == INVALID && + kitten->propagated < SIZE_STACK (kitten->trail)) { + if (kitten->terminator && + kitten->terminator (kitten->terminator_data)) { + break; + } + const unsigned lit = PEEK_STACK (kitten->trail, kitten->propagated); + conflict = propagate_literal (kitten, lit); + kitten->propagated++; + propagated++; + } + ADD (kitten_propagations, propagated); + return conflict; +} + +static void bump (kitten *kitten) { + value *marks = kitten->marks; + for (all_stack (unsigned, idx, kitten->analyzed)) { + marks[idx] = 0; + move_to_front (kitten, idx); + } + check_queue (kitten); +} + +static inline void unassign (kitten *kitten, value *values, unsigned lit) { + const unsigned not_lit = lit ^ 1; + assert (values[lit]); + assert (values[not_lit]); + const unsigned idx = lit / 2; +#ifdef LOGGING + kar *var = kitten->vars + idx; + kitten->level = var->level; + LOG ("unassign %u", lit); +#endif + values[lit] = values[not_lit] = 0; + assert (kitten->unassigned < kitten->lits / 2); + kitten->unassigned++; + kink *links = kitten->links; + kink *link = links + idx; + if (link->stamp > links[kitten->queue.search].stamp) + update_search (kitten, idx); +} + +static void backtrack (kitten *kitten, unsigned jump) { + check_queue (kitten); + assert (jump < kitten->level); + LOG ("back%s to level %u", + (kitten->level == jump + 1 ? "tracking" : "jumping"), jump); + kar *vars = kitten->vars; + value *values = kitten->values; + unsigneds *trail = &kitten->trail; + while (!EMPTY_STACK (*trail)) { + const unsigned lit = TOP_STACK (*trail); + const unsigned idx = lit / 2; + const unsigned level = vars[idx].level; + if (level == jump) + break; + (void) POP_STACK (*trail); + unassign (kitten, values, lit); + } + kitten->propagated = SIZE_STACK (*trail); + kitten->level = jump; + check_queue (kitten); +} + +void completely_backtrack_to_root_level (kitten *kitten) { + check_queue (kitten); + LOG ("completely backtracking to level 0"); + value *values = kitten->values; + unsigneds *trail = &kitten->trail; + unsigneds *units = &kitten->units; +#ifndef NDEBUG + kar *vars = kitten->vars; +#endif + for (all_stack (unsigned, lit, *trail)) { + assert (vars[lit / 2].level); + unassign (kitten, values, lit); + } + CLEAR_STACK (*trail); + for (all_stack (unsigned, ref, *units)) { + klause *c = dereference_klause (kitten, ref); + assert (c->size == 1); + const unsigned unit = c->lits[0]; + const value value = values[unit]; + if (value <= 0) + continue; + unassign (kitten, values, unit); + } + kitten->propagated = 0; + kitten->level = 0; + check_queue (kitten); +} + +static void analyze (kitten *kitten, unsigned conflict) { + assert (kitten->level); + assert (kitten->inconsistent == INVALID); + assert (EMPTY_STACK (kitten->analyzed)); + assert (EMPTY_STACK (kitten->resolved)); + assert (EMPTY_STACK (kitten->klause)); + PUSH_STACK (kitten->klause, INVALID); + unsigned reason = conflict; + value *marks = kitten->marks; + const kar *const vars = kitten->vars; + const unsigned level = kitten->level; + unsigned const *p = END_STACK (kitten->trail); + unsigned open = 0, jump = 0, size = 1, uip; + for (;;) { + assert (reason != INVALID); + klause *c = dereference_klause (kitten, reason); + assert (c); + ROG (reason, "analyzing"); + PUSH_STACK (kitten->resolved, reason); + for (all_literals_in_klause (lit, c)) { + const unsigned idx = lit / 2; + if (marks[idx]) + continue; + assert (kitten->values[lit] < 0); + LOG ("analyzed %u", lit); + marks[idx] = true; + PUSH_STACK (kitten->analyzed, idx); + const kar *const v = vars + idx; + const unsigned tmp = v->level; + if (tmp < level) { + if (tmp > jump) { + jump = tmp; + if (size > 1) { + const unsigned other = PEEK_STACK (kitten->klause, 1); + POKE_STACK (kitten->klause, 1, lit); + lit = other; + } + } + PUSH_STACK (kitten->klause, lit); + size++; + } else + open++; + } + unsigned idx; + do { + assert (BEGIN_STACK (kitten->trail) < p); + uip = *--p; + } while (!marks[idx = uip / 2]); + assert (open); + if (!--open) + break; + reason = vars[idx].reason; + } + const unsigned not_uip = uip ^ 1; + LOG ("first UIP %u jump level %u size %u", not_uip, jump, size); + POKE_STACK (kitten->klause, 0, not_uip); + bump (kitten); + CLEAR_STACK (kitten->analyzed); + const unsigned learned_ref = new_learned_klause (kitten); + CLEAR_STACK (kitten->resolved); + CLEAR_STACK (kitten->klause); + backtrack (kitten, jump); + assign (kitten, not_uip, learned_ref); +} + +static void failing (kitten *kitten) { + assert (kitten->inconsistent == INVALID); + assert (!EMPTY_STACK (kitten->assumptions)); + assert (EMPTY_STACK (kitten->analyzed)); + assert (EMPTY_STACK (kitten->resolved)); + assert (EMPTY_STACK (kitten->klause)); + LOG ("analyzing failing assumptions"); + const value *const values = kitten->values; + const kar *const vars = kitten->vars; + unsigned failed_clashing = INVALID; + unsigned first_failed = INVALID; + unsigned failed_unit = INVALID; + for (all_stack (unsigned, lit, kitten->assumptions)) { + if (values[lit] >= 0) + continue; + if (first_failed == INVALID) + first_failed = lit; + const unsigned failed_idx = lit / 2; + const kar *const failed_var = vars + failed_idx; + if (!failed_var->level) { + failed_unit = lit; + break; + } + if (failed_clashing == INVALID && failed_var->reason == INVALID) + failed_clashing = lit; + } + unsigned failed; + if (failed_unit != INVALID) + failed = failed_unit; + else if (failed_clashing != INVALID) + failed = failed_clashing; + else + failed = first_failed; + assert (failed != INVALID); + const unsigned failed_idx = failed / 2; + const kar *const failed_var = vars + failed_idx; + const unsigned failed_reason = failed_var->reason; + LOG ("first failed assumption %u", failed); + kitten->failed[failed] = true; + + if (failed_unit != INVALID) { + assert (dereference_klause (kitten, failed_reason)->size == 1); + LOG ("root-level falsified assumption %u", failed); + kitten->failing = failed_reason; + ROG (kitten->failing, "failing reason"); + return; + } + + const unsigned not_failed = failed ^ 1; + if (failed_clashing != INVALID) { + LOG ("clashing with negated assumption %u", not_failed); + kitten->failed[not_failed] = true; + assert (kitten->failing == INVALID); + return; + } + + value *marks = kitten->marks; + assert (!marks[failed_idx]); + marks[failed_idx] = true; + PUSH_STACK (kitten->analyzed, failed_idx); + PUSH_STACK (kitten->klause, not_failed); + + unsigneds work; + INIT_STACK (work); + + LOGLITS (BEGIN_STACK (kitten->trail), SIZE_STACK (kitten->trail), + "trail"); + + assert (SIZE_STACK (kitten->trail)); + unsigned const *p = END_STACK (kitten->trail); + unsigned open = 1; + for (;;) { + if (!open) + break; + open--; + unsigned idx, uip; + do { + assert (BEGIN_STACK (kitten->trail) < p); + uip = *--p; + } while (!marks[idx = uip / 2]); + + const kar *var = vars + idx; + const unsigned reason = var->reason; + if (reason == INVALID) { + unsigned lit = 2 * idx; + if (values[lit] < 0) + lit ^= 1; + LOG ("failed assumption %u", lit); + assert (!kitten->failed[lit]); + kitten->failed[lit] = true; + const unsigned not_lit = lit ^ 1; + PUSH_STACK (kitten->klause, not_lit); + } else { + ROG (reason, "analyzing"); + PUSH_STACK (kitten->resolved, reason); + klause *c = dereference_klause (kitten, reason); + for (all_literals_in_klause (other, c)) { + const unsigned other_idx = other / 2; + if (marks[other_idx]) + continue; + assert (other_idx != idx); + marks[other_idx] = true; + assert (values[other]); + if (vars[other_idx].level) + open++; + else + PUSH_STACK (work, other_idx); + PUSH_STACK (kitten->analyzed, other_idx); + + LOG ("analyzing final literal %u", other ^ 1); + } + } + } + for (size_t next = 0; next < SIZE_STACK (work); next++) { + const unsigned idx = PEEK_STACK (work, next); + const kar *var = vars + idx; + const unsigned reason = var->reason; + if (reason == INVALID) { + unsigned lit = 2 * idx; + if (values[lit] < 0) + lit ^= 1; + LOG ("failed assumption %u", lit); + assert (!kitten->failed[lit]); + kitten->failed[lit] = true; + const unsigned not_lit = lit ^ 1; + PUSH_STACK (kitten->klause, not_lit); + } else { + ROG (reason, "analyzing unit"); + PUSH_STACK (kitten->resolved, reason); + } + } + + // this is bfs not dfs so it does not work for lrat :/ + /* + for (size_t next = 0; next < SIZE_STACK (kitten->analyzed); next++) { + const unsigned idx = PEEK_STACK (kitten->analyzed, next); + assert (marks[idx]); + const kar *var = vars + idx; + const unsigned reason = var->reason; + if (reason == INVALID) { + unsigned lit = 2 * idx; + if (values[lit] < 0) + lit ^= 1; + LOG ("failed assumption %u", lit); + assert (!kitten->failed[lit]); + kitten->failed[lit] = true; + const unsigned not_lit = lit ^ 1; + PUSH_STACK (kitten->klause, not_lit); + } else { + ROG (reason, "analyzing"); + PUSH_STACK (kitten->resolved, reason); + klause *c = dereference_klause (kitten, reason); + for (all_literals_in_klause (other, c)) { + const unsigned other_idx = other / 2; + if (other_idx == idx) + continue; + if (marks[other_idx]) + continue; + marks[other_idx] = true; + PUSH_STACK (kitten->analyzed, other_idx); + LOG ("analyzing final literal %u", other ^ 1); + } + } + } + */ + + for (all_stack (unsigned, idx, kitten->analyzed)) + assert (marks[idx]), marks[idx] = 0; + CLEAR_STACK (kitten->analyzed); + + RELEASE_STACK (work); + + const size_t resolved = SIZE_STACK (kitten->resolved); + assert (resolved); + + if (resolved == 1) { + kitten->failing = PEEK_STACK (kitten->resolved, 0); + ROG (kitten->failing, "reusing as core"); + } else { + kitten->failing = new_learned_klause (kitten); + ROG (kitten->failing, "new core"); + } + + CLEAR_STACK (kitten->resolved); + CLEAR_STACK (kitten->klause); +} + +static void flush_trail (kitten *kitten) { + unsigneds *trail = &kitten->trail; + LOG ("flushing %zu root-level literals from trail", SIZE_STACK (*trail)); + assert (!kitten->level); + kitten->propagated = 0; + CLEAR_STACK (*trail); +} + +static int decide (kitten *kitten) { + if (!kitten->level && !EMPTY_STACK (kitten->trail)) + flush_trail (kitten); + + const value *const values = kitten->values; + unsigned decision = INVALID; + const size_t assumptions = SIZE_STACK (kitten->assumptions); + while (kitten->level < assumptions) { + unsigned assumption = PEEK_STACK (kitten->assumptions, kitten->level); + value value = values[assumption]; + if (value < 0) { + LOG ("found failing assumption %u", assumption); + failing (kitten); + return 20; + } else if (value > 0) { + + kitten->level++; + LOG ("pseudo decision level %u for already satisfied assumption %u", + kitten->level, assumption); + } else { + decision = assumption; + LOG ("using assumption %u as decision", decision); + break; + } + } + + if (!kitten->unassigned) + return 10; + + if (KITTEN_TICKS >= kitten->limits.ticks) { + LOG ("ticks limit %" PRIu64 " hit after %" PRIu64 " ticks", + kitten->limits.ticks, KITTEN_TICKS); + return -1; + } + + if (kitten->terminator && kitten->terminator (kitten->terminator_data)) { + LOG ("terminator requested termination"); + return -1; + } + + if (decision == INVALID) { + unsigned idx = kitten->queue.search; + const kink *const links = kitten->links; + for (;;) { + assert (idx != INVALID); + if (!values[2 * idx]) + break; + idx = links[idx].prev; + } + update_search (kitten, idx); + const unsigned phase = kitten->phases[idx]; + decision = 2 * idx + phase; + LOG ("decision %u variable %u phase %u", decision, idx, phase); + } + INC (kitten_decisions); + kitten->level++; + assign (kitten, decision, INVALID); + return 0; +} + +static void inconsistent (kitten *kitten, unsigned ref) { + assert (ref != INVALID); + assert (kitten->inconsistent == INVALID); + + if (!kitten->antecedents) { + kitten->inconsistent = ref; + ROG (ref, "registering inconsistent virtually empty"); + return; + } + + unsigneds *analyzed = &kitten->analyzed; + unsigneds *resolved = &kitten->resolved; + + assert (EMPTY_STACK (*analyzed)); + assert (EMPTY_STACK (*resolved)); + + value *marks = kitten->marks; + const kar *const vars = kitten->vars; + unsigned next = 0; + + for (;;) { + assert (ref != INVALID); + klause *c = dereference_klause (kitten, ref); + assert (c); + ROG (ref, "analyzing inconsistent"); + PUSH_STACK (*resolved, ref); + for (all_literals_in_klause (lit, c)) { + const unsigned idx = lit / 2; + assert (!vars[idx].level); + if (marks[idx]) + continue; + assert (kitten->values[lit] < 0); + LOG ("analyzed %u", lit); + marks[idx] = true; + PUSH_STACK (kitten->analyzed, idx); + } + if (next == SIZE_STACK (kitten->analyzed)) + break; + const unsigned idx = PEEK_STACK (kitten->analyzed, next); + next++; + const kar *const v = vars + idx; + assert (!v->level); + ref = v->reason; + } + assert (EMPTY_STACK (kitten->klause)); + ref = new_learned_klause (kitten); + ROG (ref, "registering final inconsistent empty"); + kitten->inconsistent = ref; + + for (all_stack (unsigned, idx, *analyzed)) + marks[idx] = 0; + + CLEAR_STACK (*analyzed); + CLEAR_STACK (*resolved); +} + +static int propagate_units (kitten *kitten) { + if (kitten->inconsistent != INVALID) + return 20; + + if (EMPTY_STACK (kitten->units)) { + LOG ("no root level unit clauses"); + return 0; + } + + LOG ("propagating %zu root level unit clauses", + SIZE_STACK (kitten->units)); + + const value *const values = kitten->values; + + for (size_t next = 0; next < SIZE_STACK (kitten->units); next++) { + const unsigned ref = PEEK_STACK (kitten->units, next); + assert (ref != INVALID); + klause *c = dereference_klause (kitten, ref); + assert (c->size == 1); + ROG (ref, "propagating unit"); + const unsigned unit = c->lits[0]; + const value value = values[unit]; + if (value > 0) + continue; + if (value < 0) { + inconsistent (kitten, ref); + return 20; + } + assign (kitten, unit, ref); + } + const unsigned conflict = propagate (kitten); + if (conflict == INVALID) + return 0; + inconsistent (kitten, conflict); + return 20; +} + +/*------------------------------------------------------------------------*/ + +static klause *begin_klauses (kitten *kitten) { + return (klause *) BEGIN_STACK (kitten->klauses); +} + +static klause *end_original_klauses (kitten *kitten) { + return (klause *) (BEGIN_STACK (kitten->klauses) + + kitten->end_original_ref); +} + +static klause *end_klauses (kitten *kitten) { + return (klause *) END_STACK (kitten->klauses); +} + +static klause *next_klause (kitten *kitten, klause *c) { + assert (begin_klauses (kitten) <= c); + assert (c < end_klauses (kitten)); + unsigned *res = c->lits + c->size; + if (kitten->antecedents && is_learned_klause (c)) + res += c->aux; + return (klause *) res; +} + +/*------------------------------------------------------------------------*/ + +static void reset_core (kitten *kitten) { + LOG ("resetting core clauses"); + size_t reset = 0; + for (all_klauses (c)) + if (is_core_klause (c)) + unset_core_klause (c), reset++; + LOG ("reset %zu core clauses", reset); + CLEAR_STACK (kitten->core); +} + +static void reset_assumptions (kitten *kitten) { + LOG ("reset %zu assumptions", SIZE_STACK (kitten->assumptions)); + while (!EMPTY_STACK (kitten->assumptions)) { + const unsigned assumption = POP_STACK (kitten->assumptions); + kitten->failed[assumption] = false; + } +#ifndef NDEBUG + for (size_t i = 0; i < kitten->size; i++) + assert (!kitten->failed[i]); +#endif + CLEAR_STACK (kitten->assumptions); + if (kitten->failing != INVALID) { + ROG (kitten->failing, "reset failed assumption reason"); + kitten->failing = INVALID; + } +} + +static void reset_incremental (kitten *kitten) { + // if (kitten->level) + completely_backtrack_to_root_level (kitten); + if (!EMPTY_STACK (kitten->assumptions)) + reset_assumptions (kitten); + else + assert (kitten->failing == INVALID); + if (kitten->status == 21) + reset_core (kitten); + UPDATE_STATUS (0); +} + +/*------------------------------------------------------------------------*/ + +static bool flip_literal (kitten *kitten, unsigned lit) { + INC (kitten_flip); + signed char *values = kitten->values; + if (values[lit] < 0) + lit ^= 1; + LOG ("trying to flip value of satisfied literal %u", lit); + assert (values[lit] > 0); + katches *watches = kitten->watches + lit; + unsigned *q = BEGIN_STACK (*watches); + const unsigned *const end_watches = END_STACK (*watches); + unsigned const *p = q; + uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; + bool res = true; + while (p != end_watches) { + const unsigned ref = *q++ = *p++; + klause *c = dereference_klause (kitten, ref); + unsigned *lits = c->lits; + const unsigned other = lits[0] ^ lits[1] ^ lit; + const value other_value = values[other]; + ticks++; + if (other_value > 0) + continue; + value replacement_value = -1; + unsigned replacement = INVALID; + const unsigned *const end_lits = lits + c->size; + unsigned *r; + for (r = lits + 2; r != end_lits; r++) { + replacement = *r; + assert (replacement != lit); + replacement_value = values[replacement]; + assert (replacement_value); + if (replacement_value > 0) + break; + } + if (replacement_value > 0) { + assert (replacement != INVALID); + ROG (ref, "unwatching %u in", lit); + lits[0] = other; + lits[1] = replacement; + *r = lit; + watch_klause (kitten, replacement, ref); + q--; + } else { + assert (replacement_value < 0); + ROG (ref, "single satisfied"); + res = false; + break; + } + } + while (p != end_watches) + *q++ = *p++; + SET_END_OF_STACK (*watches, q); + ADD (kitten_ticks, ticks); + if (res) { + LOG ("flipping value of %u", lit); + values[lit] = -1; + const unsigned not_lit = lit ^ 1; + values[not_lit] = 1; + INC (kitten_flipped); + } else + LOG ("failed to flip value of %u", lit); + return res; +} + +/*------------------------------------------------------------------------*/ + +// this cadical specific clause addition avoids copying clauses multiple +// times just to convert literals to unsigned representation. +// +static unsigned int2u (int lit) { + assert (lit != 0); + int idx = abs (lit) - 1; + return (lit < 0) + 2u * (unsigned) idx; +} + +void kitten_assume (kitten *kitten, unsigned elit) { + REQUIRE_INITIALIZED (); + if (kitten->status) + reset_incremental (kitten); + const unsigned ilit = import_literal (kitten, elit); + LOG ("registering assumption %u", ilit); + PUSH_STACK (kitten->assumptions, ilit); +} + +void kitten_assume_signed (kitten *kitten, int elit) { + unsigned kelit = int2u (elit); + kitten_assume (kitten, kelit); +} + +void kitten_clause_with_id_and_exception (kitten *kitten, unsigned id, + size_t size, + const unsigned *elits, + unsigned except) { + REQUIRE_INITIALIZED (); + if (kitten->status) + reset_incremental (kitten); + assert (EMPTY_STACK (kitten->klause)); + const unsigned *const end = elits + size; + for (const unsigned *p = elits; p != end; p++) { + const unsigned elit = *p; + if (elit == except) + continue; + const unsigned ilit = import_literal (kitten, elit); + assert (ilit < kitten->lits); + const unsigned iidx = ilit / 2; + if (kitten->marks[iidx]) + INVALID_API_USAGE ("variable '%u' of literal '%u' occurs twice", + elit / 2, elit); + kitten->marks[iidx] = true; + PUSH_STACK (kitten->klause, ilit); + } + for (unsigned *p = kitten->klause.begin; p != kitten->klause.end; p++) + kitten->marks[*p / 2] = false; + new_original_klause (kitten, id); + CLEAR_STACK (kitten->klause); +} + +void citten_clause_with_id_and_exception (kitten *kitten, unsigned id, + size_t size, const int *elits, + unsigned except) { + REQUIRE_INITIALIZED (); + if (kitten->status) + reset_incremental (kitten); + assert (EMPTY_STACK (kitten->klause)); + const int *const end = elits + size; + for (const int *p = elits; p != end; p++) { + const unsigned elit = int2u (*p); // this is the conversion + if (elit == except) + continue; + const unsigned ilit = import_literal (kitten, elit); + assert (ilit < kitten->lits); + const unsigned iidx = ilit / 2; + if (kitten->marks[iidx]) + INVALID_API_USAGE ("variable '%u' of literal '%u' occurs twice", + elit / 2, elit); + kitten->marks[iidx] = true; + PUSH_STACK (kitten->klause, ilit); + } + for (unsigned *p = kitten->klause.begin; p != kitten->klause.end; p++) + kitten->marks[*p / 2] = false; + new_original_klause (kitten, id); + CLEAR_STACK (kitten->klause); +} + +void citten_clause_with_id_and_equivalence (kitten *kitten, unsigned id, + size_t size, const int *elits, + unsigned lit, unsigned other) { + REQUIRE_INITIALIZED (); + if (kitten->status) + reset_incremental (kitten); + assert (EMPTY_STACK (kitten->klause)); + bool sat = false; + const int *const end = elits + size; + for (const int *p = elits; p != end; p++) { + const unsigned elit = int2u (*p); // this is the conversion + if (elit == (lit ^ 1u) || elit == (other ^ 1u)) + continue; + if (elit == lit || elit == other) { + sat = true; + break; + } + const unsigned ilit = import_literal (kitten, elit); + assert (ilit < kitten->lits); + const unsigned iidx = ilit / 2; + if (kitten->marks[iidx]) + INVALID_API_USAGE ("variable '%u' of literal '%u' occurs twice", + elit / 2, elit); + kitten->marks[iidx] = true; + PUSH_STACK (kitten->klause, ilit); + } + for (unsigned *p = kitten->klause.begin; p != kitten->klause.end; p++) + kitten->marks[*p / 2] = false; + if (!sat) + new_original_klause (kitten, id); + CLEAR_STACK (kitten->klause); +} + +void kitten_clause (kitten *kitten, size_t size, unsigned *elits) { + kitten_clause_with_id_and_exception (kitten, INVALID, size, elits, + INVALID); +} + +void citten_clause_with_id (kitten *kitten, unsigned id, size_t size, + int *elits) { + citten_clause_with_id_and_exception (kitten, id, size, elits, INVALID); +} + +void kitten_unit (kitten *kitten, unsigned lit) { + kitten_clause (kitten, 1, &lit); +} + +void kitten_binary (kitten *kitten, unsigned a, unsigned b) { + unsigned clause[2] = {a, b}; + kitten_clause (kitten, 2, clause); +} + +int kitten_solve (kitten *kitten) { + REQUIRE_INITIALIZED (); + if (kitten->status) + reset_incremental (kitten); + else // if (kitten->level) + completely_backtrack_to_root_level (kitten); + + LOG ("starting solving under %zu assumptions", + SIZE_STACK (kitten->assumptions)); + + INC (kitten_solved); + + int res = propagate_units (kitten); + while (!res) { + const unsigned conflict = propagate (kitten); + if (kitten->terminator && + kitten->terminator (kitten->terminator_data)) { + LOG ("terminator requested termination"); + res = -1; + break; + } + if (conflict != INVALID) { + if (kitten->level) + analyze (kitten, conflict); + else { + inconsistent (kitten, conflict); + res = 20; + } + } else + res = decide (kitten); + } + + if (res < 0) + res = 0; + + if (!res && !EMPTY_STACK (kitten->assumptions)) + reset_assumptions (kitten); + + UPDATE_STATUS (res); + + if (res == 10) + INC (kitten_sat); + else if (res == 20) + INC (kitten_unsat); + else + INC (kitten_unknown); + + LOG ("finished solving with result %d", res); + + return res; +} + +int kitten_status (kitten *kitten) { return kitten->status; } + +unsigned kitten_compute_clausal_core (kitten *kitten, + uint64_t *learned_ptr) { + REQUIRE_STATUS (20); + + if (!kitten->antecedents) + INVALID_API_USAGE ("antecedents not tracked"); + + LOG ("computing clausal core"); + + unsigneds *resolved = &kitten->resolved; + assert (EMPTY_STACK (*resolved)); + + unsigned original = 0; + uint64_t learned = 0; + + unsigned reason_ref = kitten->inconsistent; + + if (reason_ref == INVALID) { + assert (!EMPTY_STACK (kitten->assumptions)); + reason_ref = kitten->failing; + if (reason_ref == INVALID) { + LOG ("assumptions mutually inconsistent"); + goto DONE; + } + } + + PUSH_STACK (*resolved, reason_ref); + unsigneds *core = &kitten->core; + assert (EMPTY_STACK (*core)); + + while (!EMPTY_STACK (*resolved)) { + const unsigned c_ref = POP_STACK (*resolved); + if (c_ref == INVALID) { + const unsigned d_ref = POP_STACK (*resolved); + ROG (d_ref, "core[%zu]", SIZE_STACK (*core)); + PUSH_STACK (*core, d_ref); + klause *d = dereference_klause (kitten, d_ref); + assert (!is_core_klause (d)); + set_core_klause (d); + if (is_learned_klause (d)) + learned++; + else + original++; + } else { + klause *c = dereference_klause (kitten, c_ref); + if (is_core_klause (c)) + continue; + PUSH_STACK (*resolved, c_ref); + PUSH_STACK (*resolved, INVALID); + ROG (c_ref, "analyzing antecedent core"); + if (!is_learned_klause (c)) + continue; + for (all_antecedents (d_ref, c)) { + klause *d = dereference_klause (kitten, d_ref); + if (!is_core_klause (d)) + PUSH_STACK (*resolved, d_ref); + } + } + } + +DONE: + + if (learned_ptr) + *learned_ptr = learned; + + LOG ("clausal core of %u original clauses", original); + LOG ("clausal core of %" PRIu64 " learned clauses", learned); + kitten->statistics.original = original; + kitten->statistics.learned = 0; + UPDATE_STATUS (21); + + return original; +} + +void kitten_traverse_core_ids (kitten *kitten, void *state, + void (*traverse) (void *, unsigned)) { + REQUIRE_STATUS (21); + + LOG ("traversing core of original clauses"); + + unsigned traversed = 0; + + for (all_original_klauses (c)) { + // only happens for 'true' incremental calls, i.e. if add happens after + // solve + if (is_learned_klause (c)) + continue; + if (!is_core_klause (c)) + continue; + ROG (reference_klause (kitten, c), "traversing"); + traverse (state, c->aux); + traversed++; + } + + LOG ("traversed %u original core clauses", traversed); + (void) traversed; + + assert (kitten->status == 21); +} + +void kitten_traverse_core_clauses (kitten *kitten, void *state, + void (*traverse) (void *, bool, size_t, + const unsigned *)) { + REQUIRE_STATUS (21); + + LOG ("traversing clausal core"); + + unsigned traversed = 0; + + for (all_stack (unsigned, c_ref, kitten->core)) { + klause *c = dereference_klause (kitten, c_ref); + assert (is_core_klause (c)); + const bool learned = is_learned_klause (c); + unsigneds *eclause = &kitten->eclause; + assert (EMPTY_STACK (*eclause)); + for (all_literals_in_klause (ilit, c)) { + const unsigned elit = export_literal (kitten, ilit); + PUSH_STACK (*eclause, elit); + } + const size_t size = SIZE_STACK (*eclause); + const unsigned *elits = eclause->begin; + ROG (reference_klause (kitten, c), "traversing"); + traverse (state, learned, size, elits); + CLEAR_STACK (*eclause); + traversed++; + } + + LOG ("traversed %u core clauses", traversed); + (void) traversed; + + assert (kitten->status == 21); +} + +void kitten_traverse_core_clauses_with_id ( + kitten *kitten, void *state, + void (*traverse) (void *state, unsigned, bool learned, size_t, + const unsigned *)) { + REQUIRE_STATUS (21); + + LOG ("traversing clausal core"); + + unsigned traversed = 0; + + for (all_stack (unsigned, c_ref, kitten->core)) { + klause *c = dereference_klause (kitten, c_ref); + assert (is_core_klause (c)); + const bool learned = is_learned_klause (c); + unsigneds *eclause = &kitten->eclause; + assert (EMPTY_STACK (*eclause)); + for (all_literals_in_klause (ilit, c)) { + const unsigned elit = export_literal (kitten, ilit); + PUSH_STACK (*eclause, elit); + } + const size_t size = SIZE_STACK (*eclause); + const unsigned *elits = eclause->begin; + ROG (reference_klause (kitten, c), "traversing"); + unsigned ctag = learned ? 0 : c->aux; + traverse (state, ctag, learned, size, elits); + CLEAR_STACK (*eclause); + traversed++; + } + + LOG ("traversed %u core clauses", traversed); + (void) traversed; + + assert (kitten->status == 21); +} + +void kitten_trace_core (kitten *kitten, void *state, + void (*trace) (void *, unsigned, unsigned, bool, + size_t, const unsigned *, size_t, + const unsigned *)) { + REQUIRE_STATUS (21); + + LOG ("tracing clausal core"); + + unsigned traced = 0; + + for (all_stack (unsigned, c_ref, kitten->core)) { + klause *c = dereference_klause (kitten, c_ref); + assert (is_core_klause (c)); + const bool learned = is_learned_klause (c); + unsigneds *eclause = &kitten->eclause; + assert (EMPTY_STACK (*eclause)); + for (all_literals_in_klause (ilit, c)) { + const unsigned elit = export_literal (kitten, ilit); + PUSH_STACK (*eclause, elit); + } + const size_t size = SIZE_STACK (*eclause); + const unsigned *elits = eclause->begin; + + unsigneds *resolved = &kitten->resolved; + assert (EMPTY_STACK (*resolved)); + if (learned) { + for (all_antecedents (ref, c)) { + PUSH_STACK (*resolved, ref); + } + } + const size_t rsize = SIZE_STACK (*resolved); + const unsigned *rids = resolved->begin; + + unsigned cid = reference_klause (kitten, c); + unsigned ctag = learned ? 0 : c->aux; + ROG (cid, "tracing"); + trace (state, cid, ctag, learned, size, elits, rsize, rids); + CLEAR_STACK (*eclause); + CLEAR_STACK (*resolved); + traced++; + } + + LOG ("traced %u core clauses", traced); + (void) traced; + + assert (kitten->status == 21); +} + +void kitten_shrink_to_clausal_core (kitten *kitten) { + REQUIRE_STATUS (21); + + LOG ("shrinking formula to core of original clauses"); + + CLEAR_STACK (kitten->trail); + + kitten->unassigned = kitten->lits / 2; + kitten->propagated = 0; + kitten->level = 0; + + update_search (kitten, kitten->queue.last); + + memset (kitten->values, 0, kitten->lits); + + for (all_kits (lit)) + CLEAR_STACK (KATCHES (lit)); + + assert (kitten->inconsistent != INVALID); + klause *inconsistent = dereference_klause (kitten, kitten->inconsistent); + if (is_learned_klause (inconsistent) || inconsistent->size) { + ROG (kitten->inconsistent, "resetting inconsistent"); + kitten->inconsistent = INVALID; + } else + ROG (kitten->inconsistent, "keeping inconsistent"); + + CLEAR_STACK (kitten->units); + + klause *begin = begin_klauses (kitten), *q = begin; + klause const *const end = end_original_klauses (kitten); +#ifdef LOGGING + unsigned original = 0; +#endif + for (klause *c = begin, *next; c != end; c = next) { + next = next_klause (kitten, c); + // assert (!is_learned_klause (c)); not necessarily true + if (is_learned_klause (c)) + continue; + if (!is_core_klause (c)) + continue; + unset_core_klause (c); + const unsigned dst = (unsigned *) q - (unsigned *) begin; + const unsigned size = c->size; + if (!size) { + if (kitten->inconsistent != INVALID) + kitten->inconsistent = dst; + } else if (size == 1) { + PUSH_STACK (kitten->units, dst); + ROG (dst, "keeping"); + } else { + watch_klause (kitten, c->lits[0], dst); + watch_klause (kitten, c->lits[1], dst); + } + if (c == q) + q = next; + else { + const size_t bytes = (char *) next - (char *) c; + memmove (q, c, bytes); + q = (klause *) ((char *) q + bytes); + } +#ifdef LOGGING + original++; +#endif + } + SET_END_OF_STACK (kitten->klauses, (unsigned *) q); + kitten->end_original_ref = SIZE_STACK (kitten->klauses); + LOG ("end of original clauses at %zu", kitten->end_original_ref); + LOG ("%u original clauses left", original); + + CLEAR_STACK (kitten->core); + + UPDATE_STATUS (0); +} + +signed char kitten_signed_value (kitten *kitten, int selit) { + REQUIRE_STATUS (10); + const unsigned elit = int2u (selit); + const unsigned eidx = elit / 2; + if (eidx >= kitten->evars) + return 0; + unsigned iidx = kitten->import[eidx]; + if (!iidx) + return 0; + const unsigned ilit = 2 * (iidx - 1) + (elit & 1); + return kitten->values[ilit]; +} + +signed char kitten_value (kitten *kitten, unsigned elit) { + REQUIRE_STATUS (10); + const unsigned eidx = elit / 2; + if (eidx >= kitten->evars) + return 0; + unsigned iidx = kitten->import[eidx]; + if (!iidx) + return 0; + const unsigned ilit = 2 * (iidx - 1) + (elit & 1); + return kitten->values[ilit]; +} + +signed char kitten_fixed (kitten *kitten, unsigned elit) { + const unsigned eidx = elit / 2; + if (eidx >= kitten->evars) + return 0; + unsigned iidx = kitten->import[eidx]; + if (!iidx) + return 0; + iidx--; + const unsigned ilit = 2 * iidx + (elit & 1); + signed char res = kitten->values[ilit]; + if (!res) + return 0; + kar *v = kitten->vars + iidx; + if (v->level) + return 0; + return res; +} + +signed char kitten_fixed_signed (kitten *kitten, int elit) { + unsigned kelit = int2u (elit); + return kitten_fixed (kitten, kelit); +} + +bool kitten_flip_literal (kitten *kitten, unsigned elit) { + REQUIRE_STATUS (10); + const unsigned eidx = elit / 2; + if (eidx >= kitten->evars) + return false; + unsigned iidx = kitten->import[eidx]; + if (!iidx) + return false; + const unsigned ilit = 2 * (iidx - 1) + (elit & 1); + if (kitten_fixed (kitten, elit)) + return false; + return flip_literal (kitten, ilit); +} + +bool kitten_flip_signed_literal (kitten *kitten, int elit) { + REQUIRE_STATUS (10); + unsigned kelit = int2u (elit); + return kitten_flip_literal (kitten, kelit); +} + +bool kitten_failed (kitten *kitten, unsigned elit) { + REQUIRE_STATUS (20); + const unsigned eidx = elit / 2; + if (eidx >= kitten->evars) + return false; + unsigned iidx = kitten->import[eidx]; + if (!iidx) + return false; + const unsigned ilit = 2 * (iidx - 1) + (elit & 1); + return kitten->failed[ilit]; +} + +// checks both watches for clauses with only one literal positively +// assigned. if such a clause is found, return false. Otherwise fix watch +// invariant and return true +static bool prime_propagate (kitten *kitten, const unsigned idx, + void *state, const bool ignoring, + bool (*ignore) (void *, unsigned)) { + unsigned lit = 2 * idx; + unsigned conflict = INVALID; + value *values = kitten->values; + for (int i = 0; i < 2; i++) { + if (conflict != INVALID) + break; + lit = lit ^ i; + const unsigned not_lit = lit ^ 1; + katches *watches = kitten->watches + not_lit; + unsigned *q = BEGIN_STACK (*watches); + const unsigned *const end_watches = END_STACK (*watches); + unsigned const *p = q; + uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; + while (p != end_watches) { + const unsigned ref = *q++ = *p++; + klause *c = dereference_klause (kitten, ref); + if (is_learned_klause (c) || ignore (state, c->aux) == ignoring) + continue; + assert (c->size > 1); + unsigned *lits = c->lits; + const unsigned other = lits[0] ^ lits[1] ^ not_lit; + const value other_value = values[other]; + ticks++; + if (other_value > 0) + continue; + value replacement_value = -1; + unsigned replacement = INVALID; + const unsigned *const end_lits = lits + c->size; + unsigned *r; + for (r = lits + 2; r != end_lits; r++) { + replacement = *r; + replacement_value = values[replacement]; + if (replacement_value > 0) + break; + } + if (replacement_value > 0) { + assert (replacement != INVALID); + ROG (ref, "unwatching %u in", not_lit); + lits[0] = other; + lits[1] = replacement; + *r = not_lit; + watch_klause (kitten, replacement, ref); + q--; + } else { + ROG (ref, "idx %u forced prime by", idx); + conflict = ref; + break; + } + } + while (p != end_watches) + *q++ = *p++; + SET_END_OF_STACK (*watches, q); + ADD (kitten_ticks, ticks); + } + return conflict == INVALID; +} + +void kitten_add_prime_implicant (kitten *kitten, void *state, int side, + void (*add_implicant) (void *, int, size_t, + const unsigned *)) { + REQUIRE_STATUS (11); + // might be possible in some edge cases + unsigneds *prime = &kitten->prime[side]; + unsigneds *prime2 = &kitten->prime[!side]; + assert (!EMPTY_STACK (*prime) || !EMPTY_STACK (*prime2)); + CLEAR_STACK (*prime2); + + for (all_stack (unsigned, lit, *prime)) { + const unsigned not_lit = lit ^ 1; + const unsigned elit = export_literal (kitten, not_lit); + PUSH_STACK (*prime2, elit); + } + + // adds a clause which will reset kitten status and backtrack + add_implicant (state, side, SIZE_STACK (*prime2), BEGIN_STACK (*prime2)); + CLEAR_STACK (*prime); + CLEAR_STACK (*prime2); +} + +// computes two prime implicants, only considering clauses based on ignore +// return -1 if no prime implicant has been computed, otherwise returns +// index of shorter implicant. +// TODO does not work if flip has been called beforehand +int kitten_compute_prime_implicant (kitten *kitten, void *state, + bool (*ignore) (void *, unsigned)) { + REQUIRE_STATUS (10); + + value *values = kitten->values; + kar *vars = kitten->vars; + unsigneds unassigned; + INIT_STACK (unassigned); + bool limit_hit = 0; + assert (EMPTY_STACK (kitten->prime[0]) && EMPTY_STACK (kitten->prime[1])); + for (int i = 0; i < 2; i++) { + const bool ignoring = i; + for (all_stack (unsigned, lit, kitten->trail)) { + if (KITTEN_TICKS >= kitten->limits.ticks) { + LOG ("ticks limit %" PRIu64 " hit after %" PRIu64 " ticks", + kitten->limits.ticks, KITTEN_TICKS); + limit_hit = 1; + break; + } + assert (values[lit] > 0); + const unsigned idx = lit / 2; + const unsigned ref = vars[idx].reason; + assert (vars[idx].level); + klause *c = 0; + if (ref != INVALID) + c = dereference_klause (kitten, ref); + if (ref == INVALID || is_learned_klause (c) || + ignore (state, c->aux) == ignoring) { + LOG ("non-prime candidate var %d", idx); + if (prime_propagate (kitten, idx, state, ignoring, ignore)) { + values[lit] = 0; + values[lit ^ 1] = 0; + PUSH_STACK (unassigned, lit); + } else + assert (values[lit] > 0); + } + } + unsigneds *prime = &kitten->prime[i]; + // push on prime implicant stack. + for (all_kits (lit)) { + if (values[lit] > 0) + PUSH_STACK (*prime, lit); + } + // reassign all literals on + for (all_stack (unsigned, lit, unassigned)) { + assert (!values[lit]); + values[lit] = 1; + values[lit ^ 1] = -1; + } + CLEAR_STACK (unassigned); + } + RELEASE_STACK (unassigned); + + if (limit_hit) { + CLEAR_STACK (kitten->prime[0]); + CLEAR_STACK (kitten->prime[1]); + return -1; + } + // the only case when one of the prime implicants is allowed to be empty + // is if ignore returns always true or always false. + assert (!EMPTY_STACK (kitten->prime[0]) || + !EMPTY_STACK (kitten->prime[1])); + UPDATE_STATUS (11); + + int res = SIZE_STACK (kitten->prime[0]) > SIZE_STACK (kitten->prime[1]); + return res; +} + +static bool contains_blit (kitten *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 (kitten *kitten, const unsigned idx, + const unsigned blit) { + unsigned lit = 2 * idx; + unsigned conflict = INVALID; + value *values = kitten->values; + LOG ("prime propagating idx %u for blit %u", idx, blit); + for (int i = 0; i < 2; i++) { + if (conflict != INVALID) + break; + lit = lit ^ i; + if (lit == blit) + continue; + const unsigned not_lit = lit ^ 1; + katches *watches = kitten->watches + not_lit; + unsigned *q = BEGIN_STACK (*watches); + const unsigned *const end_watches = END_STACK (*watches); + unsigned const *p = q; + uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; + while (p != end_watches) { + const unsigned ref = *q++ = *p++; + klause *c = dereference_klause (kitten, ref); + if (is_learned_klause (c)) + continue; + ROG (ref, "checking with blit %u", blit); + assert (c->size > 1); + unsigned *lits = c->lits; + const unsigned other = lits[0] ^ lits[1] ^ not_lit; + const value other_value = values[other]; + ticks++; + bool use = other == blit || not_lit == blit; + if (other_value > 0) + continue; + value replacement_value = -1; + unsigned replacement = INVALID; + const unsigned *const end_lits = lits + c->size; + unsigned *r; + for (r = lits + 2; r != end_lits; r++) { + replacement = *r; + replacement_value = values[replacement]; + use = use || replacement == blit; + if (replacement_value > 0) + break; + } + if (replacement_value > 0) { + assert (replacement != INVALID); + ROG (ref, "unwatching %u in", not_lit); + lits[0] = other; + lits[1] = replacement; + *r = not_lit; + watch_klause (kitten, replacement, ref); + q--; + } else if (!use) { + continue; + } else { + ROG (ref, "idx %u forced prime by", idx); + conflict = ref; + break; + } + } + while (p != end_watches) + *q++ = *p++; + SET_END_OF_STACK (*watches, q); + ADD (kitten_ticks, ticks); + } + return conflict == INVALID; +} + +static int compute_prime_implicant_for (kitten *kitten, unsigned blit) { + value *values = kitten->values; + kar *vars = kitten->vars; + unsigneds unassigned; + INIT_STACK (unassigned); + bool limit_hit = false; + assert (EMPTY_STACK (kitten->prime[0]) && EMPTY_STACK (kitten->prime[1])); + for (int i = 0; i < 2; i++) { + const unsigned block = blit ^ i; + const bool ignoring = i; + if (prime_propagate_blit (kitten, block / 2, block)) { + value tmp = values[blit]; + assert (tmp); + values[blit] = 0; + values[blit ^ 1] = 0; + PUSH_STACK (unassigned, tmp > 0 ? blit : blit ^ 1); + PUSH_STACK (kitten->prime[i], block); // will be negated! + } else + assert (false); + for (all_stack (unsigned, lit, kitten->trail)) { + if (KITTEN_TICKS >= kitten->limits.ticks) { + LOG ("ticks limit %" PRIu64 " hit after %" PRIu64 " ticks", + kitten->limits.ticks, KITTEN_TICKS); + limit_hit = true; + break; + } + if (!values[lit]) + continue; + assert (values[lit]); // not true when flipping is involved + const unsigned idx = lit / 2; + const unsigned ref = vars[idx].reason; + assert (vars[idx].level); + LOG ("non-prime candidate var %d", idx); + if (prime_propagate_blit (kitten, idx, block)) { + value tmp = values[lit]; + assert (tmp); + values[lit] = 0; + values[lit ^ 1] = 0; + PUSH_STACK (unassigned, tmp > 0 ? lit : lit ^ 1); + } + } + unsigneds *prime = &kitten->prime[i]; + // push on prime implicant stack. + for (all_kits (lit)) { + if (values[lit] > 0) + PUSH_STACK (*prime, lit); + } + // reassign all literals on + for (all_stack (unsigned, lit, unassigned)) { + assert (!values[lit]); + values[lit] = 1; + values[lit ^ 1] = -1; + } + CLEAR_STACK (unassigned); + } + RELEASE_STACK (unassigned); + + if (limit_hit) { + CLEAR_STACK (kitten->prime[0]); + CLEAR_STACK (kitten->prime[1]); + return -1; + } + // the only case when one of the prime implicants is allowed to be empty + // is if ignore returns always true or always false. + assert (!EMPTY_STACK (kitten->prime[0]) || + !EMPTY_STACK (kitten->prime[1])); + LOGLITS (BEGIN_STACK (kitten->prime[0]), SIZE_STACK (kitten->prime[0]), + "first implicant %u", blit); + LOGLITS (BEGIN_STACK (kitten->prime[1]), SIZE_STACK (kitten->prime[1]), + "second implicant %u", blit ^ 1); + UPDATE_STATUS (11); + + int res = SIZE_STACK (kitten->prime[0]) > SIZE_STACK (kitten->prime[1]); + return res; +} + +int kitten_flip_and_implicant_for_signed_literal (kitten *kitten, + int elit) { + REQUIRE_STATUS (10); + unsigned kelit = int2u (elit); + if (!kitten_flip_literal (kitten, kelit)) { + return -2; + } + const unsigned eidx = kelit / 2; + unsigned iidx = kitten->import[eidx]; + assert (iidx); + const unsigned ilit = 2 * (iidx - 1) + (kelit & 1); + return compute_prime_implicant_for (kitten, ilit); +} diff --git a/src/sat/cadical/kitten.h b/src/sat/cadical/kitten.h new file mode 100644 index 000000000..3132cda4d --- /dev/null +++ b/src/sat/cadical/kitten.h @@ -0,0 +1,97 @@ +#ifndef _kitten_h_INCLUDED +#define _kitten_h_INCLUDED + +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct kitten kitten; + +kitten *kitten_init (void); +void kitten_clear (kitten *); +void kitten_release (kitten *); + +#ifdef LOGGING +void kitten_set_logging (kitten *kitten); +#endif + +void kitten_track_antecedents (kitten *); + +void kitten_shuffle_clauses (kitten *); +void kitten_flip_phases (kitten *); +void kitten_randomize_phases (kitten *); + +void kitten_assume (kitten *, unsigned lit); +void kitten_assume_signed (kitten *, int lit); + +void kitten_clause (kitten *, size_t size, unsigned *); +void citten_clause_with_id (kitten *, unsigned id, size_t size, int *); +void kitten_unit (kitten *, unsigned); +void kitten_binary (kitten *, unsigned, unsigned); + +void kitten_clause_with_id_and_exception (kitten *, unsigned id, + size_t size, const unsigned *, + unsigned except); + +void citten_clause_with_id_and_exception (kitten *, unsigned id, + size_t size, const int *, + unsigned except); +void citten_clause_with_id_and_equivalence (kitten *, unsigned id, + size_t size, const int *, + unsigned, unsigned); +void kitten_no_ticks_limit (kitten *); +void kitten_set_ticks_limit (kitten *, uint64_t); +uint64_t kitten_current_ticks (kitten *); + +void kitten_no_terminator (kitten *); +void kitten_set_terminator (kitten *, void *, int (*) (void *)); + +int kitten_solve (kitten *); +int kitten_status (kitten *); + +signed char kitten_value (kitten *, unsigned); +signed char kitten_signed_value (kitten *, int); // converts second argument +signed char kitten_fixed (kitten *, unsigned); +signed char kitten_fixed_signed (kitten *, int); // converts +bool kitten_failed (kitten *, unsigned); +bool kitten_flip_literal (kitten *, unsigned); +bool kitten_flip_signed_literal (kitten *, int); + +unsigned kitten_compute_clausal_core (kitten *, uint64_t *learned); +void kitten_shrink_to_clausal_core (kitten *); + +void kitten_traverse_core_ids (kitten *, void *state, + void (*traverse) (void *state, unsigned id)); + +void kitten_traverse_core_clauses (kitten *, void *state, + void (*traverse) (void *state, + bool learned, size_t, + const unsigned *)); +void kitten_traverse_core_clauses_with_id ( + kitten *, void *state, + void (*traverse) (void *state, unsigned, bool learned, size_t, + const unsigned *)); +void kitten_trace_core (kitten *, void *state, + void (*trace) (void *, unsigned, unsigned, bool, + size_t, const unsigned *, size_t, + const unsigned *)); + +int kitten_compute_prime_implicant (kitten *kitten, void *state, + bool (*ignore) (void *, unsigned)); + +void kitten_add_prime_implicant (kitten *kitten, void *state, int side, + void (*add_implicant) (void *, int, size_t, + const unsigned *)); + +int kitten_flip_and_implicant_for_signed_literal (kitten *kitten, int elit); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/sat/cadical/level.hpp b/src/sat/cadical/level.hpp new file mode 100644 index 000000000..cf20a77b1 --- /dev/null +++ b/src/sat/cadical/level.hpp @@ -0,0 +1,33 @@ +#ifndef _level_hpp_INCLUDED +#define _level_hpp_INCLUDED + +#include <climits> + +namespace CaDiCaL { + +// For each new decision we increase the decision level and push a 'Level' +// on the 'control' stack. The information gathered here is used in +// 'reuse_trail' and for early aborts in clause minimization. + +struct Level { + + int decision; // decision literal of this level + int trail; // trail start of this level + + struct { + int count; // how many variables seen during 'analyze' + int trail; // smallest trail position seen on this level + } seen; + + void reset () { + seen.count = 0; + seen.trail = INT_MAX; + } + + Level (int d, int t) : decision (d), trail (t) { reset (); } + Level () {} +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/lidruptracer.cpp b/src/sat/cadical/lidruptracer.cpp new file mode 100644 index 000000000..453f87df4 --- /dev/null +++ b/src/sat/cadical/lidruptracer.cpp @@ -0,0 +1,656 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +LidrupTracer::LidrupTracer (Internal *i, File *f, bool b) + : internal (i), file (f), binary (b), num_clauses (0), size_clauses (0), + clauses (0), last_hash (0), last_id (0), last_clause (0) +#ifndef QUIET + , + added (0), deleted (0) +#endif +{ + (void) internal; + + // Initialize random number table for hash function. + // + Random random (42); + for (unsigned n = 0; n < num_nonces; n++) { + uint64_t nonce = random.next (); + if (!(nonce & 1)) + nonce++; + assert (nonce), assert (nonce & 1); + nonces[n] = nonce; + } +#ifndef NDEBUG + binary = b; +#else + (void) b; +#endif + piping = file->piping (); +} + +void LidrupTracer::connect_internal (Internal *i) { + internal = i; + file->connect_internal (internal); + LOG ("LIDRUP TRACER connected to internal"); +} + +LidrupTracer::~LidrupTracer () { + LOG ("LIDRUP TRACER delete"); + delete file; + for (size_t i = 0; i < size_clauses; i++) + for (LidrupClause *c = clauses[i], *next; c; c = next) + next = c->next, delete_clause (c); + delete[] clauses; +} + +/*------------------------------------------------------------------------*/ + +void LidrupTracer::enlarge_clauses () { + assert (num_clauses == size_clauses); + const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1; + LOG ("LIDRUP Tracer enlarging clauses of tracer from %" PRIu64 + " to %" PRIu64, + (uint64_t) size_clauses, (uint64_t) new_size_clauses); + LidrupClause **new_clauses; + new_clauses = new LidrupClause *[new_size_clauses]; + clear_n (new_clauses, new_size_clauses); + for (uint64_t i = 0; i < size_clauses; i++) { + for (LidrupClause *c = clauses[i], *next; c; c = next) { + next = c->next; + const uint64_t h = reduce_hash (c->hash, new_size_clauses); + c->next = new_clauses[h]; + new_clauses[h] = c; + } + } + delete[] clauses; + clauses = new_clauses; + size_clauses = new_size_clauses; +} + +LidrupClause *LidrupTracer::new_clause () { + LidrupClause *res = new LidrupClause; + res->next = 0; + res->hash = last_hash; + res->id = last_id; + for (const auto &id : imported_chain) { + res->chain.push_back (id); + } + for (const auto &lit : imported_clause) { + res->literals.push_back (lit); + } + last_clause = res; + num_clauses++; + return res; +} + +void LidrupTracer::delete_clause (LidrupClause *c) { + assert (c); + num_clauses--; + delete c; +} + +uint64_t LidrupTracer::reduce_hash (uint64_t hash, uint64_t size) { + assert (size > 0); + unsigned shift = 32; + uint64_t res = hash; + while ((((uint64_t) 1) << shift) > size) { + res ^= res >> shift; + shift >>= 1; + } + res &= size - 1; + assert (res < size); + return res; +} + +uint64_t LidrupTracer::compute_hash (const int64_t id) { + assert (id > 0); + unsigned j = id % num_nonces; + uint64_t tmp = nonces[j] * (uint64_t) id; + return last_hash = tmp; +} + +bool LidrupTracer::find_and_delete (const int64_t id) { + if (!num_clauses) + return false; + LidrupClause **res = 0, *c; + const uint64_t hash = compute_hash (id); + const uint64_t h = reduce_hash (hash, size_clauses); + for (res = clauses + h; (c = *res); res = &c->next) { + if (c->hash == hash && c->id == id) { + break; + } + if (!c->next) + return false; + } + if (!c) + return false; + assert (c && res); + *res = c->next; + for (auto &lit : c->literals) { + imported_clause.push_back (lit); + } + for (auto &cid : c->chain) { + imported_chain.push_back (cid); + } + delete_clause (c); + return true; +} + +void LidrupTracer::insert () { + if (num_clauses == size_clauses) + enlarge_clauses (); + const uint64_t h = reduce_hash (compute_hash (last_id), size_clauses); + LidrupClause *c = new_clause (); + c->next = clauses[h]; + clauses[h] = c; +} + +/*------------------------------------------------------------------------*/ + +inline void LidrupTracer::flush_if_piping () { + if (piping) + file->flush (); +} + +inline void LidrupTracer::put_binary_zero () { + assert (binary); + assert (file); + file->put ((unsigned char) 0); +} + +inline void LidrupTracer::put_binary_lit (int lit) { + assert (binary); + assert (file); + assert (lit != INT_MIN); + unsigned x = 2 * abs (lit) + (lit < 0); + unsigned char ch; + while (x & ~0x7f) { + ch = (x & 0x7f) | 0x80; + file->put (ch); + x >>= 7; + } + ch = x; + file->put (ch); +} + +inline void LidrupTracer::put_binary_id (int64_t id, bool can_be_negative) { + assert (binary); + assert (file); + uint64_t x = abs (id); + if (can_be_negative) { + x = 2 * x + (id < 0); + } + unsigned char ch; + while (x & ~0x7f) { + ch = (x & 0x7f) | 0x80; + file->put (ch); + x >>= 7; + } + ch = x; + file->put (ch); +} + +/*------------------------------------------------------------------------*/ + +void LidrupTracer::lidrup_add_restored_clause (int64_t id) { + if (!batch_weaken.empty () || !batch_delete.empty ()) + lidrup_batch_weaken_restore_and_delete (); + batch_restore.push_back (id); +} + +void LidrupTracer::lidrup_add_derived_clause ( + int64_t id, const vector<int> &clause, const vector<int64_t> &chain) { + lidrup_batch_weaken_restore_and_delete (); + if (binary) { + file->put ('l'); + put_binary_id (id); + } else { + file->put ("l "); + file->put (id); + file->put (' '); + } + for (const auto &external_lit : clause) + if (binary) + put_binary_lit (external_lit); + else + file->put (external_lit), file->put (' '); + if (binary) + put_binary_zero (); + else + file->put ("0 "); + for (const auto &cid : chain) + if (binary) + put_binary_id (cid); + else + file->put (cid), file->put (' '); + if (binary) + put_binary_zero (); + else + file->put ("0\n"); +} + +void LidrupTracer::lidrup_add_original_clause (int64_t id, + const vector<int> &clause) { + lidrup_batch_weaken_restore_and_delete (); + if (binary) { + file->put ('i'); + put_binary_id (id); + } else { + file->put ("i "); + file->put (id); + file->put (' '); + } + for (const auto &external_lit : clause) + if (binary) + put_binary_lit (external_lit); + else + file->put (external_lit), file->put (' '); + if (binary) + put_binary_zero (); + else + file->put ("0\n"); +} + +void LidrupTracer::lidrup_batch_weaken_restore_and_delete () { + assert (batch_weaken.empty () || batch_delete.empty ()); + if (!batch_weaken.empty ()) { + if (binary) { + file->put ('w'); + } else { + file->put ("w "); + } + for (const auto &id : batch_weaken) { + if (binary) + put_binary_id (id); + else + file->put (id), file->put (' '); + } + batch_weaken.clear (); + if (binary) + put_binary_zero (); + else + file->put ("0\n"); +#ifndef QUIET + batched++; +#endif + } + if (!batch_delete.empty ()) { + if (binary) { + file->put ('d'); + } else { + file->put ("d "); + } + for (const auto &id : batch_delete) { + if (binary) + put_binary_id (id); + else + file->put (id), file->put (' '); + } + batch_delete.clear (); + if (binary) + put_binary_zero (); + else + file->put ("0\n"); +#ifndef QUIET + batched++; +#endif + } + if (!batch_restore.empty ()) { + if (binary) { + file->put ('r'); + } else { + file->put ("r "); + } + for (const auto &id : batch_restore) { + if (binary) + put_binary_id (id); + else + file->put (id), file->put (' '); + } + batch_restore.clear (); + if (binary) + put_binary_zero (); + else + file->put ("0\n"); +#ifndef QUIET + batched++; +#endif + } +} + +void LidrupTracer::lidrup_conclude_and_delete ( + const vector<int64_t> &conclusion) { + lidrup_batch_weaken_restore_and_delete (); + int64_t size = conclusion.size (); + if (size > 1) { + if (binary) { + file->put ('U'); + put_binary_id (size); + } else { + file->put ("U "); + file->put (size), file->put ("\n"); + } + } + for (auto &id : conclusion) { + if (binary) + file->put ('u'); + else + file->put ("u "); + if (!find_and_delete (id)) { + assert (imported_clause.empty ()); + assert (conclusion.size () == 1); + if (binary) { + put_binary_zero (); + put_binary_id (id); + put_binary_zero (); + } else { + file->put ("0 "); + file->put (id); + file->put (" 0\n"); + } + } else { + for (const auto &external_lit : imported_clause) { + // flip sign... + const auto not_elit = -external_lit; + if (binary) + put_binary_lit (not_elit); + else + file->put (not_elit), file->put (' '); + } + if (binary) + put_binary_zero (); + else + file->put ("0 "); + for (const auto &cid : imported_chain) { + if (binary) + put_binary_id (cid); + else + file->put (cid), file->put (' '); + } + if (binary) + put_binary_zero (); + else + file->put ("0\n"); + imported_clause.clear (); + imported_chain.clear (); + } + } + flush_if_piping (); +} + +void LidrupTracer::lidrup_report_status (int status) { + lidrup_batch_weaken_restore_and_delete (); + if (binary) + file->put ('s'); + else + file->put ("s "); + if (status == SATISFIABLE) + file->put ("SATISFIABLE"); + else if (status == UNSATISFIABLE) + file->put ("UNSATISFIABLE"); + else + file->put ("UNKNOWN"); + if (!binary) + file->put ("\n"); + flush_if_piping (); +} + +void LidrupTracer::lidrup_conclude_sat (const vector<int> &model) { + lidrup_batch_weaken_restore_and_delete (); + if (binary) + file->put ('m'); + else + file->put ("m "); + for (auto &lit : model) { + if (binary) + put_binary_lit (lit); + else + file->put (lit), file->put (' '); + } + if (binary) + put_binary_zero (); + else + file->put ("0\n"); + flush_if_piping (); +} + +void LidrupTracer::lidrup_conclude_unknown (const vector<int> &trail) { + lidrup_batch_weaken_restore_and_delete (); + if (binary) + file->put ('e'); + else + file->put ("e "); + for (auto &lit : trail) { + if (binary) + put_binary_lit (lit); + else + file->put (lit), file->put (' '); + } + if (binary) + put_binary_zero (); + else + file->put ("0\n"); + flush_if_piping (); +} + +void LidrupTracer::lidrup_solve_query () { + lidrup_batch_weaken_restore_and_delete (); + if (binary) + file->put ('q'); + else + file->put ("q "); + for (auto &lit : assumptions) { + if (binary) + put_binary_lit (lit); + else + file->put (lit), file->put (' '); + } + if (binary) + put_binary_zero (); + else + file->put ("0\n"); + flush_if_piping (); +} + +/*------------------------------------------------------------------------*/ + +void LidrupTracer::add_derived_clause (int64_t id, bool, + const vector<int> &clause, + const vector<int64_t> &chain) { + if (file->closed ()) + return; + assert (imported_clause.empty ()); + LOG (clause, "LIDRUP TRACER tracing addition of derived clause"); + lidrup_add_derived_clause (id, clause, chain); +#ifndef QUIET + added++; +#endif +} + +void LidrupTracer::add_assumption_clause (int64_t id, + const vector<int> &clause, + const vector<int64_t> &chain) { + if (file->closed ()) + return; + assert (imported_clause.empty ()); + LOG (clause, + "LIDRUP TRACER tracing addition of assumption clause[%" PRId64 "]", + id); + for (auto &lit : clause) + imported_clause.push_back (lit); + for (auto &cid : chain) + imported_chain.push_back (cid); + last_id = id; + insert (); + imported_clause.clear (); + imported_chain.clear (); +} + +void LidrupTracer::delete_clause (int64_t id, bool, const vector<int> &) { + if (file->closed ()) + return; + assert (imported_clause.empty ()); + LOG ("LIDRUP TRACER tracing deletion of clause[%" PRId64 "]", id); + if (find_and_delete (id)) { + assert (imported_clause.empty ()); + if (!batch_delete.empty () || !batch_restore.empty ()) + lidrup_batch_weaken_restore_and_delete (); + batch_weaken.push_back (id); +#ifndef QUIET + weakened++; +#endif + } else { + if (!batch_weaken.empty () || !batch_restore.empty ()) + lidrup_batch_weaken_restore_and_delete (); + batch_delete.push_back (id); +#ifndef QUIET + deleted++; +#endif + } +} + +void LidrupTracer::weaken_minus (int64_t id, const vector<int> &) { + if (file->closed ()) + return; + assert (imported_clause.empty ()); + LOG ("LIDRUP TRACER tracing weaken minus of clause[%" PRId64 "]", id); + last_id = id; + insert (); +} + +void LidrupTracer::conclude_unsat (ConclusionType, + const vector<int64_t> &conclusion) { + if (file->closed ()) + return; + assert (imported_clause.empty ()); + LOG (conclusion, "LIDRUP TRACER tracing conclusion of clause(s)"); + lidrup_conclude_and_delete (conclusion); +} + +void LidrupTracer::add_original_clause (int64_t id, bool, + const vector<int> &clause, + bool restored) { + if (file->closed ()) + return; + if (!restored) { + LOG (clause, "LIDRUP TRACER tracing addition of original clause"); +#ifndef QUIET + original++; +#endif + return lidrup_add_original_clause (id, clause); + } + assert (restored); + if (find_and_delete (id)) { + LOG (clause, + "LIDRUP TRACER the clause was not yet weakened, so no restore"); + return; + } + LOG (clause, "LIDRUP TRACER tracing addition of restored clause"); + lidrup_add_restored_clause (id); +#ifndef QUIET + restore++; +#endif +} + +void LidrupTracer::report_status (int status, int64_t) { + if (file->closed ()) + return; + LOG ("LIDRUP TRACER tracing report of status %d", status); + lidrup_report_status (status); +} + +void LidrupTracer::conclude_sat (const vector<int> &model) { + if (file->closed ()) + return; + LOG (model, "LIDRUP TRACER tracing conclusion of model"); + lidrup_conclude_sat (model); +} + +void LidrupTracer::conclude_unknown (const vector<int> &entrailed) { + if (file->closed ()) + return; + LOG (entrailed, "LIDRUP TRACER tracing conclusion of UNK"); + lidrup_conclude_unknown (entrailed); +} + +void LidrupTracer::solve_query () { + if (file->closed ()) + return; + LOG (assumptions, "LIDRUP TRACER tracing solve query with assumptions"); + lidrup_solve_query (); +#ifndef QUIET + solved++; +#endif +} + +void LidrupTracer::add_assumption (int lit) { + LOG ("LIDRUP TRACER tracing addition of assumption %d", lit); + assumptions.push_back (lit); +} + +void LidrupTracer::reset_assumptions () { + LOG (assumptions, "LIDRUP TRACER tracing reset of assumptions"); + assumptions.clear (); +} + +/*------------------------------------------------------------------------*/ + +bool LidrupTracer::closed () { return file->closed (); } + +#ifndef QUIET + +void LidrupTracer::print_statistics () { + // TODO complete this. + uint64_t bytes = file->bytes (); + uint64_t total = added + deleted + weakened + restore + original; + MSG ("LIDRUP %" PRId64 " original clauses %.2f%%", original, + percent (original, total)); + MSG ("LIDRUP %" PRId64 " learned clauses %.2f%%", added, + percent (added, total)); + MSG ("LIDRUP %" PRId64 " deleted clauses %.2f%%", deleted, + percent (deleted, total)); + MSG ("LIDRUP %" PRId64 " weakened clauses %.2f%%", weakened, + percent (weakened, total)); + MSG ("LIDRUP %" PRId64 " restored clauses %.2f%%", restore, + percent (restore, total)); + MSG ("LIDRUP %" PRId64 " batches of deletions, weaken and restores %.2f", + batched, relative (batched, deleted + restore + weakened)); + MSG ("LIDRUP %" PRId64 " queries %.2f", solved, relative (solved, total)); + MSG ("LIDRUP %" PRId64 " bytes (%.2f MB)", bytes, + bytes / (double) (1 << 20)); +} + +#endif + +void LidrupTracer::close (bool print) { + assert (!closed ()); + file->close (); +#ifndef QUIET + if (print) { + MSG ("LIDRUP proof file '%s' closed", file->name ()); + print_statistics (); + } +#else + (void) print; +#endif +} + +void LidrupTracer::flush (bool print) { + assert (!closed ()); + lidrup_batch_weaken_restore_and_delete (); + file->flush (); +#ifndef QUIET + if (print) { + MSG ("LIDRUP proof file '%s' flushed", file->name ()); + print_statistics (); + } +#else + (void) print; +#endif +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/lidruptracer.hpp b/src/sat/cadical/lidruptracer.hpp new file mode 100644 index 000000000..196e74670 --- /dev/null +++ b/src/sat/cadical/lidruptracer.hpp @@ -0,0 +1,117 @@ +#ifndef _lidruptracer_h_INCLUDED +#define _lidruptracer_h_INCLUDED + +class FileTracer; + +namespace CaDiCaL { + +struct LidrupClause { + LidrupClause *next; // collision chain link for hash table + uint64_t hash; // previously computed full 64-bit hash + int64_t id; // id of clause + std::vector<int64_t> chain; + std::vector<int> literals; +}; + +class LidrupTracer : public FileTracer { + + Internal *internal; + File *file; + bool binary; + bool piping; // The 'file' is a pipe and needs eagerly flushing. + + // hash table for conclusion + // + uint64_t num_clauses; // number of clauses in hash table + uint64_t size_clauses; // size of clause hash table + LidrupClause **clauses; // hash table of clauses + vector<int> imported_clause; + vector<int> assumptions; + vector<int64_t> imported_chain; + vector<int64_t> batch_weaken; + vector<int64_t> batch_delete; + vector<int64_t> batch_restore; + + static const unsigned num_nonces = 4; + + uint64_t nonces[num_nonces]; // random numbers for hashing + uint64_t last_hash; // last computed hash value of clause + int64_t last_id; // id of the last added clause + LidrupClause *last_clause; + uint64_t compute_hash (int64_t); // compute and save hash value of clause + + LidrupClause *new_clause (); + void delete_clause (LidrupClause *); + + static uint64_t reduce_hash (uint64_t hash, uint64_t size); + + void enlarge_clauses (); // enlarge hash table for clauses + void insert (); // insert clause in hash table + bool + find_and_delete (const int64_t); // find clause position in hash table + +#ifndef QUIET + int64_t added, deleted, weakened, restore, original, solved, batched; +#endif + + void flush_if_piping (); + + void put_binary_zero (); + void put_binary_lit (int external_lit); + void put_binary_id (int64_t id, bool = true); + + void lidrup_add_derived_clause (int64_t id, const vector<int> &clause, + const vector<int64_t> &chain); + void lidrup_delete_clause (int64_t id); //, const vector<int> &clause); + void + lidrup_add_restored_clause (int64_t id); //, const vector<int> &clause); + void lidrup_add_original_clause (int64_t id, const vector<int> &clause); + void lidrup_conclude_and_delete (const vector<int64_t> &conclusion); + void lidrup_report_status (int status); + void lidrup_conclude_sat (const vector<int> &model); + void lidrup_conclude_unknown (const vector<int> &trail); + void lidrup_solve_query (); + void lidrup_batch_weaken_restore_and_delete (); + +public: + LidrupTracer (Internal *, File *file, bool); + ~LidrupTracer (); + + // proof section: + void add_derived_clause (int64_t, bool, const vector<int> &, + const vector<int64_t> &) override; + void add_assumption_clause (int64_t, const vector<int> &, + const vector<int64_t> &) override; + void weaken_minus (int64_t, const vector<int> &) override; + void delete_clause (int64_t, bool, const vector<int> &) override; + void add_original_clause (int64_t, bool, const vector<int> &, + bool = false) override; + void report_status (int, int64_t) override; + void conclude_sat (const vector<int> &) override; + void conclude_unsat (ConclusionType, const vector<int64_t> &) override; + void conclude_unknown (const vector<int> &) override; + + void solve_query () override; + void add_assumption (int) override; + void reset_assumptions () override; + + // skip + void begin_proof (int64_t) override {} + void finalize_clause (int64_t, const vector<int> &) override {} + void strengthen (int64_t) override {} + void add_constraint (const vector<int> &) override {} + + // logging and file io + void connect_internal (Internal *i) override; + +#ifndef QUIET + void print_statistics (); +#endif + bool closed () override; + void close (bool) override; + void flush (bool) override; +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/limit.cpp b/src/sat/cadical/limit.cpp new file mode 100644 index 000000000..8a5259b2a --- /dev/null +++ b/src/sat/cadical/limit.cpp @@ -0,0 +1,129 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +Limit::Limit () { memset (this, 0, sizeof *this); } + +/*------------------------------------------------------------------------*/ + +double Internal::scale (double v) const { + const double ratio = clause_variable_ratio (); + const double factor = (ratio <= 2) ? 1.0 : log (ratio) / log (2); + double res = factor * v; + if (res < 1) + res = 1; + return res; +} + +/*------------------------------------------------------------------------*/ + +Last::Last () { memset (this, 0, sizeof *this); } + +/*------------------------------------------------------------------------*/ + +Inc::Inc () { + memset (this, 0, sizeof *this); + decisions = conflicts = -1; // unlimited +} + +void Internal::limit_terminate (int l) { + if (l <= 0 && !lim.terminate.forced) { + LOG ("keeping unbounded terminate limit"); + } else if (l <= 0) { + LOG ("reset terminate limit to be unbounded"); + lim.terminate.forced = 0; + } else { + lim.terminate.forced = l; + LOG ("new terminate limit of %d calls", l); + } +} + +void Internal::limit_conflicts (int l) { + if (l < 0 && inc.conflicts < 0) { + LOG ("keeping unbounded conflict limit"); + } else if (l < 0) { + LOG ("reset conflict limit to be unbounded"); + inc.conflicts = -1; + } else { + inc.conflicts = l; + LOG ("new conflict limit of %d conflicts", l); + } +} + +void Internal::limit_decisions (int l) { + if (l < 0 && inc.decisions < 0) { + LOG ("keeping unbounded decision limit"); + } else if (l < 0) { + LOG ("reset decision limit to be unbounded"); + inc.decisions = -1; + } else { + inc.decisions = l; + LOG ("new decision limit of %d decisions", l); + } +} + +void Internal::limit_preprocessing (int l) { + if (l < 0) { + LOG ("ignoring invalid preprocessing limit %d", l); + } else if (!l) { + LOG ("reset preprocessing limit to no preprocessing"); + inc.preprocessing = 0; + } else { + inc.preprocessing = l; + LOG ("new preprocessing limit of %d preprocessing rounds", l); + } +} + +void Internal::limit_local_search (int l) { + if (l < 0) { + LOG ("ignoring invalid local search limit %d", l); + } else if (!l) { + LOG ("reset local search limit to no local search"); + inc.localsearch = 0; + } else { + inc.localsearch = l; + LOG ("new local search limit of %d local search rounds", l); + } +} + +bool Internal::is_valid_limit (const char *name) { + if (!strcmp (name, "terminate")) + return true; + if (!strcmp (name, "conflicts")) + return true; + if (!strcmp (name, "decisions")) + return true; + if (!strcmp (name, "preprocessing")) + return true; + if (!strcmp (name, "localsearch")) + return true; + return false; +} + +bool Internal::limit (const char *name, int l) { + bool res = true; + if (!strcmp (name, "terminate")) + limit_terminate (l); + else if (!strcmp (name, "conflicts")) + limit_conflicts (l); + else if (!strcmp (name, "decisions")) + limit_decisions (l); + else if (!strcmp (name, "preprocessing")) + limit_preprocessing (l); + else if (!strcmp (name, "localsearch")) + limit_local_search (l); + else + res = false; + return res; +} + +void Internal::reset_limits () { + LOG ("reset limits"); + limit_terminate (0); + limit_conflicts (-1); + limit_decisions (-1); + limit_preprocessing (0); + limit_local_search (0); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/limit.hpp b/src/sat/cadical/limit.hpp new file mode 100644 index 000000000..89004d36d --- /dev/null +++ b/src/sat/cadical/limit.hpp @@ -0,0 +1,156 @@ +#ifndef _limit_hpp_INCLUDED +#define _limit_hpp_INCLUDED + +#include <cstdint> +#include <limits> + +namespace CaDiCaL { + +struct Internal; + +struct Limit { + + bool initialized; + + int64_t conflicts; // conflict limit if non-negative + int64_t decisions; // decision limit if non-negative + int64_t preprocessing; // limit on preprocessing rounds + int64_t localsearch; // limit on local search rounds + + int64_t compact; // conflict limit for next 'compact' + int64_t condition; // conflict limit for next 'condition' + int64_t elim; // conflict limit for next 'elim' + int64_t flush; // conflict limit for next 'flush' + int64_t inprobe; // conflict limit for next 'inprobe' + int64_t reduce; // conflict limit for next 'reduce' + int64_t rephase; // conflict limit for next 'rephase' + int64_t report; // report limit for header + int64_t restart; // conflict limit for next 'restart' + int64_t stabilize; // conflict/ticks limit for next 'stabilize' + + int keptsize; // maximum kept size in 'reduce' + int keptglue; // maximum kept glue in 'reduce' + int64_t recompute_tier; // conflict limit for next tier recomputation + + // How often rephased during (1) or out (0) of stabilization. + // + int64_t rephased[2]; + + // Current elimination bound per eliminated variable. + // + int64_t elimbound; + + struct { + int check; // countdown to next terminator call + int forced; // forced termination for testing + } terminate; + + Limit (); +}; + +struct Delay { + struct { + int64_t interval = 0, limit = 0; + bool bypass = 0; + + bool delay () { + if (bypass) + return true; + if (limit) { + --limit; + return true; + } else { + return false; + } + } + + void bump_delay () { + interval += interval < INT64_MAX; + limit = interval; + } + + void reduce_delay () { + if (!interval) + return; + interval /= 2; + limit = interval; + } + + void bypass_delay () { bypass = 1; } + void unbypass_delay () { bypass = 0; } + } bumpreasons; +}; + +struct Last { + struct { + int64_t propagations; + } transred; + struct { + int64_t ticks; + } sweep, vivify, probe; + struct { + int64_t fixed, subsumephases, marked; + } elim; + struct { + int64_t reductions; + } inprobe; + struct { + int64_t conflicts; + } reduce, rephase; + struct { + int64_t ticks; + int64_t marked; + } ternary; + struct { + int64_t fixed; + } collect; + struct { + int64_t marked, ticks; + } factor; + struct { + int64_t conflicts; + int64_t ticks; + } stabilize; + Last (); +}; + +struct Inc { + int64_t flush; // flushing interval in terms of conflicts + int64_t stabilize; // base ticks limit after first mode switch + int64_t conflicts; // next conflict limit if non-negative + int64_t decisions; // next decision limit if non-negative + int64_t preprocessing; // next preprocessing limit if non-negative + int64_t localsearch; // next local search limit if non-negative + Inc (); +}; + +#define SET_EFFORT_LIMIT(LIMIT, NAME, THRESHHOLD) \ + int64_t LIMIT; \ + do { \ + const int64_t OLD_LIMIT = stats.ticks.NAME; \ + const int64_t TICKS = stats.ticks.search[0] + stats.ticks.search[1]; \ + const int64_t LAST = last.NAME.ticks; \ + int64_t REFERENCE = TICKS - LAST; \ + if (!REFERENCE || !stats.conflicts) { \ + VERBOSE (2, "last %" PRId64 " current %" PRId64 " delta %" PRId64, \ + LAST, TICKS, REFERENCE); \ + REFERENCE = opts.preprocessinit; \ + } \ + const double EFFORT = (double) opts.NAME##effort * 1e-3; \ + const int64_t DELTA = EFFORT * REFERENCE; \ + const int64_t THRESH = opts.NAME##thresh * clauses.size (); \ + if (THRESHHOLD && DELTA < THRESH) { \ + VERBOSE (2, \ + "delaying %s with ticklimit %" PRId64 \ + " and threshhold %" PRId64, \ + #NAME, DELTA, THRESH); \ + return false; \ + } \ + last.NAME.ticks = TICKS; \ + const int64_t NEW_LIMIT = OLD_LIMIT + DELTA; \ + LIMIT = NEW_LIMIT; \ + } while (0) + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/logging.cpp b/src/sat/cadical/logging.cpp new file mode 100644 index 000000000..a677a99e1 --- /dev/null +++ b/src/sat/cadical/logging.cpp @@ -0,0 +1,215 @@ +#ifdef LOGGING + +#include "internal.hpp" + +namespace CaDiCaL { + +void Logger::print_log_prefix (Internal *internal) { + internal->print_prefix (); + tout.magenta (); + fputs ("LOG ", stdout); + tout.magenta (true); + printf ("%d ", internal->level); + tout.normal (); +} + +void Logger::log_empty_line (Internal *internal) { + internal->print_prefix (); + tout.magenta (); + const int len = internal->prefix.size (), max = 78 - len; + for (int i = 0; i < max; i++) + fputc ('-', stdout); + fputc ('\n', stdout); + tout.normal (); + fflush (stdout); +} + +void Logger::log (Internal *internal, const char *fmt, ...) { + print_log_prefix (internal); + tout.magenta (); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + fputc ('\n', stdout); + tout.normal (); + fflush (stdout); +} + +// It is hard to factor out the common part between the two clause loggers, +// since they are also used in slightly different contexts. Our attempt to +// do so were not more readable than the current version. See the header +// for an explanation of the difference between the following two functions. + +void Logger::log (Internal *internal, const Clause *c, const char *fmt, + ...) { + print_log_prefix (internal); + tout.magenta (); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + if (c) { + if (c->redundant) + printf (" glue %d redundant", c->glue); + else + printf (" irredundant"); + printf (" size %d clause[%" PRId64 "]", c->size, c->id); + if (c->moved) + printf (" ... (moved)"); + else { + if (internal->opts.logsort) { + vector<int> s; + for (const auto &lit : *c) + s.push_back (lit); + sort (s.begin (), s.end (), clause_lit_less_than ()); + for (const auto &lit : s) + printf (" %d", lit); + } else { + for (const auto &lit : *c) { + printf (" %s", loglit (internal, lit).c_str ()); + } + } + } + } else if (internal->level) + printf (" decision"); + else + printf (" unit"); + fputc ('\n', stdout); + tout.normal (); + fflush (stdout); +} + +void Logger::log (Internal *internal, const Gate *g, const char *fmt, ...) { + print_log_prefix (internal); + tout.magenta (); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + if (g) { + printf ("%s%s%s gate[%" PRIu64 "] (arity: %ld) %s := %s", + g->degenerated_and_pos ? " deg+" : "", + g->degenerated_and_neg ? " deg-" : "", + g->garbage ? " garbage" : "", g->id, g->arity (), + loglit (internal, g->lhs).c_str (), + string_of_gate (g->tag).c_str ()); + for (const auto &lit : g->rhs) { + printf (" %s", loglit (internal, lit).c_str ()); + } + } else + printf (" null gate"); + fputc ('\n', stdout); + tout.normal (); + fflush (stdout); +} + +// Same as above, but for the global clause 'c' (which is not a reason). + +void Logger::log (Internal *internal, const vector<int> &c, const char *fmt, + ...) { + print_log_prefix (internal); + tout.magenta (); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + if (internal->opts.logsort) { + vector<int> s; + for (const auto &lit : c) + s.push_back (lit); + sort (s.begin (), s.end (), clause_lit_less_than ()); + for (const auto &lit : s) + printf (" %d", lit); + } else { + for (const auto &lit : c) + printf (" %d", lit); + } + fputc ('\n', stdout); + tout.normal (); + fflush (stdout); +} + +// Now for 'restore_clause' to avoid copying (without logging). + +void Logger::log (Internal *internal, + const vector<int>::const_iterator &begin, + const vector<int>::const_iterator &end, const char *fmt, + ...) { + print_log_prefix (internal); + tout.magenta (); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + if (internal->opts.logsort) { + vector<int> s; + for (auto p = begin; p != end; p++) + s.push_back (*p); + sort (s.begin (), s.end (), clause_lit_less_than ()); + for (const auto &lit : s) + printf (" %d", lit); + } else { + for (auto p = begin; p != end; p++) + printf (" %d", *p); + } + fputc ('\n', stdout); + tout.normal (); + fflush (stdout); +} + +// for LRAT proof chains + +void Logger::log (Internal *internal, const vector<int64_t> &c, + const char *fmt, ...) { + print_log_prefix (internal); + tout.magenta (); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + for (const auto &id : c) + printf (" %" PRId64, id); + fputc ('\n', stdout); + tout.normal (); + fflush (stdout); +} + +// for LRAT proof clauses + +void Logger::log (Internal *internal, const int *literals, + const unsigned size, const char *fmt, ...) { + print_log_prefix (internal); + tout.magenta (); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + for (unsigned i = 0; i < size; i++) { + const int lit = literals[i]; + printf (" %d", lit); + } + fputc ('\n', stdout); + tout.normal (); + fflush (stdout); +} + +string Logger::loglit (Internal *internal, int lit) { + std::string v = std::to_string (lit); + if (lit && -internal->max_var <= lit && internal->max_var >= lit) { + const int va = internal->val (lit); + if (va) { + v = v + "@" + std::to_string (internal->var (lit).level); + if (!internal->var (lit).reason) + v = v + "+"; + } + if (va > 0) + v += "=1"; + else if (va < 0) + v += "=-1"; + } + return v; +} +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/logging.hpp b/src/sat/cadical/logging.hpp new file mode 100644 index 000000000..46b0e07bf --- /dev/null +++ b/src/sat/cadical/logging.hpp @@ -0,0 +1,98 @@ +#ifndef _logging_hpp_INCLUDED +#define _logging_hpp_INCLUDED + +/*------------------------------------------------------------------------*/ +#ifdef LOGGING +/*------------------------------------------------------------------------*/ + +#include <cstdint> +#include <vector> + +namespace CaDiCaL { + +// For debugging purposes and to help understanding what the solver is doing +// there is a logging facility which is compiled in by './configure -l'. It +// still has to be enabled at run-time though (again using the '-l' option +// in the stand-alone solver). It produces quite a bit of information. + +using namespace std; + +struct Clause; +struct Gate; +struct Internal; + +struct Logger { + + static void print_log_prefix (Internal *); + + // Simple logging of a C-style format string. + // + static void log (Internal *, const char *fmt, ...) + CADICAL_ATTRIBUTE_FORMAT (2, 3); + + // Prints the format string (with its argument) and then the clause. The + // clause can also be a zero pointer and then is interpreted as a decision + // (current decision level > 0) or unit clause (zero decision level) and + // printed accordingly. + // + static void log (Internal *, const Clause *, const char *fmt, ...) + CADICAL_ATTRIBUTE_FORMAT (3, 4); + + // Same as before, except that this is meant for the global 'clause' stack + // used for new clauses (and not for reasons). + // + static void log (Internal *, const vector<int> &, const char *fmt, ...) + CADICAL_ATTRIBUTE_FORMAT (3, 4); + + // Another variant, to avoid copying (without logging). + // + static void log (Internal *, const vector<int>::const_iterator &begin, + const vector<int>::const_iterator &end, const char *fmt, + ...) CADICAL_ATTRIBUTE_FORMAT (4, 5); + + // used for logging LRAT proof chains + // + static void log (Internal *, const vector<int64_t> &, const char *fmt, + ...) CADICAL_ATTRIBUTE_FORMAT (3, 4); + + static void log (Internal *, const int *, const unsigned, const char *fmt, + ...) CADICAL_ATTRIBUTE_FORMAT (4, 5); + + static void log_empty_line (Internal *); + + static void log (Internal *, const Gate *, const char *fmt, ...) + CADICAL_ATTRIBUTE_FORMAT (3, 4); + + static string loglit (Internal *, int lit); +}; + +} // namespace CaDiCaL + +/*------------------------------------------------------------------------*/ + +// Make sure that 'logging' code is really not included (second case of the +// '#ifdef') if logging code is not included. + +#define LOG(...) \ + do { \ + if (!internal->opts.log) \ + break; \ + Logger::log (internal, __VA_ARGS__); \ + } while (0) + +#define LOGLIT(lit) Logger::loglit (internal, lit).c_str () + +/*------------------------------------------------------------------------*/ +#else // end of 'then' part of 'ifdef LOGGING' +/*------------------------------------------------------------------------*/ + +#define LOG(...) \ + do { \ + } while (0) + +#define LOGLIT(...) + +/*------------------------------------------------------------------------*/ +#endif // end of 'else' part of 'ifdef LOGGING' +/*------------------------------------------------------------------------*/ +#endif diff --git a/src/sat/cadical/lookahead.cpp b/src/sat/cadical/lookahead.cpp new file mode 100644 index 000000000..3d60a9a01 --- /dev/null +++ b/src/sat/cadical/lookahead.cpp @@ -0,0 +1,520 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +struct literal_occ { + int lit; + int count; + bool operator< (const literal_occ &locc) const { + return (count > locc.count) || (count == locc.count && lit < locc.lit); + } + literal_occ operator++ () { + ++count; + return *this; + } +}; + +std::vector<int> Internal::lookahead_populate_locc () { + std::vector<literal_occ> loccs ((std::size_t) max_var + 1); + for (std::size_t lit = 0; lit < loccs.size (); ++lit) { + loccs[lit].lit = lit; + } + for (const auto &c : clauses) + if (!c->redundant) + for (const auto &lit : *c) + if (active (lit)) + ++loccs[std::abs (lit)]; + std::sort (begin (loccs), end (loccs)); + std::vector<int> locc_map; + locc_map.reserve (max_var); + for (const auto &locc : loccs) + locc_map.push_back (locc.lit); + return locc_map; +} + +int Internal::lookahead_locc (const std::vector<int> &loccs) { + for (auto lit : loccs) + if (active (abs (lit)) && !assumed (lit) && !assumed (-lit) && + !val (lit)) + return lit; + return 0; +} + +// This calculates the literal that appears the most often reusing the +// available datastructures and iterating over the clause set. This is too +// slow to be called iteratively. A faster (but inexact) version is +// lookahead_populate_loc and lookahead_loc. +int Internal::most_occurring_literal () { + init_noccs (); + for (const auto &c : clauses) + if (!c->redundant) + for (const auto &lit : *c) + if (active (lit)) + noccs (lit)++; + int64_t max_noccs = 0; + int res = 0; + + if (unsat) + return INT_MIN; + + propagate (); + for (int idx = 1; idx <= max_var; idx++) { + if (!active (idx) || assumed (idx) || assumed (-idx) || val (idx)) + continue; + for (int sign = -1; sign <= 1; sign += 2) { + const int lit = sign * idx; + if (!active (lit)) + continue; + int64_t tmp = noccs (lit); + if (tmp <= max_noccs) + continue; + max_noccs = tmp; + res = lit; + } + } + MSG ("maximum occurrence %" PRId64 " of literal %d", max_noccs, res); + reset_noccs (); + return res; +} + +// We probe on literals first, which occur more often negated and thus we +// sort the 'probes' stack in such a way that literals which occur negated +// less frequently come first. Probes are taken from the back of the stack. + +struct probe_negated_noccs_rank { + Internal *internal; + probe_negated_noccs_rank (Internal *i) : internal (i) {} + typedef size_t Type; + Type operator() (int a) const { return internal->noccs (-a); } +}; + +// Follow the ideas in 'generate_probes' but flush non root probes and +// reorder remaining probes. + +void Internal::lookahead_flush_probes () { + + assert (!probes.empty ()); + + init_noccs (); + for (const auto &c : clauses) { + int a, b; + if (!is_binary_clause (c, a, b)) + continue; + noccs (a)++; + noccs (b)++; + } + + const auto eop = probes.end (); + auto j = probes.begin (); + for (auto i = j; i != eop; i++) { + int lit = *i; + if (!active (lit)) + continue; + const bool have_pos_bin_occs = noccs (lit) > 0; + const bool have_neg_bin_occs = noccs (-lit) > 0; + if (have_pos_bin_occs == have_neg_bin_occs) + continue; + if (have_pos_bin_occs) + lit = -lit; + assert (!noccs (lit)), assert (noccs (-lit) > 0); + if (propfixed (lit) >= stats.all.fixed) + continue; + MSG ("keeping probe %d negated occs %" PRId64 "", lit, noccs (-lit)); + *j++ = lit; + } + size_t remain = j - probes.begin (); +#ifndef QUIET + size_t flushed = probes.size () - remain; +#endif + probes.resize (remain); + + rsort (probes.begin (), probes.end (), probe_negated_noccs_rank (this)); + + reset_noccs (); + shrink_vector (probes); + + PHASE ("probe-round", stats.probingrounds, + "flushed %zd literals %.0f%% remaining %zd", flushed, + percent (flushed, remain + flushed), remain); +} + +void Internal::lookahead_generate_probes () { + + assert (probes.empty ()); + + // First determine all the literals which occur in binary clauses. It is + // way faster to go over the clauses once, instead of walking the watch + // lists for each literal. + // + init_noccs (); + for (const auto &c : clauses) { + int a, b; + if (!is_binary_clause (c, a, b)) + continue; + noccs (a)++; + noccs (b)++; + } + + for (int idx = 1; idx <= max_var; idx++) { + + // Then focus on roots of the binary implication graph, which are + // literals occurring negatively in a binary clause, but not positively. + // If neither 'idx' nor '-idx' is a root it makes less sense to probe + // this variable. + + // This argument requires that equivalent literal substitution through + // 'decompose' is performed, because otherwise there might be 'cyclic + // roots' which are not tried, i.e., -1 2 0, 1 -2 0, 1 2 3 0, 1 2 -3 0. + + const bool have_pos_bin_occs = noccs (idx) > 0; + const bool have_neg_bin_occs = noccs (-idx) > 0; + + // if (have_pos_bin_occs == have_neg_bin_occs) continue; + + if (have_pos_bin_occs) { + int probe = -idx; + + // See the discussion where 'propfixed' is used below. + // + if (propfixed (probe) >= stats.all.fixed) + continue; + + MSG ("scheduling probe %d negated occs %" PRId64 "", probe, + noccs (-probe)); + probes.push_back (probe); + } + + if (have_neg_bin_occs) { + int probe = idx; + + // See the discussion where 'propfixed' is used below. + // + if (propfixed (probe) >= stats.all.fixed) + continue; + + MSG ("scheduling probe %d negated occs %" PRId64 "", probe, + noccs (-probe)); + probes.push_back (probe); + } + } + + rsort (probes.begin (), probes.end (), probe_negated_noccs_rank (this)); + + reset_noccs (); + shrink_vector (probes); + + PHASE ("probe-round", stats.probingrounds, + "scheduled %zd literals %.0f%%", probes.size (), + percent (probes.size (), 2 * max_var)); +} + +int Internal::lookahead_next_probe () { + + int generated = 0; + + for (;;) { + + if (probes.empty ()) { + if (generated++) + return 0; + lookahead_generate_probes (); + } + + while (!probes.empty ()) { + + int probe = probes.back (); + probes.pop_back (); + + // Eliminated or assigned. + // + if (!active (probe) || assumed (probe) || assumed (-probe)) + continue; + + // There is now new unit since the last time we propagated this probe, + // thus we propagated it before without obtaining a conflict and + // nothing changed since then. Thus there is no need to propagate it + // again. This observation was independently made by Partik Simons + // et.al. in the context of implementing 'smodels' (see for instance + // Alg. 4 in his JAIR article from 2002) and it has also been + // contributed to the thesis work of Yacine Boufkhad. + // + if (propfixed (probe) >= stats.all.fixed) + continue; + + return probe; + } + } +} + +bool non_tautological_cube (std::vector<int> cube) { + std::sort (begin (cube), end (cube), clause_lit_less_than ()); + + for (size_t i = 0, j = 1; j < cube.size (); ++i, ++j) + if (cube[i] == cube[j]) + return false; + else if (cube[i] == -cube[j]) + return false; + else if (cube[i] == 0) + return false; + + return true; +} + +bool Internal::terminating_asked () { + + if (external->terminator && external->terminator->terminate ()) { + MSG ("connected terminator forces termination"); + return true; + } + + if (termination_forced) { + MSG ("termination forced"); + return true; + } + return false; +} + +// We run probing on all literals with some differences: +// +// * no limit on the number of propagations. We rely on terminating to +// stop() +// * we run only one round +// +// The run can be expensive, so we actually first run the cheaper +// occurrence version and only then run lookahead. +// +int Internal::lookahead_probing () { + + if (!active ()) + return 0; + + MSG ("lookahead-probe-round %" PRId64 + " without propagations limit and %zu assumptions", + stats.probingrounds, assumptions.size ()); + + termination_forced = false; + +#ifndef QUIET + int old_failed = stats.failed; + int64_t old_probed = stats.probed; +#endif + int64_t old_hbrs = stats.hbrs; + + if (unsat) + return INT_MIN; + if (level) + backtrack (); + if (!propagate ()) { + MSG ("empty clause before probing"); + learn_empty_clause (); + return INT_MIN; + } + + if (terminating_asked ()) + return most_occurring_literal (); + + decompose (); + + if (ternary ()) // If we derived a binary clause + decompose (); // then start another round of ELS. + + // Remove duplicated binary clauses and perform in essence hyper unary + // resolution, i.e., derive the unit '2' from '1 2' and '-1 2'. + // + mark_duplicated_binary_clauses_as_garbage (); + + lim.conflicts = -1; + + if (!probes.empty ()) + lookahead_flush_probes (); + + // We reset 'propfixed' since there was at least another conflict thus + // a new learned clause, which might produce new propagations (and hyper + // binary resolvents). During 'generate_probes' we keep the old value. + // + for (int idx = 1; idx <= max_var; idx++) + propfixed (idx) = propfixed (-idx) = -1; + + assert (unsat || propagated == trail.size ()); + propagated = propagated2 = trail.size (); + + int probe; + int res = most_occurring_literal (); + int max_hbrs = -1; + + set_mode (PROBE); + + MSG ("unsat = %d, terminating_asked () = %d ", unsat, + terminating_asked ()); + init_probehbr_lrat (); + while (!unsat && !terminating_asked () && + (probe = lookahead_next_probe ())) { + stats.probed++; + int hbrs; + + probe_assign_decision (probe); + if (probe_propagate ()) + hbrs = trail.size (), backtrack (); + else + hbrs = 0, failed_literal (probe); + clean_probehbr_lrat (); + if (max_hbrs < hbrs || + (max_hbrs == hbrs && + internal->bumped (probe) > internal->bumped (res))) { + res = probe; + max_hbrs = hbrs; + } + } + + reset_mode (PROBE); + + if (unsat) { + MSG ("probing derived empty clause"); + res = INT_MIN; + } else if (propagated < trail.size ()) { + MSG ("probing produced %zd units", + (size_t) (trail.size () - propagated)); + if (!propagate ()) { + MSG ("propagating units after probing results in empty clause"); + learn_empty_clause (); + res = INT_MIN; + } else + sort_watches (); + } + +#ifndef QUIET + int failed = stats.failed - old_failed; + int64_t probed = stats.probed - old_probed; +#endif + int64_t hbrs = stats.hbrs - old_hbrs; + + MSG ("lookahead-probe-round %" PRId64 " probed %" PRId64 + " and found %d failed literals", + stats.probingrounds, probed, failed); + + if (hbrs) + PHASE ("lookahead-probe-round", stats.probingrounds, + "found %" PRId64 " hyper binary resolvents", hbrs); + + MSG ("lookahead literal %d with %d\n", res, max_hbrs); + + return res; +} + +CubesWithStatus Internal::generate_cubes (int depth, int min_depth) { + if (!active () || depth == 0) { + CubesWithStatus cubes; + cubes.status = 0; + cubes.cubes.push_back (std::vector<int> ()); + return cubes; + } + + lookingahead = true; + START (lookahead); + MSG ("Generating cubes of depth %i", depth); + + // presimplify required due to assumptions + + termination_forced = false; + int res = already_solved (); + if (res == 0) + res = restore_clauses (); + if (unsat) + res = 10; + if (res != 0) + res = solve (true); + if (res != 0) { + MSG ("Solved during preprocessing"); + CubesWithStatus cubes; + cubes.status = res; + lookingahead = false; + STOP (lookahead); + return cubes; + } + + reset_limits (); + MSG ("generate cubes with %zu assumptions\n", assumptions.size ()); + + assert (ntab.empty ()); + std::vector<int> current_assumptions{assumptions}; + std::vector<std::vector<int>> cubes{{assumptions}}; + auto loccs{lookahead_populate_locc ()}; + LOG ("loccs populated\n"); + assert (ntab.empty ()); + + for (int i = 0; i < depth; ++i) { + LOG ("Probing at depth %i, currently %zu have been generated", i, + cubes.size ()); + std::vector<std::vector<int>> cubes2{std::move (cubes)}; + cubes.clear (); + + for (size_t j = 0; j < cubes2.size (); ++j) { + assert (ntab.empty ()); + assert (!unsat); + reset_assumptions (); + for (auto lit : cubes2[j]) + assume (lit); + restore_clauses (); + propagate (); + // preprocess_round(0); //uncomment maybe + + if (unsat) { + LOG ("current cube is unsat; skipping"); + unsat = false; + continue; + } + + int res = terminating_asked () ? lookahead_locc (loccs) + : lookahead_probing (); + if (unsat) { + LOG ("current cube is unsat; skipping"); + unsat = false; + continue; + } + + if (res == 0) { + LOG ("no lit to split %i", res); + cubes.push_back (cubes2[j]); + continue; + } + + assert (res != 0); + LOG ("splitting on lit %i", res); + std::vector<int> cube1{cubes2[j]}; + cube1.push_back (res); + std::vector<int> cube2{std::move (cubes2[j])}; + cube2.push_back (-res); + cubes.push_back (cube1); + cubes.push_back (cube2); + } + + if (terminating_asked () && i >= min_depth) + break; + } + + assert (std::for_each ( + std::begin (cubes), std::end (cubes), + [] (std::vector<int> cube) { return non_tautological_cube (cube); })); + reset_assumptions (); + + for (auto lit : current_assumptions) + assume (lit); + + STOP (lookahead); + lookingahead = false; + + if (unsat) { + LOG ("Solved during preprocessing"); + CubesWithStatus cubes; + cubes.status = 20; + return cubes; + } + + CubesWithStatus rcubes; + rcubes.status = 0; + rcubes.cubes = cubes; + + return rcubes; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/lratchecker.cpp b/src/sat/cadical/lratchecker.cpp new file mode 100644 index 000000000..1dcead375 --- /dev/null +++ b/src/sat/cadical/lratchecker.cpp @@ -0,0 +1,829 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +inline unsigned LratChecker::l2u (int lit) { + assert (lit); + assert (lit != INT_MIN); + unsigned res = 2 * (abs (lit) - 1); + if (lit < 0) + res++; + return res; +} + +signed char &LratChecker::mark (int lit) { + const unsigned u = l2u (lit); + assert (u < marks.size ()); + return marks[u]; +} + +signed char &LratChecker::checked_lit (int lit) { + const unsigned u = l2u (lit); + assert (u < checked_lits.size ()); + return checked_lits[u]; +} + +/*------------------------------------------------------------------------*/ + +LratCheckerClause *LratChecker::new_clause () { + const size_t size = imported_clause.size (); + assert (size <= UINT_MAX); + const int off = size ? 1 : 0; + const size_t bytes = + sizeof (LratCheckerClause) + (size - off) * sizeof (int); + LratCheckerClause *res = (LratCheckerClause *) new char[bytes]; + res->garbage = false; + res->next = 0; + res->hash = last_hash; + res->id = last_id; + res->size = size; + res->used = false; + res->tautological = false; + int *literals = res->literals, *p = literals; +#ifndef NDEBUG + for (auto &b : checked_lits) + assert (!b); // = false; +#endif + for (const auto &lit : imported_clause) { + *p++ = lit; + checked_lit (-lit) = true; + if (checked_lit (lit)) { + LOG (imported_clause, "LRAT CHECKER clause tautological"); + res->tautological = true; + } + } + for (const auto &lit : imported_clause) + checked_lit (-lit) = false; + num_clauses++; + + return res; +} + +void LratChecker::delete_clause (LratCheckerClause *c) { + assert (c); + if (!c->garbage) { + assert (num_clauses); + num_clauses--; + } else { + assert (num_garbage); + num_garbage--; + } + delete[] (char *) c; +} + +void LratChecker::enlarge_clauses () { + assert (num_clauses == size_clauses); + const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1; + LOG ("LRAT CHECKER enlarging clauses of checker from %" PRIu64 + " to %" PRIu64, + (uint64_t) size_clauses, (uint64_t) new_size_clauses); + LratCheckerClause **new_clauses; + new_clauses = new LratCheckerClause *[new_size_clauses]; + clear_n (new_clauses, new_size_clauses); + for (uint64_t i = 0; i < size_clauses; i++) { + for (LratCheckerClause *c = clauses[i], *next; c; c = next) { + next = c->next; + const uint64_t h = reduce_hash (c->hash, new_size_clauses); + c->next = new_clauses[h]; + new_clauses[h] = c; + } + } + delete[] clauses; + clauses = new_clauses; + size_clauses = new_size_clauses; +} + +// Probably not necessary since we have no watches. +// +void LratChecker::collect_garbage_clauses () { + + stats.collections++; + + LOG ("LRAT CHECKER collecting %" PRIu64 " garbage clauses %.0f%%", + num_garbage, percent (num_garbage, num_clauses)); + + for (LratCheckerClause *c = garbage, *next; c; c = next) + next = c->next, delete_clause (c); + + assert (!num_garbage); + garbage = 0; +} + +/*------------------------------------------------------------------------*/ + +LratChecker::LratChecker (Internal *i) + : internal (i), size_vars (0), concluded (false), num_clauses (0), + num_finalized (0), num_garbage (0), size_clauses (0), clauses (0), + garbage (0), last_hash (0), last_id (0), current_id (0) { + + // Initialize random number table for hash function. + // + Random random (42); + for (unsigned n = 0; n < num_nonces; n++) { + uint64_t nonce = random.next (); + if (!(nonce & 1)) + nonce++; + assert (nonce), assert (nonce & 1); + nonces[n] = nonce; + } + + memset (&stats, 0, sizeof (stats)); // Initialize statistics. +} + +void LratChecker::connect_internal (Internal *i) { + internal = i; + LOG ("connected to internal"); +} + +LratChecker::~LratChecker () { + LOG ("LRAT CHECKER delete"); + for (size_t i = 0; i < size_clauses; i++) + for (LratCheckerClause *c = clauses[i], *next; c; c = next) + next = c->next, delete_clause (c); + for (LratCheckerClause *c = garbage, *next; c; c = next) + next = c->next, delete_clause (c); + delete[] clauses; +} + +/*------------------------------------------------------------------------*/ + +void LratChecker::enlarge_vars (int64_t idx) { + + assert (0 < idx), assert (idx <= INT_MAX); + + int64_t new_size_vars = size_vars ? 2 * size_vars : 2; + while (idx >= new_size_vars) + new_size_vars *= 2; + LOG ("LRAT CHECKER enlarging variables of checker from %" PRId64 + " to %" PRId64 "", + size_vars, new_size_vars); + + marks.resize (2 * new_size_vars); + checked_lits.resize (2 * new_size_vars); + + assert (idx < new_size_vars); + size_vars = new_size_vars; +} + +inline void LratChecker::import_literal (int lit) { + assert (lit); + assert (lit != INT_MIN); + int idx = abs (lit); + if (idx >= size_vars) + enlarge_vars (idx); + imported_clause.push_back (lit); +} + +void LratChecker::import_clause (const vector<int> &c) { + for (const auto &lit : c) + import_literal (lit); +} + +/*------------------------------------------------------------------------*/ + +uint64_t LratChecker::reduce_hash (uint64_t hash, uint64_t size) { + assert (size > 0); + unsigned shift = 32; + uint64_t res = hash; + while ((((uint64_t) 1) << shift) > size) { + res ^= res >> shift; + shift >>= 1; + } + res &= size - 1; + assert (res < size); + return res; +} + +uint64_t LratChecker::compute_hash (const int64_t id) { + assert (id > 0); + unsigned j = id % num_nonces; + uint64_t tmp = nonces[j] * (uint64_t) id; + return last_hash = tmp; +} + +LratCheckerClause **LratChecker::find (const int64_t id) { + stats.searches++; + LratCheckerClause **res, *c; + const uint64_t hash = compute_hash (id); + const uint64_t h = reduce_hash (hash, size_clauses); + for (res = clauses + h; (c = *res); res = &c->next) { + if (c->hash == hash && c->id == id) { + break; + } + stats.collisions++; + } + return res; +} + +void LratChecker::insert () { + stats.insertions++; + if (num_clauses == size_clauses) + enlarge_clauses (); + const uint64_t h = reduce_hash (compute_hash (last_id), size_clauses); + LratCheckerClause *c = new_clause (); + c->next = clauses[h]; + clauses[h] = c; +} + +/*------------------------------------------------------------------------*/ + +// "strict" resolution check instead of rup check +bool LratChecker::check_resolution (vector<int64_t> proof_chain) { + if (proof_chain.empty ()) { + LOG ("LRAT CHECKER resolution check skipped clause is tautological"); + return true; + } + // LOG (imported_clause, "LRAT CHECKER checking clause with resolution"); +#ifndef NDEBUG + for (auto &b : checked_lits) + assert (!b); // = false; +#endif + if (!proof_chain.size () || proof_chain.back () < 0) + return false; + LratCheckerClause *c = *find (proof_chain.back ()); + assert (c); + for (int *i = c->literals; i < c->literals + c->size; i++) { + int lit = *i; + checked_lit (lit) = true; + assert (!checked_lit (-lit)); + } + for (auto p = proof_chain.end () - 2; p >= proof_chain.begin (); p--) { + auto &id = *p; + c = *find (id); + assert (c); // since this is checked in check already + for (int *i = c->literals; i < c->literals + c->size; i++) { + int lit = *i; + if (!checked_lit (-lit)) + checked_lit (lit) = true; + else + checked_lit (-lit) = false; + } + } + for (const auto &lit : imported_clause) { + if (checked_lit (-lit)) { + LOG ("LRAT CHECKER resolution failed, resolved literal %d in learned " + "clause", + lit); + for (auto &b : checked_lits) + b = false; // clearing checking bits + return false; + } + if (!checked_lit (lit)) { + // learned clause is subsumed by resolvents + checked_lit (lit) = true; + } + checked_lit (-lit) = true; + } + bool failed = false; + for (int64_t lit = 1; lit < size_vars; lit++) { + bool ok = checked_lit (lit) && checked_lit (-lit); + ok = ok || (!checked_lit (lit) && !checked_lit (-lit)); + checked_lit (lit) = checked_lit (-lit) = false; + if (!ok && !failed) { + LOG ("LRAT CHECKER resolution failed, learned clause does not match " + "on " + "variable %" PRId64, + lit); + failed = true; + } + } + + return !failed; +} + +/*------------------------------------------------------------------------*/ + +bool LratChecker::check (vector<int64_t> proof_chain) { + LOG (imported_clause, "LRAT CHECKER checking clause"); + stats.checks++; +#ifndef NDEBUG + for (auto &b : checked_lits) + assert (!b); // = false; +#endif + bool taut = false; + for (const auto &lit : imported_clause) { // tautological clauses + checked_lit (-lit) = true; + if (checked_lit (lit)) { + LOG (imported_clause, "LRAT CHECKER clause tautological"); + assert (!proof_chain.size ()); // would be unnecessary hence a bug + taut = true; + } + } + // we assume that we can have RUP and ER clauses. One side of the ER + // clauses are pure, i.e. without any chain, the long clause is blocked, + // so the chain consists only of negative ids. Therefore these checks are + // enough to distiguish between RUP and ER + if (taut || !proof_chain.size () || proof_chain.back () < 0) { + for (const auto &lit : imported_clause) { // tautological clauses + checked_lit (-lit) = false; + } + return taut; + } + + vector<LratCheckerClause *> used_clauses; + bool checking = false; + for (auto &id : proof_chain) { + LratCheckerClause *c = *find (id); + if (!c) { + LOG ("LRAT CHECKER LRAT failed. Did not find clause with id %" PRIu64, + id); + break; + } + if (c->tautological) { + LOG ("LRAT CHECKER LRAT failed. Clause with id %" PRId64 + " is tautological", + id); + break; + } + used_clauses.push_back (c); + if (c->used) { + LOG ("LRAT CHECKER LRAT failed. Id %" PRId64 + " was used multiple times", + id); + break; + } else + c->used = true; + int unit = 0; + for (int *i = c->literals; i < c->literals + c->size; i++) { + int lit = *i; + if (checked_lit (-lit)) + continue; + if (unit && unit != lit) { + unit = INT_MIN; // multiple unfalsified literals + break; + } + unit = lit; // potential unit + } + if (unit == INT_MIN) { + LOG ("LRAT CHECKER check failed, found non unit clause %" PRId64, id); + break; + } + if (!unit) { + LOG ("LRAT CHECKER check succeded, clause falsified %" PRId64, id); + checking = true; + break; + } + // LOG ("LRAT CHECKER found unit clause %" PRIu64 ", assign %d", id, + // unit); + checked_lit (unit) = true; + } + for (auto &lc : used_clauses) { + lc->used = false; + } + for (auto &b : checked_lits) + b = false; + if (!checking) { + LOG ("LRAT CHECKER failed, no conflict found"); + return false; // check failed because no empty clause was found + } + return true; +} + +bool LratChecker::check_blocked (vector<int64_t> proof_chain) { + for (const auto &lit : imported_clause) { + checked_lit (-lit) = true; + mark (-lit) = true; + } + for (size_t i = 0; i < size_clauses; i++) { + for (LratCheckerClause *c = clauses[i], *next; c; c = next) { + next = c->next; + if (c->garbage) + continue; + // if c is part of the proof chain its id occurs negatively there. + if (std::find (proof_chain.begin (), proof_chain.end (), -c->id) != + proof_chain.end ()) { + // clause needs to be blocked + unsigned count = 0; + vector<int> candidates; + for (unsigned i = 0; i < c->size; i++) { + const int lit = c->literals[i]; + if (checked_lit (lit)) { + count++; + } + if (mark (lit)) { + candidates.push_back (lit); + } + } + if (count < 2) { + // check failed + for (const auto &lit : imported_clause) { + checked_lit (-lit) = false; + mark (-lit) = false; + } + return false; + } else { + // all literals outside of candidates are not valid RAT candidates + for (auto &lit : imported_clause) { + if (mark (-lit) && + std::find (candidates.begin (), candidates.end (), -lit) == + candidates.end ()) { + mark (-lit) = false; + } + } + } + } else { + // any literal contained in the clause is not a valid RAT candidate + for (unsigned i = 0; i < c->size; i++) { + const int lit = c->literals[i]; + if (checked_lit (lit)) { + mark (lit) = false; + } + } + } + } + } + bool success = false; + for (const auto &lit : imported_clause) { + if (mark (-lit)) + success = true; + checked_lit (-lit) = mark (-lit) = false; + } + return success; +} + +/*------------------------------------------------------------------------*/ + +void LratChecker::add_original_clause (int64_t id, bool, + const vector<int> &c, bool restore) { + START (checking); + LOG (c, "LRAT CHECKER addition of original clause[%" PRId64 "]", id); + if (restore) + restore_clause (id, c); + stats.added++; + stats.original++; + import_clause (c); + last_id = id; + if (!restore && id == 1 + current_id) + current_id = id; + + if (size_clauses && !restore) { + LratCheckerClause **p = find (id), *d = *p; + if (d) { + fatal_message_start (); + fputs ("different clause with id ", stderr); + fprintf (stderr, "%" PRId64, id); + fputs (" already present\n", stderr); + fatal_message_end (); + } + } + assert (id); + insert (); + imported_clause.clear (); + STOP (checking); +} + +void LratChecker::add_derived_clause (int64_t id, bool, + const vector<int> &c, + const vector<int64_t> &proof_chain) { + START (checking); + LOG (c, "LRAT CHECKER addition of derived clause[%" PRId64 "]", id); + stats.added++; + stats.derived++; + import_clause (c); + last_id = id; + assert (id == current_id + 1); + current_id = id; + if (size_clauses) { + LratCheckerClause **p = find (id), *d = *p; + if (d) { + fatal_message_start (); + fputs ("different clause with id ", stderr); + fprintf (stderr, "%" PRId64, id); + fputs (" already present\n", stderr); + fatal_message_end (); + } + } + assert (id); + bool failed = true; + if (check (proof_chain) && check_resolution (proof_chain)) { + failed = false; + } else if (check_blocked (proof_chain)) { + failed = false; + } + if (failed) { + LOG (proof_chain, "LRAT CHECKER check failed with chain"); +#ifdef LOGGING + for (const auto &pid : proof_chain) { + const int64_t aid = abs (pid); + LratCheckerClause **p = find (aid), *d = *p; + LOG (d->literals, d->size, "clause[%" PRId64 "]", pid); + } +#endif + fatal_message_start (); + fputs ("failed to check derived clause:\n", stderr); + for (const auto &lit : imported_clause) + fprintf (stderr, "%d ", lit); + fputc ('0', stderr); + fatal_message_end (); + } else + insert (); + imported_clause.clear (); + STOP (checking); +} + +void LratChecker::add_assumption_clause (int64_t id, const vector<int> &c, + const vector<int64_t> &chain) { + for (auto &lit : c) { + if (std::find (assumptions.begin (), assumptions.end (), -lit) != + assumptions.end ()) + continue; + if (std::find (constraint.begin (), constraint.end (), -lit) != + constraint.end ()) + continue; + fatal_message_start (); + fputs ("clause contains non assumptions or constraint literals\n", + stderr); + fatal_message_end (); + } + add_derived_clause (id, true, c, chain); + delete_clause (id, true, c); + assumption_clauses.push_back (id); +} + +void LratChecker::add_assumption (int a) { assumptions.push_back (a); } + +void LratChecker::add_constraint (const vector<int> &c) { + constraint.clear (); + for (auto &lit : c) { + assert (lit); + if (std::find (constraint.begin (), constraint.end (), lit) != + constraint.end ()) + continue; + constraint.push_back (lit); + } +} + +void LratChecker::reset_assumptions () { + assumption_clauses.clear (); + assumptions.clear (); + concluded = false; + // constraint.clear (); +} + +void LratChecker::conclude_unsat (ConclusionType conclusion, + const vector<int64_t> &ids) { + if (concluded) { + fatal_message_start (); + fputs ("already concluded\n", stderr); + fatal_message_end (); + } + concluded = true; + if (conclusion == CONFLICT) { + LratCheckerClause **p = find (ids.back ()), *d = *p; + if (!d || d->size) { + fatal_message_start (); + fputs ("empty clause not in proof\n", stderr); + fatal_message_end (); + } + return; + } else if (conclusion == ASSUMPTIONS) { + if (ids.size () != 1 || assumption_clauses.size () != 1) { + fatal_message_start (); + fputs ("expected exactly one assumption clause\n", stderr); + fatal_message_end (); + } + if (ids.back () != assumption_clauses.back ()) { + fatal_message_start (); + fputs ("conclusion is not an assumption clause\n", stderr); + fatal_message_end (); + } + return; + } else { + assert (conclusion == CONSTRAINT); + if (constraint.size () != ids.size ()) { + fatal_message_start (); + fputs ("not complete conclusion given for constraint\n", stderr); + fputs ("The constraint contains the literals: ", stderr); + for (auto c : constraint) { + fprintf (stderr, "%d ", c); + } + + fputs ("\nThe ids are: ", stderr); + for (auto c : ids) { + fprintf (stderr, "%" PRId64 " ", c); + } + fatal_message_end (); + } + for (auto &id : ids) { + if (std::find (assumption_clauses.begin (), assumption_clauses.end (), + id) != assumption_clauses.end ()) + continue; + fatal_message_start (); + fputs ("assumption clause for constraint missing\n", stderr); + fatal_message_end (); + } + } +} + +/*------------------------------------------------------------------------*/ + +void LratChecker::delete_clause (int64_t id, bool, const vector<int> &c) { + START (checking); + LOG (c, "LRAT CHECKER checking deletion of clause[%" PRId64 "]", id); + stats.deleted++; + import_clause (c); + last_id = id; + LratCheckerClause **p = find (id), *d = *p; + if (d) { + for (const auto &lit : imported_clause) + mark (lit) = true; + const int *dp = d->literals; + for (unsigned i = 0; i < d->size; i++) { + int lit = *(dp + i); + if (!mark (lit)) { // should never happen since ids + fatal_message_start (); // are unique. + fputs ("deleted clause not in proof:\n", stderr); + for (const auto &lit : imported_clause) + fprintf (stderr, "%d ", lit); + fputc ('0', stderr); + fatal_message_end (); + } + } + for (const auto &lit : imported_clause) + mark (lit) = false; + + // Remove from hash table, mark as garbage, connect to garbage list. + num_garbage++; + assert (num_clauses); + num_clauses--; + *p = d->next; + d->next = garbage; + garbage = d; + d->garbage = true; + + // If there are enough garbage clauses collect them. + // TODO: probably can just delete clause directly without + // specific garbage collection phase. + if (num_garbage > 0.5 * max ((size_t) size_clauses, (size_t) size_vars)) + collect_garbage_clauses (); + } else { + fatal_message_start (); + fputs ("deleted clause not in proof:\n", stderr); + for (const auto &lit : imported_clause) + fprintf (stderr, "%d ", lit); + fputc ('0', stderr); + fatal_message_end (); + } + imported_clause.clear (); + STOP (checking); +} + +/*------------------------------------------------------------------------*/ + +void LratChecker::weaken_minus (int64_t id, const vector<int> &c) { + LOG (c, "LRAT CHECKER saving clause[%" PRId64 "] to restore later", id); + import_clause (c); + + assert (id <= current_id); + last_id = id; + LratCheckerClause **p = find (id), *d = *p; + if (d) { + for (const auto &lit : imported_clause) + mark (lit) = true; + const int *dp = d->literals; + for (unsigned i = 0; i < d->size; i++) { + int lit = *(dp + i); + if (!mark (lit)) { // should never happen since ids + fatal_message_start (); // are unique. + fputs ("deleted clause not in proof:\n", stderr); + for (const auto &lit : imported_clause) + fprintf (stderr, "%d ", lit); + fputc ('0', stderr); + fatal_message_end (); + } + } + for (const auto &lit : imported_clause) + mark (lit) = false; + } else { + fatal_message_start (); + fputs ("weakened clause not in proof:\n", stderr); + for (const auto &lit : imported_clause) + fprintf (stderr, "%d ", lit); + fputc ('0', stderr); + fatal_message_end (); + } + imported_clause.clear (); + + vector<int> e = c; + sort (begin (e), end (e)); + clauses_to_reconstruct[id] = e; +} + +void LratChecker::restore_clause (int64_t id, const vector<int> &c) { + LOG (c, "LRAT CHECKER check of restoration of clause[%" PRId64 "]", id); + if (clauses_to_reconstruct.find (id) == end (clauses_to_reconstruct)) { + fatal_message_start (); + fputs ("restoring clauses not deleted previously:\n", stderr); + for (const auto &lit : c) + fprintf (stderr, "%d ", lit); + fputc ('0', stderr); + fatal_message_end (); + } + vector<int> e = c; + sort (begin (e), end (e)); + const vector<int> &d = clauses_to_reconstruct.find (id)->second; + bool eq = true; + if (c.size () != d.size ()) { + eq = false; + } + + for (std::vector<int>::size_type i = 0; i < e.size () && eq; ++i) { + eq = (e[i] == d[i]); + } + + if (!eq) { + fatal_message_start (); + fputs ("restoring clause that is different than the one imported:\n", + stderr); + for (const auto &lit : c) + fprintf (stderr, "%d ", lit); + fputc ('0', stderr); + fputs ("vs:\n", stderr); + for (const auto &lit : d) + fprintf (stderr, "%d ", lit); + fputc ('0', stderr); + fatal_message_end (); + } + + clauses_to_reconstruct.erase (id); +} + +void LratChecker::finalize_clause (int64_t id, const vector<int> &c) { + START (checking); + LOG (c, "LRAT CHECKER checking finalize of clause[%" PRId64 "]", id); + stats.finalized++; + num_finalized++; + import_clause (c); + assert (id <= current_id); + last_id = id; + LratCheckerClause **p = find (id), *d = *p; + if (d) { + for (const auto &lit : imported_clause) + mark (lit) = true; + const int *dp = d->literals; + for (unsigned i = 0; i < d->size; i++) { + int lit = *(dp + i); + if (!mark (lit)) { // should never happen since ids + fatal_message_start (); // are unique. + fputs ("deleted clause not in proof:\n", stderr); + for (const auto &lit : imported_clause) + fprintf (stderr, "%d ", lit); + fputc ('0', stderr); + fatal_message_end (); + } + } + for (const auto &lit : imported_clause) + mark (lit) = false; + + } else { + fatal_message_start (); + fputs ("deleted clause not in proof:\n", stderr); + for (const auto &lit : imported_clause) + fprintf (stderr, "%d ", lit); + fputc ('0', stderr); + fatal_message_end (); + } + imported_clause.clear (); + STOP (checking); +} + +// check if all clauses have been deleted +void LratChecker::report_status (int, int64_t) { + START (checking); + if (num_finalized == num_clauses) { + num_finalized = 0; + LOG ("LRAT CHECKER successful finalize check, all clauses have been " + "deleted"); + } else { + fatal_message_start (); + fputs ("finalize check failed ", stderr); + fprintf (stderr, "%" PRIu64, num_clauses); + fputs (" are not finalized", stderr); + fatal_message_end (); + } + STOP (checking); +} + +/*------------------------------------------------------------------------*/ + +void LratChecker::dump () { + int max_var = 0; + for (uint64_t i = 0; i < size_clauses; i++) + for (LratCheckerClause *c = clauses[i]; c; c = c->next) + for (unsigned i = 0; i < c->size; i++) + if (abs (c->literals[i]) > max_var) + max_var = abs (c->literals[i]); + printf ("p cnf %d %" PRIu64 "\n", max_var, num_clauses); + for (uint64_t i = 0; i < size_clauses; i++) + for (LratCheckerClause *c = clauses[i]; c; c = c->next) { + for (unsigned i = 0; i < c->size; i++) + printf ("%d ", c->literals[i]); + printf ("0\n"); + } +} + +void LratChecker::begin_proof (int64_t id) { current_id = id; } + +} // namespace CaDiCaL diff --git a/src/sat/cadical/lratchecker.hpp b/src/sat/cadical/lratchecker.hpp new file mode 100644 index 000000000..ff9a5ba90 --- /dev/null +++ b/src/sat/cadical/lratchecker.hpp @@ -0,0 +1,164 @@ +#ifndef _lratchecker_hpp_INCLUDED +#define _lratchecker_hpp_INCLUDED + +/*------------------------------------------------------------------------*/ +#include <unordered_map> + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// This checker implements an LRUP checker. +// It requires LRAT-style proof chains for each learned clause +// +// Most of the infrastructure is taken from checker, but without the +// propagation + +/*------------------------------------------------------------------------*/ + +struct LratCheckerClause { + LratCheckerClause *next; // collision chain link for hash table + uint64_t hash; // previously computed full 64-bit hash + int64_t id; // id of clause + bool garbage; // for garbage clauses + unsigned size; + bool used; + bool tautological; + int literals[1]; // 'literals' of length 'size' +}; + +/*------------------------------------------------------------------------*/ + +class LratChecker : public StatTracer { + + Internal *internal; + + // Capacity of variable values. + // + int64_t size_vars; + + // The 'watchers' and 'marks' data structures are not that time critical + // and thus we access them by first mapping a literal to 'unsigned'. + // + static unsigned l2u (int lit); + + signed char &checked_lit (int lit); + signed char &mark (int lit); + + vector<signed char> checked_lits; + vector<signed char> marks; // mark bits of literals + unordered_map<int64_t, vector<int>> clauses_to_reconstruct; + vector<int> assumptions; + vector<int> constraint; + bool concluded; + + uint64_t num_clauses; // number of clauses in hash table + uint64_t num_finalized; + uint64_t num_garbage; // number of garbage clauses + uint64_t size_clauses; // size of clause hash table + LratCheckerClause **clauses; // hash table of clauses + LratCheckerClause *garbage; // linked list of garbage clauses + + vector<int> imported_clause; // original clause for reporting + vector<int64_t> assumption_clauses; + + void enlarge_vars (int64_t idx); + void import_literal (int lit); + void import_clause (const vector<int> &); + + static const unsigned num_nonces = 4; + + uint64_t nonces[num_nonces]; // random numbers for hashing + uint64_t last_hash; // last computed hash value of clause + int64_t last_id; // id of the last added/deleted clause + int64_t current_id; // id of the last added clause + uint64_t compute_hash (int64_t); // compute and save hash value of clause + + // Reduce hash value to the actual size. + // + static uint64_t reduce_hash (uint64_t hash, uint64_t size); + + void enlarge_clauses (); // enlarge hash table for clauses + void insert (); // insert clause in hash table + LratCheckerClause ** + find (const int64_t); // find clause position in hash table + + void add_clause (const char *type); + + void collect_garbage_clauses (); + + LratCheckerClause *new_clause (); + void delete_clause (LratCheckerClause *); + + bool check (vector<int64_t>); // check RUP + bool check_resolution (vector<int64_t>); // check resolution + bool check_blocked (vector<int64_t>); // check ER + + struct { + + int64_t added; // number of added clauses + int64_t original; // number of added original clauses + int64_t derived; // number of added derived clauses + + int64_t deleted; // number of deleted clauses + int64_t finalized; // number of finalized clauses + + int64_t insertions; // number of clauses added to hash table + int64_t collisions; // number of hash collisions in 'find' + int64_t searches; // number of searched clauses in 'find' + + int64_t checks; // number of implication checks + + int64_t collections; // garbage collections + + } stats; + +public: + LratChecker (Internal *); + virtual ~LratChecker (); + + void connect_internal (Internal *i) override; + void begin_proof (int64_t) override; + + void add_original_clause (int64_t, bool, const vector<int> &, + bool restore) override; + void restore_clause (int64_t, const vector<int> &); + + // check the proof chain for the new clause and add it to the checker + void add_derived_clause (int64_t, bool, const vector<int> &, + const vector<int64_t> &) override; + + // check if the clause is present and delete it from the checker + void delete_clause (int64_t, bool, const vector<int> &) override; + // check if the clause is present and delete it from the checker + void weaken_minus (int64_t, const vector<int> &) override; + + // check if the clause is present and delete it from the checker + void finalize_clause (int64_t, const vector<int> &) override; + + // check the proof chain of the assumption clause and delete it + // immediately also check that they contain only assumptions and + // constraints + void add_assumption_clause (int64_t, const vector<int> &, + const vector<int64_t> &) override; + + // mark lit as assumption + void add_assumption (int) override; + + // mark lits as constraint + void add_constraint (const vector<int> &) override; + + void reset_assumptions () override; + + // check if all clauses have been deleted + void report_status (int, int64_t) override; + + void conclude_unsat (ConclusionType, const vector<int64_t> &) override; + + void print_stats () override; + void dump (); // for debugging purposes only +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/lrattracer.cpp b/src/sat/cadical/lrattracer.cpp new file mode 100644 index 000000000..0bcd9a35b --- /dev/null +++ b/src/sat/cadical/lrattracer.cpp @@ -0,0 +1,200 @@ +#include "internal.hpp" + +#include <limits.h> + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +LratTracer::LratTracer (Internal *i, File *f, bool b) + : internal (i), file (f), binary (b) +#ifndef QUIET + , + added (0), deleted (0) +#endif + , + latest_id (0) { + (void) internal; +} + +void LratTracer::connect_internal (Internal *i) { + internal = i; + file->connect_internal (internal); + LOG ("LRAT TRACER connected to internal"); +} + +LratTracer::~LratTracer () { + LOG ("LRAT TRACER delete"); + delete file; +} + +/*------------------------------------------------------------------------*/ + +inline void LratTracer::put_binary_zero () { + assert (binary); + assert (file); + file->put ((unsigned char) 0); +} + +inline void LratTracer::put_binary_lit (int lit) { + assert (binary); + assert (file); + assert (lit != INT_MIN); + unsigned idx = abs (lit); + assert (idx < (1u << 31)); + unsigned x = 2 * idx + (lit < 0); + unsigned char ch; + while (x & ~0x7f) { + ch = (x & 0x7f) | 0x80; + file->put (ch); + x >>= 7; + } + ch = x; + file->put (ch); +} + +inline void LratTracer::put_binary_id (int64_t id) { + assert (binary); + assert (file); + uint64_t x = abs (id); + x = 2 * x + (id < 0); + unsigned char ch; + while (x & ~0x7f) { + ch = (x & 0x7f) | 0x80; + file->put (ch); + x >>= 7; + } + ch = x; + file->put (ch); +} + +/*------------------------------------------------------------------------*/ + +void LratTracer::lrat_add_clause (int64_t id, const vector<int> &clause, + const vector<int64_t> &chain) { + if (delete_ids.size ()) { + if (!binary) + file->put (latest_id), file->put (" "); + if (binary) + file->put ('d'); + else + file->put ("d "); + for (auto &did : delete_ids) { + if (binary) + put_binary_id (did); + else + file->put (did), file->put (" "); + } + if (binary) + put_binary_zero (); + else + file->put ("0\n"); + delete_ids.clear (); + } + latest_id = id; + + if (binary) + file->put ('a'), put_binary_id (id); + else + file->put (id), file->put (" "); + for (const auto &external_lit : clause) + if (binary) + put_binary_lit (external_lit); + else + file->put (external_lit), file->put (' '); + if (binary) + put_binary_zero (); + else + file->put ("0 "); + for (const auto &c : chain) + if (binary) + put_binary_id (c); + else + file->put (c), file->put (' '); // in proof chain, so they get + if (binary) + put_binary_zero (); // since cadical has no rat-steps + else + file->put ("0\n"); // this is just 2c here +} + +void LratTracer::lrat_delete_clause (int64_t id) { + delete_ids.push_back (id); // pushing off deletion for later +} + +/*------------------------------------------------------------------------*/ + +void LratTracer::add_derived_clause (int64_t id, bool, + const vector<int> &clause, + const vector<int64_t> &chain) { + if (file->closed ()) + return; + LOG ("LRAT TRACER tracing addition of derived clause"); + lrat_add_clause (id, clause, chain); +#ifndef QUIET + added++; +#endif +} + +void LratTracer::delete_clause (int64_t id, bool, const vector<int> &) { + if (file->closed ()) + return; + LOG ("LRAT TRACER tracing deletion of clause"); + lrat_delete_clause (id); +#ifndef QUIET + deleted++; +#endif +} + +void LratTracer::begin_proof (int64_t id) { + if (file->closed ()) + return; + LOG ("LRAT TRACER tracing begin of proof"); + latest_id = id; +} + +/*------------------------------------------------------------------------*/ + +bool LratTracer::closed () { return file->closed (); } + +#ifndef QUIET + +void LratTracer::print_statistics () { + uint64_t bytes = file->bytes (); + uint64_t total = added + deleted; + MSG ("LRAT %" PRId64 " added clauses %.2f%%", added, + percent (added, total)); + MSG ("LRAT %" PRId64 " deleted clauses %.2f%%", deleted, + percent (deleted, total)); + MSG ("LRAT %" PRId64 " bytes (%.2f MB)", bytes, + bytes / (double) (1 << 20)); +} + +#endif + +void LratTracer::close (bool print) { + assert (!closed ()); + file->close (); +#ifndef QUIET + if (print) { + MSG ("LRAT proof file '%s' closed", file->name ()); + print_statistics (); + } +#else + (void) print; +#endif +} + +void LratTracer::flush (bool print) { + assert (!closed ()); + file->flush (); +#ifndef QUIET + if (print) { + MSG ("LRAT proof file '%s' flushed", file->name ()); + print_statistics (); + } +#else + (void) print; +#endif +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/lrattracer.hpp b/src/sat/cadical/lrattracer.hpp new file mode 100644 index 000000000..208cf81e2 --- /dev/null +++ b/src/sat/cadical/lrattracer.hpp @@ -0,0 +1,57 @@ +#ifndef _lrattracer_h_INCLUDED +#define _lrattracer_h_INCLUDED + +namespace CaDiCaL { + +class LratTracer : public FileTracer { + + Internal *internal; + File *file; + bool binary; + +#ifndef QUIET + int64_t added, deleted; +#endif + int64_t latest_id; + vector<int64_t> delete_ids; + + void put_binary_zero (); + void put_binary_lit (int external_lit); + void put_binary_id (int64_t id); + + // support LRAT + void lrat_add_clause (int64_t, const vector<int> &, + const vector<int64_t> &); + void lrat_delete_clause (int64_t); + +public: + // own and delete 'file' + LratTracer (Internal *, File *file, bool binary); + ~LratTracer (); + + void connect_internal (Internal *i) override; + void begin_proof (int64_t) override; + + void add_original_clause (int64_t, bool, const vector<int> &, + bool = false) override {} // skip + + void add_derived_clause (int64_t, bool, const vector<int> &, + const vector<int64_t> &) override; + + void delete_clause (int64_t, bool, const vector<int> &) override; + + void finalize_clause (int64_t, const vector<int> &) override {} // skip + + void report_status (int, int64_t) override {} // skip + +#ifndef QUIET + void print_statistics (); +#endif + bool closed () override; + void close (bool) override; + void flush (bool) override; +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/lucky.cpp b/src/sat/cadical/lucky.cpp new file mode 100644 index 000000000..19da3ee28 --- /dev/null +++ b/src/sat/cadical/lucky.cpp @@ -0,0 +1,434 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +// It turns out that even in the competition there are formulas which are +// easy to satisfy by either setting all variables to the same truth value +// or by assigning variables to the same value and propagating it. In the +// latter situation this can be done either in the order of all variables +// (forward or backward) or in the order of all clauses. These lucky +// assignments can be tested initially in a kind of pre-solving step. + +// This function factors out clean up code common among the 'lucky' +// functions for backtracking and resetting a potential conflict. One could +// also use exceptions here, but there are two different reasons for +// aborting early. The first kind of aborting is due to asynchronous +// termination and the second kind due to a situation in which it is clear +// that a particular function will not be successful (for instance a +// completely negative clause is found). The latter situation returns zero +// and will just abort the particular lucky function, while the former will +// abort all (by returning '-1'). + +int Internal::unlucky (int res) { + if (level > 0) + backtrack (); + if (conflict) + conflict = 0; + return res; +} + +int Internal::trivially_false_satisfiable () { + LOG ("checking that all clauses contain a negative literal"); + assert (!level); + assert (assumptions.empty ()); + for (const auto &c : clauses) { + if (terminated_asynchronously (100)) + return unlucky (-1); + if (c->garbage) + continue; + if (c->redundant) + continue; + bool satisfied = false, found_negative_literal = false; + for (const auto &lit : *c) { + const signed char tmp = val (lit); + if (tmp > 0) { + satisfied = true; + break; + } + if (tmp < 0) + continue; + if (lit > 0) + continue; + found_negative_literal = true; + break; + } + if (satisfied || found_negative_literal) + continue; + LOG (c, "found purely positively"); + return unlucky (0); + } + VERBOSE (1, "all clauses contain a negative literal"); + for (auto idx : vars) { + if (terminated_asynchronously (10)) + return unlucky (-1); + if (val (idx)) + continue; + search_assume_decision (-idx); + if (propagate ()) + continue; + assert (level > 0); + LOG ("propagation failed including redundant clauses"); + return unlucky (0); + } + stats.lucky.constant.zero++; + return 10; +} + +int Internal::trivially_true_satisfiable () { + LOG ("checking that all clauses contain a positive literal"); + assert (!level); + assert (assumptions.empty ()); + for (const auto &c : clauses) { + if (terminated_asynchronously (100)) + return unlucky (-1); + if (c->garbage) + continue; + if (c->redundant) + continue; + bool satisfied = false, found_positive_literal = false; + for (const auto &lit : *c) { + const signed char tmp = val (lit); + if (tmp > 0) { + satisfied = true; + break; + } + if (tmp < 0) + continue; + if (lit < 0) + continue; + found_positive_literal = true; + break; + } + if (satisfied || found_positive_literal) + continue; + LOG (c, "found purely negatively"); + return unlucky (0); + } + VERBOSE (1, "all clauses contain a positive literal"); + for (auto idx : vars) { + if (terminated_asynchronously (10)) + return unlucky (-1); + if (val (idx)) + continue; + search_assume_decision (idx); + if (propagate ()) + continue; + assert (level > 0); + LOG ("propagation failed including redundant clauses"); + return unlucky (0); + } + stats.lucky.constant.one++; + return 10; +} + +/*------------------------------------------------------------------------*/ +inline bool Internal::lucky_propagate_discrepency (int dec) { + search_assume_decision (dec); + bool no_conflict = propagate (); + if (no_conflict) + return false; + if (level > 1) { + backtrack (level - 1); + search_assume_decision (-dec); + no_conflict = propagate (); + if (no_conflict) + return false; + return true; + } else { + analyze (); + assert (!level); + no_conflict = propagate (); + if (!no_conflict) { + analyze (); + LOG ("lucky inconsistency backward assigning to true"); + return true; + } + } + return false; +} + +int Internal::forward_false_satisfiable () { + LOG ("checking increasing variable index false assignment"); + assert (!unsat); + assert (!level); + assert (assumptions.empty ()); + for (auto idx : vars) { + START: + if (terminated_asynchronously (100)) + return unlucky (-1); + if (val (idx)) + continue; + if (lucky_propagate_discrepency (-idx)) { + if (unsat) + return 20; + else + return unlucky (0); + } else + goto START; + } + VERBOSE (1, "forward assuming variables false satisfies formula"); + assert (satisfied ()); + stats.lucky.forward.zero++; + return 10; +} + +int Internal::forward_true_satisfiable () { + LOG ("checking increasing variable index true assignment"); + assert (!unsat); + assert (!level); + assert (assumptions.empty ()); + for (auto idx : vars) { + START: + if (terminated_asynchronously (10)) + return unlucky (-1); + if (val (idx)) + continue; + if (lucky_propagate_discrepency (idx)) { + if (unsat) + return 20; + else + return unlucky (0); + } else + goto START; + } + VERBOSE (1, "forward assuming variables true satisfies formula"); + assert (satisfied ()); + stats.lucky.forward.one++; + return 10; +} + +/*------------------------------------------------------------------------*/ + +int Internal::backward_false_satisfiable () { + LOG ("checking decreasing variable index false assignment"); + assert (!unsat); + assert (!level); + assert (assumptions.empty ()); + for (int idx = max_var; idx > 0; idx--) { + START: + if (terminated_asynchronously (10)) + return unlucky (-1); + if (val (idx)) + continue; + if (lucky_propagate_discrepency (-idx)) { + if (unsat) + return 20; + else + return unlucky (0); + } else + goto START; + } + VERBOSE (1, "backward assuming variables false satisfies formula"); + assert (satisfied ()); + stats.lucky.backward.zero++; + return 10; +} + +int Internal::backward_true_satisfiable () { + LOG ("checking decreasing variable index true assignment"); + assert (!unsat); + assert (!level); + assert (assumptions.empty ()); + for (int idx = max_var; idx > 0; idx--) { + START: + if (terminated_asynchronously (10)) + return unlucky (-1); + if (val (idx)) + continue; + if (lucky_propagate_discrepency (idx)) { + if (unsat) + return 20; + else + return unlucky (0); + } else + goto START; + } + VERBOSE (1, "backward assuming variables true satisfies formula"); + assert (satisfied ()); + stats.lucky.backward.one++; + return 10; +} + +/*------------------------------------------------------------------------*/ + +// The following two functions test if the formula is a satisfiable horn +// formula. Actually the test is slightly more general. It goes over all +// clauses and assigns the first positive literal to true and propagates. +// Already satisfied clauses are of course skipped. A reverse function +// is not implemented yet. + +int Internal::positive_horn_satisfiable () { + LOG ("checking that all clauses are positive horn satisfiable"); + assert (!level); + assert (assumptions.empty ()); + for (const auto &c : clauses) { + if (terminated_asynchronously (10)) + return unlucky (-1); + if (c->garbage) + continue; + if (c->redundant) + continue; + int positive_literal = 0; + bool satisfied = false; + for (const auto &lit : *c) { + const signed char tmp = val (lit); + if (tmp > 0) { + satisfied = true; + break; + } + if (tmp < 0) + continue; + if (lit < 0) + continue; + positive_literal = lit; + break; + } + if (satisfied) + continue; + if (!positive_literal) { + LOG (c, "no positive unassigned literal in"); + return unlucky (0); + } + assert (positive_literal > 0); + LOG (c, "found positive literal %d in", positive_literal); + search_assume_decision (positive_literal); + if (propagate ()) + continue; + LOG ("propagation of positive literal %d leads to conflict", + positive_literal); + return unlucky (0); + } + for (auto idx : vars) { + if (terminated_asynchronously (10)) + return unlucky (-1); + if (val (idx)) + continue; + search_assume_decision (-idx); + if (propagate ()) + continue; + LOG ("propagation of remaining literal %d leads to conflict", -idx); + return unlucky (0); + } + VERBOSE (1, "clauses are positive horn satisfied"); + assert (!conflict); + assert (satisfied ()); + stats.lucky.horn.positive++; + return 10; +} + +int Internal::negative_horn_satisfiable () { + LOG ("checking that all clauses are negative horn satisfiable"); + assert (!level); + assert (assumptions.empty ()); + for (const auto &c : clauses) { + if (terminated_asynchronously (10)) + return unlucky (-1); + if (c->garbage) + continue; + if (c->redundant) + continue; + int negative_literal = 0; + bool satisfied = false; + for (const auto &lit : *c) { + const signed char tmp = val (lit); + if (tmp > 0) { + satisfied = true; + break; + } + if (tmp < 0) + continue; + if (lit > 0) + continue; + negative_literal = lit; + break; + } + if (satisfied) + continue; + if (!negative_literal) { + if (level > 0) + backtrack (); + LOG (c, "no negative unassigned literal in"); + return unlucky (0); + } + assert (negative_literal < 0); + LOG (c, "found negative literal %d in", negative_literal); + search_assume_decision (negative_literal); + if (propagate ()) + continue; + LOG ("propagation of negative literal %d leads to conflict", + negative_literal); + return unlucky (0); + } + for (auto idx : vars) { + if (terminated_asynchronously (10)) + return unlucky (-1); + if (val (idx)) + continue; + search_assume_decision (idx); + if (propagate ()) + continue; + LOG ("propagation of remaining literal %d leads to conflict", idx); + return unlucky (0); + } + VERBOSE (1, "clauses are negative horn satisfied"); + assert (!conflict); + assert (satisfied ()); + stats.lucky.horn.negative++; + return 10; +} + +/*------------------------------------------------------------------------*/ + +int Internal::lucky_phases () { + assert (!level); + require_mode (SEARCH); + if (!opts.lucky) + return 0; + + // TODO: Some of the lucky assignments can also be found if there are + // assumptions, but this is not completely implemented nor tested yet. + // Nothing done for constraint either. + // External propagator assumes a CDCL loop, so lucky is not tried here. + if (!assumptions.empty () || !constraint.empty () || external_prop) + return 0; + + START (search); + START (lucky); + assert (!searching_lucky_phases); + searching_lucky_phases = true; + stats.lucky.tried++; + const int64_t active_before = stats.active; + int res = trivially_false_satisfiable (); + if (!res) + res = trivially_true_satisfiable (); + if (!res) + res = forward_true_satisfiable (); + if (!res) + res = forward_false_satisfiable (); + if (!res) + res = backward_false_satisfiable (); + if (!res) + res = backward_true_satisfiable (); + if (!res) + res = positive_horn_satisfiable (); + if (!res) + res = negative_horn_satisfiable (); + if (res < 0) + assert (termination_forced), res = 0; + if (res == 10) + stats.lucky.succeeded++; + report ('l', !res); + assert (searching_lucky_phases); + + const int64_t units = active_before - stats.active; + + if (!res && units) + LOG ("lucky %zd units", units); + searching_lucky_phases = false; + STOP (lucky); + STOP (search); + + return res; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/message.cpp b/src/sat/cadical/message.cpp new file mode 100644 index 000000000..9f9a212ab --- /dev/null +++ b/src/sat/cadical/message.cpp @@ -0,0 +1,212 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ +#ifndef QUIET +/*------------------------------------------------------------------------*/ + +void Internal::print_prefix () { fputs (prefix.c_str (), stdout); } + +void Internal::vmessage (const char *fmt, va_list &ap) { +#ifdef LOGGING + if (!opts.log) +#endif + if (opts.quiet) + return; + print_prefix (); + vprintf (fmt, ap); + fputc ('\n', stdout); + fflush (stdout); +} + +void Internal::message (const char *fmt, ...) { + va_list ap; + va_start (ap, fmt); + vmessage (fmt, ap); + va_end (ap); +} + +void Internal::message () { +#ifdef LOGGING + if (!opts.log) +#endif + if (opts.quiet) + return; + print_prefix (); + fputc ('\n', stdout); + fflush (stdout); +} + +/*------------------------------------------------------------------------*/ + +void Internal::vverbose (int level, const char *fmt, va_list &ap) { +#ifdef LOGGING + if (!opts.log) +#endif + if (opts.quiet || level > opts.verbose) + return; + print_prefix (); + vprintf (fmt, ap); + fputc ('\n', stdout); + fflush (stdout); +} + +void Internal::verbose (int level, const char *fmt, ...) { + va_list ap; + va_start (ap, fmt); + vverbose (level, fmt, ap); + va_end (ap); +} + +void Internal::verbose (int level) { +#ifdef LOGGING + if (!opts.log) +#endif + if (opts.quiet || level > opts.verbose) + return; + print_prefix (); + fputc ('\n', stdout); + fflush (stdout); +} + +/*------------------------------------------------------------------------*/ + +void Internal::section (const char *title) { +#ifdef LOGGING + if (!opts.log) +#endif + if (opts.quiet) + return; + if (stats.sections++) + MSG (); + print_prefix (); + tout.blue (); + fputs ("--- [ ", stdout); + tout.blue (true); + fputs (title, stdout); + tout.blue (); + fputs (" ] ", stdout); + for (int i = strlen (title) + strlen (prefix.c_str ()) + 9; i < 78; i++) + fputc ('-', stdout); + tout.normal (); + fputc ('\n', stdout); + MSG (); +} + +/*------------------------------------------------------------------------*/ + +void Internal::phase (const char *phase, const char *fmt, ...) { +#ifdef LOGGING + if (!opts.log) +#endif + if (opts.quiet || (!force_phase_messages && opts.verbose < 2)) + return; + print_prefix (); + printf ("[%s] ", phase); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + fputc ('\n', stdout); + fflush (stdout); +} + +void Internal::phase (const char *phase, int64_t count, const char *fmt, + ...) { +#ifdef LOGGING + if (!opts.log) +#endif + if (opts.quiet || (!force_phase_messages && opts.verbose < 2)) + return; + print_prefix (); + printf ("[%s-%" PRId64 "] ", phase, count); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + fputc ('\n', stdout); + fflush (stdout); +} + +/*------------------------------------------------------------------------*/ +#endif // ifndef QUIET +/*------------------------------------------------------------------------*/ + +void Internal::warning (const char *fmt, ...) { + fflush (stdout); + terr.bold (); + fputs ("cadical: ", stderr); + terr.red (1); + fputs ("warning:", stderr); + terr.normal (); + fputc (' ', stderr); + va_list ap; + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + fputc ('\n', stderr); + fflush (stderr); +} + +/*------------------------------------------------------------------------*/ + +void Internal::error_message_start () { + fflush (stdout); + terr.bold (); + fputs ("cadical: ", stderr); + terr.red (1); + fputs ("error:", stderr); + terr.normal (); + fputc (' ', stderr); +} + +void Internal::error_message_end () { + fputc ('\n', stderr); + fflush (stderr); + // TODO add possibility to use call back instead. + exit (1); +} + +void Internal::verror (const char *fmt, va_list &ap) { + error_message_start (); + vfprintf (stderr, fmt, ap); + error_message_end (); +} + +void Internal::error (const char *fmt, ...) { + va_list ap; + va_start (ap, fmt); + verror (fmt, ap); + va_end (ap); // unreachable +} + +/*------------------------------------------------------------------------*/ + +void fatal_message_start () { + fflush (stdout); + terr.bold (); + fputs ("cadical: ", stderr); + terr.red (1); + fputs ("fatal error:", stderr); + terr.normal (); + fputc (' ', stderr); +} + +void fatal_message_end () { + fputc ('\n', stderr); + fflush (stderr); + abort (); +} + +void fatal (const char *fmt, ...) { + fatal_message_start (); + va_list ap; + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + fatal_message_end (); + abort (); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/message.hpp b/src/sat/cadical/message.hpp new file mode 100644 index 000000000..ca2c00587 --- /dev/null +++ b/src/sat/cadical/message.hpp @@ -0,0 +1,65 @@ +#ifndef _message_h_INCLUDED +#define _message_h_INCLUDED + +/*------------------------------------------------------------------------*/ + +// Macros for compact message code. + +#ifndef QUIET + +#define LINE() \ + do { \ + if (internal) \ + internal->message (); \ + } while (0) + +#define MSG(...) \ + do { \ + if (internal) \ + internal->message (__VA_ARGS__); \ + } while (0) + +#define PHASE(...) \ + do { \ + if (internal) \ + internal->phase (__VA_ARGS__); \ + } while (0) + +#define SECTION(...) \ + do { \ + if (internal) \ + internal->section (__VA_ARGS__); \ + } while (0) + +#define VERBOSE(...) \ + do { \ + if (internal) \ + internal->verbose (__VA_ARGS__); \ + } while (0) + +#else + +#define LINE() \ + do { \ + } while (0) +#define MSG(...) \ + do { \ + } while (0) +#define PHASE(...) \ + do { \ + } while (0) +#define SECTION(...) \ + do { \ + } while (0) +#define VERBOSE(...) \ + do { \ + } while (0) + +#endif + +#define FATAL fatal +#define WARNING(...) internal->warning (__VA_ARGS__) + +/*------------------------------------------------------------------------*/ + +#endif // ifndef _message_h_INCLUDED diff --git a/src/sat/cadical/minimize.cpp b/src/sat/cadical/minimize.cpp new file mode 100644 index 000000000..ff9b89df4 --- /dev/null +++ b/src/sat/cadical/minimize.cpp @@ -0,0 +1,224 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +// Functions for learned clause minimization. We only have the recursive +// version, which actually really is implemented recursively. We also +// played with a derecursified version, which however was more complex and +// slower. The trick to keep potential stack exhausting recursion under +// guards is to explicitly limit the recursion depth. + +// Instead of signatures as in the original implementation in MiniSAT and +// our corresponding paper, we use the 'poison' idea of Allen Van Gelder to +// mark unsuccessful removal attempts, then Donald Knuth's idea to abort +// minimization if only one literal was seen on the level and a new idea of +// also aborting if the earliest seen literal was assigned afterwards. + +bool Internal::minimize_literal (int lit, int depth) { + LOG ("attempt to minimize lit %d at depth %d", lit, depth); + assert (val (lit) > 0); + Flags &f = flags (lit); + Var &v = var (lit); + if (!v.level || f.removable || f.keep) + return true; + if (!v.reason || f.poison || v.level == level) + return false; + const Level &l = control[v.level]; + if (!depth && l.seen.count < 2) + return false; // Don Knuth's idea + if (v.trail <= l.seen.trail) + return false; // new early abort + if (depth > opts.minimizedepth) + return false; + bool res = true; + assert (v.reason); + if (opts.minimizeticks) + stats.ticks.search[stable]++; + if (v.reason == external_reason) { + assert (!opts.exteagerreasons); + v.reason = learn_external_reason_clause (lit, 0, true); + if (!v.reason) { + assert (!v.level); + return true; + } + } + assert (v.reason != external_reason); + const const_literal_iterator end = v.reason->end (); + const_literal_iterator i; + for (i = v.reason->begin (); res && i != end; i++) { + const int other = *i; + if (other == lit) + continue; + res = minimize_literal (-other, depth + 1); + } + if (res) + f.removable = true; + else + f.poison = true; + minimized.push_back (lit); + if (!depth) { + LOG ("minimizing %d %s", lit, res ? "succeeded" : "failed"); + } + return res; +} + +// Sorting the clause before minimization with respect to the trail order +// (literals with smaller trail height first) is necessary but natural and +// might help to minimize the required recursion depth too. + +struct minimize_trail_positive_rank { + Internal *internal; + minimize_trail_positive_rank (Internal *s) : internal (s) {} + typedef unsigned Type; + Type operator() (const int &a) const { + assert (internal->val (a)); + return (unsigned) internal->var (a).trail; + } +}; + +struct minimize_trail_smaller { + Internal *internal; + minimize_trail_smaller (Internal *s) : internal (s) {} + bool operator() (const int &a, const int &b) const { + return internal->var (a).trail < internal->var (b).trail; + } +}; + +struct minimize_trail_level_positive_rank { + Internal *internal; + minimize_trail_level_positive_rank (Internal *s) : internal (s) {} + typedef uint64_t Type; + Type operator() (const int &a) const { + assert (internal->val (a)); + Var &v = internal->var (a); + uint64_t res = v.level; + res <<= 32; + res |= v.trail; + return res; + } +}; + +struct minimize_trail_level_smaller { + Internal *internal; + minimize_trail_level_smaller (Internal *s) : internal (s) {} + bool operator() (const int &a, const int &b) const { + return minimize_trail_level_positive_rank (internal) (a) < + minimize_trail_level_positive_rank (internal) (b); + } +}; + +void Internal::minimize_clause () { + START (minimize); + LOG (clause, "minimizing first UIP clause"); + + external->check_learned_clause (); // check 1st UIP learned clause first + minimize_sort_clause (); + + assert (minimized.empty ()); + assert (minimize_chain.empty ()); + const auto end = clause.end (); + auto j = clause.begin (), i = j; + std::vector<int> stack; + for (; i != end; i++) { + if (minimize_literal (-*i)) { + if (lrat) { + assert (mini_chain.empty ()); + calculate_minimize_chain (-*i, stack); + for (auto p : mini_chain) { + minimize_chain.push_back (p); + } + mini_chain.clear (); + } + stats.minimized++; + } else + flags (*j++ = *i).keep = true; + } + LOG ("minimized %zd literals", (size_t) (clause.end () - j)); + if (j != end) + clause.resize (j - clause.begin ()); + clear_minimized_literals (); + for (auto p = minimize_chain.rbegin (); p != minimize_chain.rend (); + p++) { + lrat_chain.push_back (*p); + } + minimize_chain.clear (); + STOP (minimize); +} + +// go backwards in reason graph and add ids +// mini_chain is in correct order so we have to add it to minimize_chain +// and then reverse when we put it on lrat_chain +// +// We have to use the non-recursive as we cannot limit the depth like the +// minimize version. Unlike the minimize version, we have to keep literals +// on the stack in order to push its reason later. +void Internal::calculate_minimize_chain (int lit, std::vector<int> &stack) { + assert (stack.empty ()); + stack.push_back (vidx (lit)); + + while (!stack.empty ()) { + const int idx = stack.back (); + assert (idx); + stack.pop_back (); + if (idx < 0) { + Var &v = var (idx); + mini_chain.push_back (v.reason->id); + continue; + } + assert (idx); + Flags &f = flags (idx); + Var &v = var (idx); + if (f.keep || f.added || f.poison) { + continue; + } + if (!v.level) { + if (f.seen) + continue; + f.seen = true; + unit_analyzed.push_back (idx); + const int lit = val (idx) > 0 ? idx : -idx; + int64_t id = unit_id (lit); + unit_chain.push_back (id); + continue; + } + f.added = true; + assert (v.reason && f.removable); + const const_literal_iterator end = v.reason->end (); + const_literal_iterator i; + LOG (v.reason, "LRAT chain for lit %d at depth %zd by going over", lit, + stack.size ()); + stack.push_back (-idx); + for (i = v.reason->begin (); i != end; i++) { + const int other = *i; + if (other == idx) + continue; + stack.push_back (vidx (other)); + } + } + assert (stack.empty ()); +} + +// Sort the literals in reverse assignment order (thus trail order) to +// establish the base case of the recursive minimization algorithm in the +// positive case (where a literal with 'keep' true is hit). +// +void Internal::minimize_sort_clause () { + MSORT (opts.radixsortlim, clause.begin (), clause.end (), + minimize_trail_positive_rank (this), + minimize_trail_smaller (this)); +} + +void Internal::clear_minimized_literals () { + LOG ("clearing %zd minimized literals", minimized.size ()); + for (const auto &lit : minimized) { + Flags &f = flags (lit); + f.poison = f.removable = f.shrinkable = f.added = false; + } + for (const auto &lit : clause) + assert (!flags (lit).shrinkable), flags (lit).keep = + flags (lit).shrinkable = + flags (lit).added = false; + minimized.clear (); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/occs.cpp b/src/sat/cadical/occs.cpp new file mode 100644 index 000000000..c1d987416 --- /dev/null +++ b/src/sat/cadical/occs.cpp @@ -0,0 +1,52 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Occurrence lists. + +void Internal::init_occs () { + if (otab.size () < 2 * vsize) + otab.resize (2 * vsize, Occs ()); + LOG ("initialized occurrence lists"); +} + +void Internal::reset_occs () { + assert (occurring ()); + erase_vector (otab); + LOG ("reset occurrence lists"); +} + +void Internal::clear_occs () { + assert (occurring ()); + for (auto &occ : otab) + occ.clear (); + LOG ("clear occurrence lists"); +} + +/*------------------------------------------------------------------------*/ + +// One-sided occurrence counter (each literal has its own counter). + +void Internal::init_noccs () { + assert (ntab.empty ()); + if (ntab.size () < 2 * vsize) + ntab.resize (2 * vsize, 0); + LOG ("initialized two-sided occurrence counters"); +} + +void Internal::clear_noccs () { + assert (!ntab.empty ()); + for (auto &nt : ntab) + nt = 0; + LOG ("clear two-sided occurrence counters"); +} + +void Internal::reset_noccs () { + assert (!max_var || !ntab.empty ()); + erase_vector (ntab); + LOG ("reset two-sided occurrence counters"); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/occs.hpp b/src/sat/cadical/occs.hpp new file mode 100644 index 000000000..6988725d6 --- /dev/null +++ b/src/sat/cadical/occs.hpp @@ -0,0 +1,36 @@ +#ifndef _occs_h_INCLUDED +#define _occs_h_INCLUDED + +#include <vector> + +namespace CaDiCaL { + +// Full occurrence lists used in a one-watch scheme for all clauses in +// subsumption checking and for irredundant clauses in variable elimination. + +struct Clause; +using namespace std; + +typedef vector<Clause *> Occs; + +inline void shrink_occs (Occs &os) { shrink_vector (os); } +inline void erase_occs (Occs &os) { erase_vector (os); } + +inline void remove_occs (Occs &os, Clause *c) { + const auto end = os.end (); + auto i = os.begin (); + for (auto j = i; j != end; j++) { + const Clause *d = *i++ = *j; + if (c == d) + i--; + } + assert (i + 1 == end); + os.resize (i - os.begin ()); +} + +typedef Occs::iterator occs_iterator; +typedef Occs::const_iterator const_occs_iterator; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/options.cpp b/src/sat/cadical/options.cpp new file mode 100644 index 000000000..26dcc3d54 --- /dev/null +++ b/src/sat/cadical/options.cpp @@ -0,0 +1,359 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// By default, e.g., for library usage, the 'opts.report' value is zero +// ('false') but can be set to '1' by the stand alone solver. Using here +// a static default value avoids that the stand alone solver reports that +// '--report=1' is different from the default in 'print ()' below. +// +int Options::reportdefault; + +/*------------------------------------------------------------------------*/ + +// The order of initializations of static objects is undefined and thus we +// can not assume that this table is already initialized if a solver and +// thus the constructor of 'Options' is called. Therefore we just have to +// reinitialize this table in every call to 'Options::Options'. This does +// not produce a data race even for parallel initialization since the +// same values are written by all threads under the assumption that the +// 'reportdefault' is set before any solver is initialized. We do have to +// perform this static initialization though, since 'has' is static and does +// not require that the 'Options' constructor was called. + +Option Options::table[] = { +#define OPTION(N, V, L, H, O, P, R, D) \ + {#N, (int) V, (int) L, (int) H, (int) O, (bool) P, D}, + OPTIONS +#undef OPTION +}; + +/*------------------------------------------------------------------------*/ + +// Binary search in 'table', which requires option names to be sorted, which +// in turned is checked at start-up in 'Options::Options'. + +Option *Options::has (const char *name) { + size_t l = 0, r = number_of_options; + while (l < r) { + size_t m = l + (r - l) / 2; + Option *res = &table[m]; + int tmp = strcmp (name, res->name); + if (!tmp) + return res; + if (tmp < 0) + r = m; + if (tmp > 0) + l = m + 1; + } + return 0; +} + +/*------------------------------------------------------------------------*/ + +bool Options::parse_long_option (const char *arg, string &name, int &val) { + if (arg[0] != '-' || arg[1] != '-') + return false; + const bool has_no_prefix = + (arg[2] == 'n' && arg[3] == 'o' && arg[4] == '-'); + const size_t offset = has_no_prefix ? 5 : 2; + name = arg + offset; + const size_t pos = name.find_first_of ('='); + if (pos != string::npos) + name[pos] = 0; + if (!Options::has (name.c_str ())) + return false; + if (pos == string::npos) + val = !has_no_prefix; + else { + const char *val_str = name.c_str () + pos + 1; + if (!parse_int_str (val_str, val)) + return false; + } + return true; +} + +/*------------------------------------------------------------------------*/ + +void Options::initialize_from_environment (int &val, const char *name, + const int L, const int H) { + char key[80], *q; + const char *p; + assert (strlen (name) + strlen ("CADICAL_") + 1 < sizeof (key)); + for (p = "CADICAL_", q = key; *p; p++) + *q++ = *p; + for (p = name; *p; p++) + *q++ = toupper (*p); + assert (q < key + sizeof (key)); + *q = 0; + const char *val_str = getenv (key); + if (!val_str) + return; + if (!parse_int_str (val_str, val)) + return; + if (val < L) + val = L; + if (val > H) + val = H; +} + +// Initialize all the options to their default value 'V'. + +Options::Options (Internal *s) : internal (s) { + assert (number_of_options == sizeof Options::table / sizeof (Option)); + + // First initialize them according to defaults in 'options.hpp'. + // + const char *prev = ""; + size_t i = 0; +#define OPTION(N, V, L, H, O, P, R, D) \ + do { \ + if ((L) > (V)) \ + FATAL ("'" #N "' default '" #V "' " \ + "lower minimum '" #L "' in 'options.hpp'"); \ + if ((H) < (V)) \ + FATAL ("'" #N "' default '" #V "' " \ + "larger maximum '" #H "' in 'options.hpp'"); \ + if (strcmp (prev, #N) > 0) \ + FATAL ("'%s' ordered before '" #N "' in 'options.hpp'", prev); \ + N = (int) (V); \ + assert (&val (i) == &N); \ + /* The order of initializing static data is undefined and thus */ \ + /* it might be the case that the 'table' is not initialized yet. */ \ + /* Thus this construction just reinitializes the table too even */ \ + /* though it might not be necessary. */ \ + assert (!table[i].name || !strcmp (table[i].name, #N)); \ + table[i] = {#N, (int) (V), (int) (L), (int) (H), \ + (int) (O), (bool) (P), D}; \ + prev = #N; \ + i++; \ + } while (0); + OPTIONS +#undef OPTION + + // Check consistency in debugging mode. + // +#ifndef NDEBUG + assert (i == number_of_options); + assert (!has ("aaaaa")); + assert (!has ("non-existing-option")); + assert (!has ("zzzzz")); +#endif + + // Now overwrite default options with environment values. + // +#define OPTION(N, V, L, H, O, P, R, D) \ + initialize_from_environment (N, #N, L, H); + OPTIONS +#undef OPTION +} + +/*------------------------------------------------------------------------*/ + +void Options::set (Option *o, int new_val) { + assert (o); + int &val = o->val (this), old_val = val; + if (old_val == new_val) { + LOG ("keeping value '%d' of option '%s'", old_val, o->name); + return; + } + if (new_val < o->lo) { + LOG ("bounding '%d' to lower limit '%d' for option '%s'", new_val, + o->lo, o->name); + new_val = o->lo; + } + if (new_val > o->hi) { + LOG ("bounding '%d' to upper limit '%d' for option '%s'", new_val, + o->hi, o->name); + new_val = o->hi; + } + val = new_val; + LOG ("set option 'set (\"%s\", %d)' from '%d'", o->name, new_val, + old_val); +} + +// Explicit option value setting. + +bool Options::set (const char *name, int val) { + Option *o = has (name); + if (!o) + return false; + set (o, val); + return true; +} + +int Options::get (const char *name) { + Option *o = has (name); + return o ? o->val (this) : 0; +} + +/*------------------------------------------------------------------------*/ + +void Options::print () { + unsigned different = 0; +#ifdef QUIET + const bool verbose = false; +#endif + char buffer[256]; + // We prefer the macro iteration here since '[VLH]' might be '1e9' etc. +#define OPTION(N, V, L, H, O, P, R, D) \ + if (N != (V)) \ + different++; \ + if (verbose || N != (V)) { \ + if ((L) == 0 && (H) == 1) { \ + snprintf (buffer, sizeof buffer, "--" #N "=%s", \ + (N ? "true" : "false")); \ + MSG (" %s%-30s%s (%s default %s'%s'%s)", \ + ((N == (V)) ? "" : tout.bright_yellow_code ()), buffer, \ + ((N == (V)) ? "" : tout.normal_code ()), \ + ((N == (V)) ? "same as" : "different from"), \ + ((N == (V)) ? tout.green_code () : tout.yellow_code ()), \ + (bool) (V) ? "true" : "false", tout.normal_code ()); \ + } else { \ + snprintf (buffer, sizeof buffer, "--" #N "=%d", N); \ + MSG (" %s%-30s%s (%s default %s'" #V "'%s)", \ + ((N == (V)) ? "" : tout.bright_yellow_code ()), buffer, \ + ((N == (V)) ? "" : tout.normal_code ()), \ + ((N == (V)) ? "same as" : "different from"), \ + ((N == (V)) ? tout.green_code () : tout.yellow_code ()), \ + tout.normal_code ()); \ + } \ + } + OPTIONS +#undef OPTION + if (!different) + MSG ("all options are set to their default value"); +} + +/*------------------------------------------------------------------------*/ + +void Options::usage () { + // We prefer the macro iteration here since '[VLH]' might be '1e9' etc. +#define OPTION(N, V, L, H, O, P, R, D) \ + if ((L) == 0 && (H) == 1) \ + printf (" %-26s " D " [%s]\n", "--" #N "=bool", \ + (bool) (V) ? "true" : "false"); \ + else \ + printf (" %-26s " D " [" #V "]\n", "--" #N "=" #L ".." #H); + OPTIONS +#undef OPTION +} + +/*------------------------------------------------------------------------*/ + +void Options::optimize (int val) { + + if (val < 0) { + LOG ("ignoring negative optimization mode '%d'", val); + return; + } + + const int max_val = 31; + if (val > max_val) { + LOG ("optimization argument '%d' reduced to '%d'", val, max_val); + val = max_val; + } + + int64_t factor2 = 1; + for (int i = 0; i < val && factor2 <= INT_MAX; i++) + factor2 *= 2; + + int64_t factor10 = 1; + for (int i = 0; i < val && factor10 <= INT_MAX; i++) + factor10 *= 10; + + unsigned increased = 0; +#define OPTION(N, V, L, H, O, P, R, D) \ + do { \ + if (!(O)) \ + break; \ + const int64_t factor1 = ((O) == 1 ? factor2 : factor10); \ + int64_t new_val = factor1 * (int64_t) (V); \ + if (new_val > (H)) \ + new_val = (H); \ + if (new_val == (int) (V)) \ + break; \ + LOG ("optimization mode '%d' for '%s' " \ + "gives '%" PRId64 "' instead of '%d", \ + val, #N, new_val, (int) (V)); \ + assert (new_val <= INT_MAX); \ + N = (int) new_val; \ + increased++; \ + } while (0); + OPTIONS +#undef OPTION + if (increased) + MSG ("optimization mode '-O%d' increased %u limits", val, increased); +} + +/*------------------------------------------------------------------------*/ + +void Options::disable_preprocessing () { + size_t count = 0; +#define OPTION(N, V, L, H, O, P, R, D) \ + do { \ + if (!(P)) \ + break; \ + if (!(N)) \ + break; \ + LOG ("plain mode disables '%s'", #N); \ + assert ((L) == 0); \ + assert ((H) == 1); \ + count++; \ + N = 0; \ + } while (0); + OPTIONS +#undef OPTION + LOG ("forced plain mode disabled %zd preprocessing options", count); +#ifndef LOGGING + (void) count; +#endif +} + +bool Options::is_preprocessing_option (const char *name) { + Option *o = has (name); + return o ? o->preprocessing : false; +} + +/*------------------------------------------------------------------------*/ + +void Options::reset_default_values () { + size_t count = 0; +#define OPTION(N, V, L, H, O, P, R, D) \ + do { \ + if (!(R)) \ + break; \ + if (N == (V)) \ + break; \ + LOG ("resetting option '%s' to default %s", #N, #V); \ + count++; \ + N = (int) (V); \ + } while (0); + OPTIONS +#undef OPTION + LOG ("reset %zd options to their default values", count); +#ifndef LOGGING + (void) count; +#endif +} + +/*------------------------------------------------------------------------*/ + +void Options::copy (Options &other) const { +#ifdef LOGGING + Internal *internal = other.internal; +#endif +#define OPTION(N, V, L, H, O, P, R, D) \ + if ((N) == (int) (V)) \ + LOG ("keeping non default option '--%s=%s'", #N, #V); \ + else if ((N) != (int) (V)) { \ + LOG ("overwriting default option by '--%s=%d'", #N, N); \ + other.N = N; \ + } + OPTIONS +#undef OPTION +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/options.hpp b/src/sat/cadical/options.hpp new file mode 100644 index 000000000..79005a952 --- /dev/null +++ b/src/sat/cadical/options.hpp @@ -0,0 +1,416 @@ +#ifndef _options_hpp_INCLUDED +#define _options_hpp_INCLUDED + +/*------------------------------------------------------------------------*/ + +// In order to add a new option, simply add a new line below. Make sure that +// options are sorted correctly (with '!}sort -k 2' in 'vi'). Otherwise +// initializing the options will trigger an internal error. For the model +// based tester 'mobical' the policy is that options which become redundant +// because another one is disabled (set to zero) should have the name of the +// latter as prefix. The 'O' column determines the options which are +// target to 'optimize' them ('-O[1-3]'). A zero value in the 'O' column +// means that this option is not optimized. A value of '1' results in +// optimizing its value exponentially with exponent base '2', and a value +// of '2' uses base '10'. The 'P' column determines simplification +// options (disabled with '--plain') and 'R' which values can be reset. + +// clang-format off + +#define OPTIONS \ +\ +/* NAME DEFAULT, LO, HI,O,P,R, USAGE */ \ +\ +OPTION( arena, 1, 0, 1,0,0,1, "allocate clauses in arena") \ +OPTION( arenacompact, 1, 0, 1,0,0,1, "keep clauses compact") \ +OPTION( arenasort, 1, 0, 1,0,0,1, "sort clauses in arena") \ +OPTION( arenatype, 3, 1, 3,0,0,1, "1=clause, 2=var, 3=queue") \ +OPTION( binary, 1, 0, 1,0,0,1, "use binary proof format") \ +OPTION( block, 0, 0, 1,0,1,1, "blocked clause elimination") \ +OPTION( blockmaxclslim, 1e5, 1,2e9,2,0,1, "maximum clause size") \ +OPTION( blockminclslim, 2, 2,2e9,0,0,1, "minimum clause size") \ +OPTION( blockocclim, 1e2, 1,2e9,2,0,1, "occurrence limit") \ +OPTION( bump, 1, 0, 1,0,0,1, "bump variables") \ +OPTION( bumpreason, 1, 0, 1,0,0,1, "bump reason literals too") \ +OPTION( bumpreasondepth, 1, 1, 3,0,0,1, "bump reason depth") \ +OPTION( bumpreasonlimit, 10, 1,2e9,0,0,1, "bump reason limit") \ +OPTION( bumpreasonrate, 100, 1,2e9,0,0,1, "bump reason decision rate") \ +OPTION( check, 0, 0, 1,0,0,0, "enable internal checking") \ +OPTION( checkassumptions, 1, 0, 1,0,0,0, "check assumptions satisfied") \ +OPTION( checkconstraint, 1, 0, 1,0,0,0, "check constraint satisfied") \ +OPTION( checkfailed, 1, 0, 1,0,0,0, "check failed literals form core") \ +OPTION( checkfrozen, 0, 0, 1,0,0,0, "check all frozen semantics") \ +OPTION( checkproof, 3, 0, 3,0,0,0, "1=drat, 2=lrat, 3=both") \ +OPTION( checkwitness, 1, 0, 1,0,0,0, "check witness internally") \ +OPTION( chrono, 1, 0, 2,0,0,1, "chronological backtracking") \ +OPTION( chronoalways, 0, 0, 1,0,0,1, "force always chronological") \ +OPTION( chronolevelim, 1e2, 0,2e9,0,0,1, "chronological level limit") \ +OPTION( chronoreusetrail, 1, 0, 1,0,0,1, "reuse trail chronologically") \ +OPTION( compact, 1, 0, 1,0,1,1, "compact internal variables") \ +OPTION( compactint, 2e3, 1,2e9,0,0,1, "compacting interval") \ +OPTION( compactlim, 1e2, 0,1e3,0,0,1, "inactive limit per mille") \ +OPTION( compactmin, 1e2, 1,2e9,0,0,1, "minimum inactive limit") \ +OPTION( condition, 0, 0, 1,0,1,1, "globally blocked clause elim") \ +OPTION( conditioneffort, 100, 1,1e5,0,0,1, "relative efficiency per mille") \ +OPTION( conditionint, 1e4, 1,2e9,0,0,1, "initial conflict interval") \ +OPTION( conditionmaxeff, 1e7, 0,2e9,1,0,1, "maximum condition efficiency") \ +OPTION( conditionmaxrat, 100, 1,2e9,1,0,1, "maximum clause variable ratio") \ +OPTION( conditionmineff, 0, 0,2e9,1,0,1, "minimum condition efficiency") \ +OPTION( congruence, 1, 0, 1,0,0,1, "congruence closure") \ +OPTION( congruenceand, 1, 0, 1,0,0,1, "extract AND gates") \ +OPTION( congruenceandarity,1e6,2,5e7,0,0,1, "AND gate arity limit") \ +OPTION( congruencebinaries,1, 0, 1,0,0,1, "extract binary and strengthen ternary clauses") \ +OPTION( congruenceite, 1, 0, 1,0,0,1, "extract ITE gates") \ +OPTION( congruencexor, 1, 0, 1,0,0,1, "extract XOR gates") \ +OPTION( congruencexorarity,4, 2, 31,0,0,1, "XOR gate arity limit") \ +OPTION( congruencexorcounts,1, 1,5e6,0,0,1, "XOR gate round") \ +OPTION( cover, 0, 0, 1,0,1,1, "covered clause elimination") \ +OPTION( covereffort, 4, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( covermaxclslim, 1e5, 1,2e9,2,0,1, "maximum clause size") \ +OPTION( covermaxeff, 1e8, 0,2e9,1,0,1, "maximum cover efficiency") \ +OPTION( coverminclslim, 2, 2,2e9,0,0,1, "minimum clause size") \ +OPTION( covermineff, 0, 0,2e9,1,0,1, "minimum cover efficiency") \ +OPTION( decompose, 1, 0, 1,0,1,1, "decompose BIG in SCCs and ELS") \ +OPTION( decomposerounds, 2, 1, 16,1,0,1, "number of decompose rounds") \ +OPTION( deduplicate, 1, 0, 1,0,1,1, "remove duplicated binaries") \ +OPTION( eagersubsume, 1, 0, 1,0,1,1, "subsume recently learned") \ +OPTION( eagersubsumelim, 20, 1,1e3,0,0,1, "limit on subsumed candidates") \ +OPTION( elim, 1, 0, 1,0,1,1, "bounded variable elimination") \ +OPTION( elimands, 1, 0, 1,0,0,1, "find AND gates") \ +OPTION( elimbackward, 1, 0, 1,0,0,1, "eager backward subsumption") \ +OPTION( elimboundmax, 16, -1,2e6,1,0,1, "maximum elimination bound") \ +OPTION( elimboundmin, 0, -1,2e6,0,0,1, "minimum elimination bound") \ +OPTION( elimclslim, 1e2, 2,2e9,2,0,1, "resolvent size limit") \ +OPTION( elimdef, 0, 0, 1,0,0,1, "mine definitions with kitten") \ +OPTION( elimdefcores, 1, 1,100,0,0,1, "number of unsat cores") \ +OPTION( elimdefticks, 2e5, 0,2e9,1,0,1, "kitten ticks limit") \ +OPTION( elimeffort, 1e3, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( elimequivs, 1, 0, 1,0,0,1, "find equivalence gates") \ +OPTION( elimint, 2e3, 1,2e9,0,0,1, "elimination interval") \ +OPTION( elimites, 1, 0, 1,0,0,1, "find if-then-else gates") \ +OPTION( elimlimited, 1, 0, 1,0,0,1, "limit resolutions") \ +OPTION( elimmaxeff, 2e9, 0,2e9,1,0,1, "maximum elimination efficiency") \ +OPTION( elimmineff, 1e7, 0,2e9,1,0,1, "minimum elimination efficiency") \ +OPTION( elimocclim, 1e2, 0,2e9,2,0,1, "occurrence limit") \ +OPTION( elimprod, 1, 0,1e4,0,0,1, "elim score product weight") \ +OPTION( elimrounds, 2, 1,512,1,0,1, "usual number of rounds") \ +OPTION( elimsubst, 1, 0, 1,0,0,1, "elimination by substitution") \ +OPTION( elimsum, 1, 0,1e4,0,0,1, "elimination score sum weight") \ +OPTION( elimxorlim, 5, 2, 27,1,0,1, "maximum XOR size") \ +OPTION( elimxors, 1, 0, 1,0,0,1, "find XOR gates") \ +OPTION( emadecisions, 1e5, 1,2e9,0,0,1, "window decision rate") \ +OPTION( emagluefast, 33, 1,2e9,0,0,1, "window fast glue") \ +OPTION( emaglueslow, 1e5, 1,2e9,0,0,1, "window slow glue") \ +OPTION( emajump, 1e5, 1,2e9,0,0,1, "window back-jump level") \ +OPTION( emalevel, 1e5, 1,2e9,0,0,1, "window back-track level") \ +OPTION( emasize, 1e5, 1,2e9,0,0,1, "window learned clause size") \ +OPTION( ematrailfast, 1e2, 1,2e9,0,0,1, "window fast trail") \ +OPTION( ematrailslow, 1e5, 1,2e9,0,0,1, "window slow trail") \ +OPTION( exteagerreasons, 1, 0, 1,0,0,1, "eagerly ask for all reasons (0: only when needed)") \ +OPTION( exteagerrecalc, 1, 0, 1,0,0,1, "after eagerly asking for reasons recalculate all levels (0: trust the external tool)") \ +OPTION( externallrat, 0, 0, 1,0,0,1, "external lrat") \ +OPTION( factor, 1, 0, 1,0,1,1, "bounded variable addition") \ +OPTION( factorcandrounds, 2, 0,2e9,0,0,1, "candidates reduction rounds") \ +OPTION( factoreffort, 50, 0,1e6,0,0,1, "relative effort per mille") \ +OPTION( factoriniticks, 300, 1,1e6,0,0,1, "initial effort in millions") \ +OPTION( factorsize, 5, 2,2e9,0,0,1, "clause size limit") \ +OPTION( factorthresh, 7, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ +OPTION( fastelim, 1, 0, 1,0,1,1, "fast BVE during preprocessing") \ +OPTION( fastelimbound, 8, 1,1e3,1,0,1, "fast BVE bound during preprocessing") \ +OPTION( fastelimclslim, 1e2, 2,2e9,2,0,1, "fast BVE resolvent size limit") \ +OPTION( fastelimocclim, 100, 1,2e9,2,0,1, "fast BVE occurence limit during preprocessing") \ +OPTION( fastelimrounds, 4, 1,512,1,0,1, "number of fastelim rounds") \ +OPTION( flush, 0, 0, 1,0,1,1, "flush redundant clauses") \ +OPTION( flushfactor, 3, 1,1e3,0,0,1, "interval increase") \ +OPTION( flushint, 1e5, 1,2e9,0,0,1, "initial limit") \ +OPTION( forcephase, 0, 0, 1,0,0,1, "always use initial phase") \ +OPTION( frat, 0, 0, 2,0,0,1, "1=frat(lrat), 2=frat(drat)") \ +OPTION( idrup, 0, 0, 1,0,0,1, "incremental proof format") \ +OPTION( ilb, 0, 0, 1,0,0,1, "ILB (incremental lazy backtrack)") \ +OPTION( ilbassumptions, 0, 0, 1,0,0,1, "trail reuse for assumptions (ILB-like)") \ +OPTION( inprobeint, 100, 1,2e9,0,0,1, "inprobing interval" ) \ +OPTION( inprobing, 1, 0, 1,0,1,1, "enable probe inprocessing") \ +OPTION( inprocessing, 1, 0, 1,0,1,1, "enable general inprocessing") \ +OPTION( instantiate, 0, 0, 1,0,1,1, "variable instantiation") \ +OPTION( instantiateclslim, 3, 2,2e9,0,0,1, "minimum clause size") \ +OPTION( instantiateocclim, 1, 1,2e9,2,0,1, "maximum occurrence limit") \ +OPTION( instantiateonce, 1, 0, 1,0,0,1, "instantiate each clause once") \ +OPTION( lidrup, 0, 0, 1,0,0,1, "linear incremental proof format") \ +LOGOPT( log, 0, 0, 1,0,0,0, "enable logging") \ +LOGOPT( logsort, 0, 0, 1,0,0,0, "sort logged clauses") \ +OPTION( lrat, 0, 0, 1,0,0,1, "use LRAT proof format") \ +OPTION( lucky, 1, 0, 1,0,0,1, "search for lucky phases") \ +OPTION( minimize, 1, 0, 1,0,0,1, "minimize learned clauses") \ +OPTION( minimizedepth, 1e3, 0,1e3,0,0,1, "minimization depth") \ +OPTION( minimizeticks, 1, 0, 1,0,0,1, "increment ticks in minimization") \ +OPTION( otfs, 1, 0, 1,0,0,1, "on-the-fly self subsumption") \ +OPTION( phase, 1, 0, 1,0,0,1, "initial phase") \ +OPTION( preprocessinit, 2e6, 0,2e9,2,0,1, "initial preprocessing base limit" ) \ +OPTION( preprocesslight, 1, 0, 1,0,1,1, "lightweight preprocessing" ) \ +OPTION( probe, 1, 0, 1,0,1,1, "failed literal probing" ) \ +OPTION( probeeffort, 8, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( probehbr, 1, 0, 1,0,0,1, "learn hyper binary clauses") \ +OPTION( probethresh, 0, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ +OPTION( profile, 2, 0, 4,0,0,0, "profiling level") \ +QUTOPT( quiet, 0, 0, 1,0,0,0, "disable all messages") \ +OPTION( radixsortlim, 32, 0,2e9,0,0,1, "radix sort limit") \ +OPTION( realtime, 0, 0, 1,0,0,0, "real instead of process time") \ +OPTION( recomputetier, 1, 0, 1,0,0,1, "recompute tiers") \ +OPTION( reduce, 1, 0, 1,0,0,1, "reduce useless clauses") \ +OPTION( reduceinit, 300, 1,1e6,0,0,1, "initial interval") \ +OPTION( reduceint, 25, 2,1e6,0,0,1, "reduce interval") \ +OPTION( reduceopt, 1, 0, 2,0,0,1, "0=prct,1=sqrt,2=max") \ +OPTION( reducetarget, 75, 10,1e2,0,0,1, "reduce fraction in percent") \ +OPTION( reducetier1glue, 2, 1,2e9,0,0,1, "glue of kept learned clauses") \ +OPTION( reducetier2glue, 6, 1,2e9,0,0,1, "glue of tier two clauses") \ +OPTION( reluctant, 1024, 0,2e9,0,0,1, "reluctant doubling period") \ +OPTION( reluctantmax,1048576, 0,2e9,0,0,1, "reluctant doubling period") \ +OPTION( rephase, 1, 0, 1,0,0,1, "enable resetting phase") \ +OPTION( rephaseint, 1e3, 1,2e9,0,0,1, "rephase interval") \ +OPTION( report,reportdefault, 0, 1,0,0,1, "enable reporting") \ +OPTION( reportall, 0, 0, 1,0,0,1, "report even if not successful") \ +OPTION( reportsolve, 0, 0, 1,0,0,1, "use solving not process time") \ +OPTION( restart, 1, 0, 1,0,0,1, "enable restarts") \ +OPTION( restartint, 2, 1,2e9,0,0,1, "restart interval") \ +OPTION( restartmargin, 10, 0,1e2,0,0,1, "slow fast margin in percent") \ +OPTION( restartreusetrail, 1, 0, 1,0,0,1, "enable trail reuse") \ +OPTION( restoreall, 0, 0, 2,0,0,1, "restore all clauses (2=really)") \ +OPTION( restoreflush, 0, 0, 1,0,0,1, "remove satisfied clauses") \ +OPTION( reverse, 0, 0, 1,0,0,1, "reverse variable ordering") \ +OPTION( score, 1, 0, 1,0,0,1, "use EVSIDS scores") \ +OPTION( scorefactor, 950,500,1e3,0,0,1, "score factor per mille") \ +OPTION( seed, 0, 0,2e9,0,0,1, "random seed") \ +OPTION( shrink, 3, 0, 3,0,0,1, "shrink conflict clause (1=only with binary, 2=minimize when pulling, 3=full)") \ +OPTION( shrinkreap, 1, 0, 1,0,0,1, "use a reap for shrinking") \ +OPTION( shuffle, 0, 0, 1,0,0,1, "shuffle variables") \ +OPTION( shufflequeue, 1, 0, 1,0,0,1, "shuffle variable queue") \ +OPTION( shufflerandom, 0, 0, 1,0,0,1, "not reverse but random") \ +OPTION( shufflescores, 1, 0, 1,0,0,1, "shuffle variable scores") \ +OPTION( stabilize, 1, 0, 1,0,0,1, "enable stabilizing phases") \ +OPTION( stabilizeinit, 1e3, 1,2e9,0,0,1, "stabilizing interval") \ +OPTION( stabilizeonly, 0, 0, 1,0,0,1, "only stabilizing phases") \ +OPTION( stats, 0, 0, 1,0,0,1, "print all statistics at the end of the run") \ +OPTION( subsume, 1, 0, 1,0,1,1, "enable clause subsumption") \ +OPTION( subsumebinlim, 1e4, 0,2e9,1,0,1, "watch list length limit") \ +OPTION( subsumeclslim, 1e2, 0,2e9,2,0,1, "clause length limit") \ +OPTION( subsumeeffort, 1e3, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( subsumelimited, 1, 0, 1,0,0,1, "limit subsumption checks") \ +OPTION( subsumemaxeff, 1e8, 0,2e9,1,0,1, "maximum subsuming efficiency") \ +OPTION( subsumemineff, 0, 0,2e9,1,0,1, "minimum subsuming efficiency") \ +OPTION( subsumeocclim, 1e2, 0,2e9,1,0,1, "watch list length limit") \ +OPTION( subsumestr, 1, 0, 1,0,0,1, "subsume strenghten") \ +OPTION( sweep, 1, 0, 1,0,1,1, "enable SAT sweeping") \ +OPTION( sweepclauses, 1024, 0,2e9,1,0,1, "environment clauses") \ +OPTION( sweepcomplete, 0, 0, 1,0,0,1, "run SAT sweeping to completion") \ +OPTION( sweepcountbinary, 1, 0, 1,0,0,1, "count binaries to environment") \ +OPTION( sweepdepth, 2, 0,2e9,1,0,1, "environment depth") \ +OPTION( sweepeffort, 1e2, 0,1e4,0,0,1, "relative effort in ticks per mille") \ +OPTION( sweepfliprounds, 1, 0,2e9,1,0,1, "flipping rounds") \ +OPTION( sweepmaxclauses, 3e5, 2,2e9,1,0,1, "maximum environment clauses") \ +OPTION( sweepmaxdepth, 3, 1,2e9,1,0,1, "maximum environment depth") \ +OPTION( sweepmaxvars, 8192, 2,2e9,1,0,1, "maximum environment variables") \ +OPTION( sweeprand, 0, 0, 1,0,0,1, "randomize sweeping environment") \ +OPTION( sweepthresh, 5, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ +OPTION( sweepvars, 256, 0,2e9,1,0,1, "environment variables") \ +OPTION( target, 1, 0, 2,0,0,1, "target phases (1=stable only)") \ +OPTION( terminateint, 10, 0,1e4,0,0,1, "termination check interval") \ +OPTION( ternary, 1, 0, 1,0,1,1, "hyper ternary resolution") \ +OPTION( ternaryeffort, 8, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( ternarymaxadd, 1e3, 0,1e4,1,0,1, "max clauses added in percent") \ +OPTION( ternaryocclim, 1e2, 1,2e9,2,0,1, "ternary occurrence limit") \ +OPTION( ternaryrounds, 2, 1, 16,1,0,1, "maximum ternary rounds") \ +OPTION( ternarythresh, 6, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ +OPTION( tier1limit, 50, 0,100,0,0,1, "limit of tier1 usage in percentage") \ +OPTION( tier2limit, 90, 0,100,0,0,1, "limit of tier2 usage in percentage") \ +OPTION( transred, 1, 0, 1,0,1,1, "transitive reduction of BIG") \ +OPTION( transredeffort, 1e2, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( transredmaxeff, 1e8, 0,2e9,1,0,1, "maximum efficiency") \ +OPTION( transredmineff, 0, 0,2e9,1,0,1, "minimum efficiency") \ +QUTOPT( verbose, 0, 0, 3,0,0,0, "more verbose messages") \ +OPTION( veripb, 0, 0, 4,0,0,1, "odd=checkdeletions, > 2=drat") \ +OPTION( vivify, 1, 0, 1,0,1,1, "vivification") \ +OPTION( vivifycalctier, 0, 0, 1,0,0,1, "recalculate tier limits") \ +OPTION( vivifydemote, 0, 0, 1,0,1,1, "demote irredundant or delete directly") \ +OPTION( vivifyeffort, 50, 0,1e5,1,0,1, "overall efficiency per mille") \ +OPTION( vivifyflush, 1, 0, 1,1,0,1, "flush subsumed before vivification rounds") \ +OPTION( vivifyinst, 1, 0, 1,0,0,1, "instantiate last literal when vivify") \ +OPTION( vivifyirred, 1, 0, 1,0,1,1, "vivification irred") \ +OPTION( vivifyirredeff, 3, 1,100,1,0,1, "irredundant efficiency per mille") \ +OPTION( vivifyonce, 0, 0, 2,0,0,1, "vivify once: 1=red, 2=red+irr") \ +OPTION( vivifyretry, 0, 0, 5,0,0,1, "re-vivify clause if vivify was successful") \ +OPTION( vivifyschedmax, 5e3, 10,2e9,0,0,1, "maximum schedule size") \ +OPTION( vivifythresh, 20, 0,100,1,0,1, "delay if ticks smaller thresh*clauses") \ +OPTION( vivifytier1, 1, 0, 1,0,1,1, "vivification tier1") \ +OPTION( vivifytier1eff, 4, 0,100,1,0,1, "relative tier1 effort") \ +OPTION( vivifytier2, 1, 0, 1,0,1,1, "vivification tier2") \ +OPTION( vivifytier2eff, 2, 1,100,1,0,1, "relative tier2 effort") \ +OPTION( vivifytier3, 1, 0, 1,0,1,1, "vivification tier3") \ +OPTION( vivifytier3eff, 1, 1,100,1,0,1, "relative tier3 effort") \ +OPTION( walk, 1, 0, 1,0,0,1, "enable random walks") \ +OPTION( walkeffort, 20, 1,1e5,1,0,1, "relative efficiency per mille") \ +OPTION( walkmaxeff, 1e7, 0,2e9,1,0,1, "maximum efficiency") \ +OPTION( walkmineff, 0, 0,1e7,1,0,1, "minimum efficiency") \ +OPTION( walknonstable, 1, 0, 1,0,0,1, "walk in non-stabilizing phase") \ +OPTION( walkredundant, 0, 0, 1,0,0,1, "walk redundant clauses too") \ + +// Note, keep an empty line right before this line because of the last '\'! +// Also keep those single spaces after 'OPTION(' for proper sorting. + +// clang-format on + +/*------------------------------------------------------------------------*/ + +// Some of the 'OPTION' macros above should only be included if certain +// compile time options are enabled. This has the effect, that for instance +// if 'LOGGING' is defined, and thus logging code is included, then also the +// 'log' option is defined. Otherwise the 'log' option is not included. + +#ifdef LOGGING +#define LOGOPT OPTION +#else +#define LOGOPT(...) /**/ +#endif + +#ifdef QUIET +#define QUTOPT(...) /**/ +#else +#define QUTOPT OPTION +#endif + +/*------------------------------------------------------------------------*/ + +namespace CaDiCaL { + +struct Internal; + +/*------------------------------------------------------------------------*/ + +class Options; + +struct Option { + const char *name; + int def, lo, hi; + int optimizable; + bool preprocessing; + const char *description; + int &val (Options *); +}; + +/*------------------------------------------------------------------------*/ + +// Produce a compile time constant for the number of options. + +static const size_t number_of_options = +#define OPTION(N, V, L, H, O, P, R, D) 1 + + OPTIONS +#undef OPTION + + 0; + +/*------------------------------------------------------------------------*/ + +class Options { + + Internal *internal; + + void set (Option *, int val); // Force to [lo,hi] interval. + + friend struct Option; + static Option table[]; + + static void initialize_from_environment (int &val, const char *name, + const int L, const int H); + + friend Config; + + void reset_default_values (); + void disable_preprocessing (); + +public: + // For library usage we disable reporting by default while for the stand + // alone SAT solver we enable it by default. This default value has to + // be set before the constructor of 'Options' is called (which in turn is + // called from the constructor of 'Solver'). If we would simply overwrite + // its initial value while initializing the stand alone solver, we will + // get that change of the default value (from 'false' to 'true') shown + // during calls to 'print ()', which is confusing to the user. + // + static int reportdefault; + + Options (Internal *); + + // Makes options directly accessible, e.g., for instance declares the + // member 'int restart' here. This will give fast access to option values + // internally in the solver and thus can also be used in tight loops. + // +private: + int __start_of_options__; // Used by 'val' below. +public: +#define OPTION(N, V, L, H, O, P, R, D) \ + int N; // Access option values by name. + OPTIONS +#undef OPTION + + // It would be more elegant to use an anonymous 'struct' of the actual + // option values overlayed with an 'int values[number_of_options]' array + // but that is not proper ISO C++ and produces a warning. Instead we use + // the following construction which relies on '__start_of_options__' and + // that the following options are really allocated directly after it. + // + inline int &val (size_t idx) { + assert (idx < number_of_options); + return (&__start_of_options__ + 1)[idx]; + } + + // With the following function we can get rather fast access to the option + // limits, the default value and the description. The code uses binary + // search over the sorted option 'table'. This static data is shared + // among different instances of the solver. The actual current option + // values are here in the 'Options' class. They can be accessed by the + // offset of the static options using 'Option::val' if you have an + // 'Option' or to have even faster access directly by the member function + // (the 'N' above, e.g., 'restart'). + // + static Option *has (const char *name); + + bool set (const char *name, int); // Explicit version. + int get (const char *name); // Get current value. + + void print (); // Print current values in command line form + static void usage (); // Print usage message for all options. + + void optimize (int val); // increase some limits (val=0..31) + + static bool is_preprocessing_option (const char *name); + + // Parse long option argument + // + // --<name> + // --<name>=<val> + // --no-<name> + // + // where '<val>' is as in 'parse_option_value'. If parsing succeeds, + // 'true' is returned and the string will be set to the name of the + // option. Additionally the parsed value is set (last argument). + // + static bool parse_long_option (const char *, string &, int &); + + // Iterating options. + + typedef Option *iterator; + typedef const Option *const_iterator; + + static iterator begin () { return table; } + static iterator end () { return table + number_of_options; } + + void copy (Options &other) const; // Copy 'this' into 'other'. +}; + +inline int &Option::val (Options *opts) { + assert (Options::table <= this && + this < Options::table + number_of_options); + return opts->val (this - Options::table); +} + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/parse.cpp b/src/sat/cadical/parse.cpp new file mode 100644 index 000000000..25334385e --- /dev/null +++ b/src/sat/cadical/parse.cpp @@ -0,0 +1,436 @@ +#include "internal.hpp" + +/*------------------------------------------------------------------------*/ + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Parse error. + +#define PER(...) \ + do { \ + internal->error_message.init ( \ + "%s:%" PRIu64 ": parse error: ", file->name (), \ + (uint64_t) file->lineno ()); \ + return internal->error_message.append (__VA_ARGS__); \ + } while (0) + +/*------------------------------------------------------------------------*/ + +// Parsing utilities. + +inline int Parser::parse_char () { return file->get (); } + +// Return an non zero error string if a parse error occurred. + +inline const char *Parser::parse_string (const char *str, char prev) { + for (const char *p = str; *p; p++) + if (parse_char () == *p) + prev = *p; + else if (*p == ' ') + PER ("expected space after '%c'", prev); + else + PER ("expected '%c' after '%c'", *p, prev); + return 0; +} + +inline const char *Parser::parse_positive_int (int &ch, int &res, + const char *name) { + assert (isdigit (ch)); + res = ch - '0'; + while (isdigit (ch = parse_char ())) { + int digit = ch - '0'; + if (INT_MAX / 10 < res || INT_MAX - digit < 10 * res) + PER ("too large '%s' in header", name); + res = 10 * res + digit; + } + return 0; +} + +static const char *cube_token = "unexpected 'a' in CNF"; + +inline const char *Parser::parse_lit (int &ch, int &lit, int &vars, + int strict) { + if (ch == 'a') + return cube_token; + int sign = 0; + if (ch == '-') { + if (!isdigit (ch = parse_char ())) + PER ("expected digit after '-'"); + sign = -1; + } else if (!isdigit (ch)) + PER ("expected digit or '-'"); + else + sign = 1; + lit = ch - '0'; + while (isdigit (ch = parse_char ())) { + int digit = ch - '0'; + if (INT_MAX / 10 < lit || INT_MAX - digit < 10 * lit) + PER ("literal too large"); + lit = 10 * lit + digit; + } + if (ch == '\r') + ch = parse_char (); + if (ch != 'c' && ch != ' ' && ch != '\t' && ch != '\n' && ch != EOF) + PER ("expected white space after '%d'", sign * lit); + if (lit > vars) { + if (strict != FORCED) + PER ("literal %d exceeds maximum variable %d", sign * lit, vars); + else + vars = lit; + } + lit *= sign; + return 0; +} + +/*------------------------------------------------------------------------*/ + +// Parsing CNF in DIMACS format. + +const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { + +#ifndef QUIET + double start = internal->time (); +#endif + + bool found_inccnf_header = false; + int ch, clauses = 0; + vars = 0; + + // First read comments before header with possibly embedded options. + // + for (;;) { + ch = parse_char (); + if (strict != STRICT) + if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r') + continue; + if (ch != 'c') + break; + string buf; + while ((ch = parse_char ()) != '\n') + if (ch == EOF) + PER ("unexpected end-of-file in header comment"); + else if (ch != '\r') + buf.push_back (ch); + const char *o; + for (o = buf.c_str (); *o && *o != '-'; o++) + ; + if (!*o) + continue; + PHASE ("parse-dimacs", "found option '%s'", o); + if (*o) + solver->set_long_option (o); + } + + if (ch != 'p') + PER ("expected 'c' or 'p'"); + + ch = parse_char (); + if (strict == STRICT) { + if (ch != ' ') + PER ("expected space after 'p'"); + ch = parse_char (); + } else if (ch != ' ' && ch != '\t') + PER ("expected white space after 'p'"); + else { + do + ch = parse_char (); + while (ch == ' ' || ch == '\t'); + } + + // Now read 'p cnf <var> <clauses>' header of DIMACS file + // or 'p inccnf' of incremental 'INCCNF' file. + // + if (ch == 'c') { + assert (!found_inccnf_header); + if (strict == STRICT) { + const char *err = parse_string ("nf ", 'c'); + if (err) + return err; + ch = parse_char (); + if (!isdigit (ch)) + PER ("expected digit after 'p cnf '"); + err = parse_positive_int (ch, vars, "<max-var>"); + if (err) + return err; + if (ch != ' ') + PER ("expected ' ' after 'p cnf %d'", vars); + if (!isdigit (ch = parse_char ())) + PER ("expected digit after 'p cnf %d '", vars); + err = parse_positive_int (ch, clauses, "<num-clauses>"); + if (err) + return err; + if (ch != '\n') + PER ("expected new-line after 'p cnf %d %d'", vars, clauses); + } else { + if (parse_char () != 'n') + PER ("expected 'n' after 'p c'"); + if (parse_char () != 'f') + PER ("expected 'f' after 'p cn'"); + ch = parse_char (); + if (!isspace (ch)) + PER ("expected space after 'p cnf'"); + do + ch = parse_char (); + while (isspace (ch)); + if (!isdigit (ch)) + PER ("expected digit after 'p cnf '"); + const char *err = parse_positive_int (ch, vars, "<max-var>"); + if (err) + return err; + if (!isspace (ch)) + PER ("expected space after 'p cnf %d'", vars); + do + ch = parse_char (); + while (isspace (ch)); + if (!isdigit (ch)) + PER ("expected digit after 'p cnf %d '", vars); + err = parse_positive_int (ch, clauses, "<num-clauses>"); + if (err) + return err; + while (ch != '\n') { + if (ch != '\r' && !isspace (ch)) + PER ("expected new-line after 'p cnf %d %d'", vars, clauses); + ch = parse_char (); + } + } + + MSG ("found %s'p cnf %d %d'%s header", tout.green_code (), vars, + clauses, tout.normal_code ()); + + if (strict != FORCED) + solver->reserve (vars); + internal->reserve_ids (clauses); + } else if (!parse_inccnf_too) + PER ("expected 'c' after 'p '"); + else if (ch == 'i') { + found_inccnf_header = true; + const char *err = parse_string ("nccnf", 'i'); + if (err) + return err; + ch = parse_char (); + if (strict == STRICT) { + if (ch != '\n') + PER ("expected new-line after 'p inccnf'"); + } else { + while (ch != '\n') { + if (ch != '\r' && !isspace (ch)) + PER ("expected new-line after 'p inccnf'"); + ch = parse_char (); + } + } + + MSG ("found %s'p inccnf'%s header", tout.green_code (), + tout.normal_code ()); + + strict = FORCED; + } else + PER ("expected 'c' or 'i' after 'p '"); + + if (parse_inccnf_too) + *parse_inccnf_too = false; + + // Now read body of DIMACS part. + // + int lit = 0, parsed = 0; + while ((ch = parse_char ()) != EOF) { + if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r') + continue; + if (ch == 'c') { + while ((ch = parse_char ()) != '\n' && ch != EOF) + ; + if (ch == EOF) + break; + continue; + } + if (ch == 'a' && found_inccnf_header) + break; + const char *err = parse_lit (ch, lit, vars, strict); + if (err) + return err; + if (ch == 'c') { + while ((ch = parse_char ()) != '\n') + if (ch == EOF) + PER ("unexpected end-of-file in comment"); + } + solver->add (lit); + if (!found_inccnf_header && !lit && parsed++ >= clauses && + strict != FORCED) + PER ("too many clauses"); + } + + if (lit) + PER ("last clause without terminating '0'"); + + if (!found_inccnf_header && parsed < clauses && strict != FORCED) + PER ("clause missing"); + +#ifndef QUIET + double end = internal->time (); + MSG ("parsed %d clauses in %.2f seconds %s time", parsed, end - start, + internal->opts.realtime ? "real" : "process"); +#endif + +#ifndef QUIET + start = end; + size_t num_cubes = 0; +#endif + if (ch == 'a') { + assert (parse_inccnf_too); + assert (found_inccnf_header); + if (!*parse_inccnf_too) + *parse_inccnf_too = true; + for (;;) { + ch = parse_char (); + if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r') + continue; + if (ch == 'c') { + while ((ch = parse_char ()) != '\n' && ch != EOF) + ; + if (ch == EOF) + break; + continue; + } + const char *err = parse_lit (ch, lit, vars, strict); + if (err == cube_token) + PER ("two 'a' in a row"); + else if (err) + return err; + if (ch == 'c') { + while ((ch = parse_char ()) != '\n') + if (ch == EOF) + PER ("unexpected end-of-file in comment"); + } + if (cubes) + cubes->push_back (lit); + if (!lit) { +#ifndef QUIET + num_cubes++; +#endif + for (;;) { + ch = parse_char (); + if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\r') + continue; + if (ch == 'c') { + while ((ch = parse_char ()) != '\n' && ch != EOF) + ; + if (ch == EOF) + break; + } + if (ch == EOF) + break; + if (ch != 'a') + PER ("expected 'a' or end-of-file after zero"); + lit = INT_MIN; + break; + } + if (ch == EOF) + break; + } + } + if (lit) + PER ("last cube without terminating '0'"); + } +#ifndef QUIET + if (found_inccnf_header) { + double end = internal->time (); + MSG ("parsed %zd cubes in %.2f seconds %s time", num_cubes, end - start, + internal->opts.realtime ? "real" : "process"); + } +#endif + + return 0; +} + +/*------------------------------------------------------------------------*/ + +// Parsing solution in competition output format. + +const char *Parser::parse_solution_non_profiled () { + external->solution = new signed char[external->max_var + 1u]; + external->solution_size = external->max_var; + clear_n (external->solution, external->max_var + 1u); + int ch; + for (;;) { + ch = parse_char (); + if (ch == EOF) + PER ("missing 's' line"); + else if (ch == 'c') { + while ((ch = parse_char ()) != '\n') + if (ch == EOF) + PER ("unexpected end-of-file in comment"); + } else if (ch == 's') + break; + else + PER ("expected 'c' or 's'"); + } + const char *err = parse_string (" SATISFIABLE", 's'); + if (err) + return err; + if ((ch = parse_char ()) == '\r') + ch = parse_char (); + if (ch != '\n') + PER ("expected new-line after 's SATISFIABLE'"); +#ifndef QUIET + int count = 0; +#endif + for (;;) { + ch = parse_char (); + if (ch != 'v') + PER ("expected 'v' at start-of-line"); + if ((ch = parse_char ()) != ' ') + PER ("expected ' ' after 'v'"); + int lit = 0; + ch = parse_char (); + do { + if (ch == ' ' || ch == '\t') { + ch = parse_char (); + continue; + } + err = parse_lit (ch, lit, external->max_var, false); + if (err) + return err; + if (ch == 'c') + PER ("unexpected comment"); + if (!lit) + break; + if (external->solution[abs (lit)]) + PER ("variable %d occurs twice", abs (lit)); + LOG ("solution %d", lit); + external->solution[abs (lit)] = sign (lit); +#ifndef QUIET + count++; +#endif + if (ch == '\r') + ch = parse_char (); + } while (ch != '\n'); + if (!lit) + break; + } + MSG ("parsed %d values %.2f%%", count, + percent (count, external->max_var)); + return 0; +} + +/*------------------------------------------------------------------------*/ + +// Wrappers to profile parsing and at the same time use the convenient +// implicit 'return' in PER in the non-profiled versions. + +const char *Parser::parse_dimacs (int &vars, int strict) { + assert (strict == FORCED || strict == RELAXED || strict == STRICT); + START (parse); + const char *err = parse_dimacs_non_profiled (vars, strict); + STOP (parse); + return err; +} + +const char *Parser::parse_solution () { + START (parse); + const char *err = parse_solution_non_profiled (); + STOP (parse); + return err; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/parse.hpp b/src/sat/cadical/parse.hpp new file mode 100644 index 000000000..c90c9f5a9 --- /dev/null +++ b/src/sat/cadical/parse.hpp @@ -0,0 +1,72 @@ +#ifndef _parse_hpp_INCLUDED +#define _parse_hpp_INCLUDED + +#include <cassert> +#include <vector> + +namespace CaDiCaL { + +// Factors out common functions for parsing of DIMACS and solution files. + +class File; +struct External; +struct Internal; + +class Parser { + + Solver *solver; + Internal *internal; + External *external; + File *file; + + void perr (const char *fmt, ...) CADICAL_ATTRIBUTE_FORMAT (2, 3); + int parse_char (); + + enum { + FORCED = 0, // Force reading even if header is broken. + RELAXED = 1, // Relaxed white space treatment in header. + STRICT = 2, // Strict white space and header compliance. + }; + + const char *parse_string (const char *str, char prev); + const char *parse_positive_int (int &ch, int &res, const char *name); + const char *parse_lit (int &ch, int &lit, int &vars, int strict); + const char *parse_dimacs_non_profiled (int &vars, int strict); + const char *parse_solution_non_profiled (); + + bool *parse_inccnf_too; + vector<int> *cubes; + +public: + // Parse a DIMACS CNF or ICNF file. + // + // Return zero if successful. Otherwise parse error. + Parser (Solver *s, File *f, bool *i, vector<int> *c) + : solver (s), internal (s->internal), external (s->external), + file (f), parse_inccnf_too (i), cubes (c) {} + + // Parse a DIMACS file. Return zero if successful. Otherwise a parse + // error is return. The parsed clauses are added to the solver and the + // maximum variable index found is returned in the 'vars' argument. The + // 'strict' argument can be '0' in which case the numbers in the header + // can be arbitrary, e.g., 'p cnf 0 0' all the time, without producing a + // parse error. Only for this setting the parsed literals are not checked + // to overflow the maximum variable index of the header. The strictest + // form of parsing is enforced for the value '2' of 'strict', in which + // case the header can not have additional white space, while a value of + // '1' exactly relaxes this, e.g., 'p cnf \t 1 3 \r\n' becomes legal. + // + const char *parse_dimacs (int &vars, int strict); + + // Parse a solution file as used in the SAT competition, e.g., with + // comment lines 'c ...', a status line 's ...' and value lines 'v ...'. + // Returns zero if successful. Otherwise a string is returned describing + // the parse error. The parsed solution is saved in 'solution' and can be + // accessed with 'sol (int lit)'. We use it for checking learned clauses. + // + const char *parse_solution (); +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/phases.cpp b/src/sat/cadical/phases.cpp new file mode 100644 index 000000000..92edd28a0 --- /dev/null +++ b/src/sat/cadical/phases.cpp @@ -0,0 +1,44 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +void Internal::copy_phases (vector<signed char> &dst) { + START (copy); + for (auto i : vars) + dst[i] = phases.saved[i]; + STOP (copy); +} + +void Internal::clear_phases (vector<signed char> &dst) { + START (copy); + for (auto i : vars) + dst[i] = 0; + STOP (copy); +} + +void Internal::phase (int lit) { + const int idx = vidx (lit); + signed char old_forced_phase = phases.forced[idx]; + signed char new_forced_phase = sign (lit); + if (old_forced_phase == new_forced_phase) { + LOG ("forced phase remains at %d", old_forced_phase * idx); + return; + } + if (old_forced_phase) + LOG ("overwriting old forced phase %d", old_forced_phase * idx); + LOG ("new forced phase %d", new_forced_phase * idx); + phases.forced[idx] = new_forced_phase; +} + +void Internal::unphase (int lit) { + const int idx = vidx (lit); + signed char old_forced_phase = phases.forced[idx]; + if (!old_forced_phase) { + LOG ("forced phase of %d already reset", lit); + return; + } + LOG ("clearing old forced phase %d", old_forced_phase * idx); + phases.forced[idx] = 0; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/phases.hpp b/src/sat/cadical/phases.hpp new file mode 100644 index 000000000..fe3459415 --- /dev/null +++ b/src/sat/cadical/phases.hpp @@ -0,0 +1,18 @@ +#ifndef _phases_hpp_INCLUDED +#define _phases_hpp_INCLUDED + +namespace CaDiCaL { + +struct Phases { + + vector<signed char> best; // The current largest trail phase. + vector<signed char> forced; // Forced through 'phase'. + vector<signed char> min; // The current minimum unsatisfied phase. + vector<signed char> prev; // Previous during local search. + vector<signed char> saved; // The actual saved phase. + vector<signed char> target; // The current target phase. +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/probe.cpp b/src/sat/cadical/probe.cpp new file mode 100644 index 000000000..acab6405b --- /dev/null +++ b/src/sat/cadical/probe.cpp @@ -0,0 +1,987 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Failed literal probing uses its own propagation and assignment +// functions. It further provides on-the-fly generation of hyper binary +// resolvents but only probes on roots of the binary implication graph. The +// search for failed literals is limited, but untried roots are kept until +// the next time 'probe' is called. Left over probes from the last attempt +// and new probes are tried until the limit is hit or all are tried. + +/*------------------------------------------------------------------------*/ + +bool Internal::inprobing () { + if (!opts.inprobing) + return false; + if (!preprocessing && !opts.inprocessing) + return false; + if (preprocessing) + assert (lim.preprocessing); + if (stats.inprobingphases && last.inprobe.reductions == stats.reductions) + return false; + return lim.inprobe <= stats.conflicts; +} + +/*------------------------------------------------------------------------*/ + +inline int Internal::get_parent_reason_literal (int lit) { + const int idx = vidx (lit); + int res = parents[idx]; + if (lit < 0) + res = -res; + return res; +} + +inline void Internal::set_parent_reason_literal (int lit, int reason) { + const int idx = vidx (lit); + if (lit < 0) + reason = -reason; + parents[idx] = reason; +} + +/*-----------------------------------------------------------------------*/ + +// for opts.probehbr=false we need to do a lot of extra work to remember the +// correct lrat_chains... This solution is also memory intensive I think +// all corresponding functions are guarded to only work with the right +// options so they can be called without checking for options +// +// call locally after failed_literal or backtracking +// +void Internal::clean_probehbr_lrat () { + if (!lrat || opts.probehbr) + return; + for (auto &field : probehbr_chains) { + for (auto &chain : field) { + chain.clear (); + } + } +} + +// call globally before a probe round (or a lookahead round) +// +void Internal::init_probehbr_lrat () { + if (!lrat || opts.probehbr) + return; + const size_t size = 2 * (1 + (size_t) max_var); + probehbr_chains.resize (size); + for (size_t i = 0; i < size; i++) { + probehbr_chains[i].resize (size); + // commented because not needed... should be empty already + /* + for (size_t j = 0; j < size; j++) { + vector<int64_t> empty; + probehbr_chains[i][j] = empty; + } + */ + } +} + +// sets lrat_chain to the stored chain in probehbr_chains. +// this leads to conflict with unit reason uip +// +void Internal::get_probehbr_lrat (int lit, int uip) { + if (!lrat || opts.probehbr) + return; + assert (lit); + assert (lrat_chain.empty ()); + assert (val (uip) < 0); + lrat_chain = probehbr_chains[vlit (lit)][vlit (uip)]; + int64_t id = unit_id (-uip); + lrat_chain.push_back (id); +} + +// sets the corresponding probehbr_chain to what is currently stored in +// lrat_chain. also clears lrat_chain. +// +void Internal::set_probehbr_lrat (int lit, int uip) { + if (!lrat || opts.probehbr) + return; + assert (lit); + assert (lrat_chain.size ()); + assert (probehbr_chains[vlit (lit)][vlit (uip)].empty ()); + probehbr_chains[vlit (lit)][vlit (uip)] = lrat_chain; + lrat_chain.clear (); +} + +// compute lrat_chain for the part of the tree from lit to dom +// use mini_chain because it needs to be reversed +// +void Internal::probe_dominator_lrat (int dom, Clause *reason) { + if (!lrat || !dom) + return; + LOG (reason, "probe dominator LRAT for %d from", dom); + for (const auto lit : *reason) { + if (val (lit) >= 0) + continue; + const auto other = -lit; + if (other == dom) + continue; + Flags &f = flags (other); + if (f.seen) + continue; + f.seen = true; + analyzed.push_back (other); + Var u = var (other); + if (u.level) { + if (!u.reason) { + LOG ("this may be a problem %d", other); + continue; + } + probe_dominator_lrat (dom, u.reason); + continue; + } + int64_t id = unit_id (other); + lrat_chain.push_back (id); + } + lrat_chain.push_back (reason->id); +} + +/*------------------------------------------------------------------------*/ + +// On-the-fly (dynamic) hyper binary resolution on decision level one can +// make use of the fact that the implication graph is actually a tree. + +// Compute a dominator of two literals in the binary implication tree. + +int Internal::probe_dominator (int a, int b) { + require_mode (PROBE); + int l = a, k = b; + Var *u = &var (l), *v = &var (k); + assert (val (l) > 0), assert (val (k) > 0); + assert (u->level == 1), assert (v->level == 1); + while (l != k) { + if (u->trail > v->trail) + swap (l, k), swap (u, v); + if (!get_parent_reason_literal (l)) + return l; + int parent = get_parent_reason_literal (k); + assert (parent), assert (val (parent) > 0); + v = &var (k = parent); + assert (v->level == 1); + } + LOG ("dominator %d of %d and %d", l, a, b); + assert (val (l) > 0); + return l; +} + +// The idea of dynamic on-the-fly hyper-binary resolution came up in the +// PrecoSAT solver, where it originally was used on all decision levels. + +// It turned out, that most of the hyper-binary resolvents were generated +// during probing on decision level one anyhow. Thus this version is +// specialized to decision level one, where actually all long (non-binary) +// forcing clauses can be resolved to become binary. So if we find a clause +// which would force a new assignment at decision level one during probing +// we resolve it (the 'reason' argument) to obtain a hyper binary resolvent. +// It consists of the still unassigned literal (the new unit) and the +// negation of the unique closest dominator of the negation of all (false) +// literals in the clause (which has to exist on decision level one). + +// There are two special cases which should be mentioned: +// +// (A) The reason is already a binary clause in a certain sense, since all +// its unwatched literals are root level fixed to false. In this +// situation it would be better to shrink the clause immediately instead +// of adding a new clause consisting only of the watched literals. +// However, this would happen during the next garbage collection anyhow. +// +// (B) The resolvent subsumes the original reason clause. This is +// equivalent to the property that the negated dominator is contained in +// the original reason. Again one could in principle shrink the clause. +// +// Note that (A) is actually subsumed by (B). The possible optimization to +// shrink the clause on-the-fly is difficult (need to update 'blit' and +// 'binary' of the other watch at least) and also not really that important. +// For (B) we simply add the new binary resolvent and mark the old subsumed +// clause as garbage instead. And since in the situation of (A) the +// shrinking will be performed at the next garbage collection anyhow, we +// do not change clauses in (A). + +// The hyper binary resolvent clause is redundant unless it subsumes the +// original reason and that one is irredundant. + +// If the option 'opts.probehbr' is 'false', we actually do not add the new +// hyper binary resolvent, but simply pretend we would have added it and +// still return the dominator as new reason / parent for the new unit. + +// Finally note that adding clauses changes the watches of the propagated +// literal and thus we can not use standard iterators during probing but +// need to fall back to indices. One watch for the hyper binary resolvent +// clause is added at the end of the currently propagated watches, but its +// watch is a binary watch and will be skipped during propagating long +// clauses anyhow. + +inline int Internal::hyper_binary_resolve (Clause *reason) { + require_mode (PROBE); + assert (level == 1); + assert (reason->size > 2); + const const_literal_iterator end = reason->end (); + const int *lits = reason->literals; + const_literal_iterator k; +#ifndef NDEBUG + // First literal unassigned, all others false. + assert (!val (lits[0])); + for (k = lits + 1; k != end; k++) + assert (val (*k) < 0); + assert (var (lits[1]).level == 1); +#endif + LOG (reason, "hyper binary resolving"); + stats.hbrs++; + stats.hbrsizes += reason->size; + const int lit = lits[1]; + int dom = -lit, non_root_level_literals = 0; + for (k = lits + 2; k != end; k++) { + const int other = -*k; + assert (val (other) > 0); + if (!var (other).level) + continue; + dom = probe_dominator (dom, other); + non_root_level_literals++; + } + probe_reason = reason; + if (non_root_level_literals && opts.probehbr) { // !(A) + bool contained = false; + for (k = lits + 1; !contained && k != end; k++) + contained = (*k == -dom); + const bool red = !contained || reason->redundant; + if (red) + stats.hbreds++; + LOG ("new %s hyper binary resolvent %d %d", + (red ? "redundant" : "irredundant"), -dom, lits[0]); + assert (clause.empty ()); + clause.push_back (-dom); + clause.push_back (lits[0]); + probe_dominator_lrat (dom, reason); + if (lrat) + clear_analyzed_literals (); + Clause *c = new_hyper_binary_resolved_clause (red, 2); + probe_reason = c; + if (red) + c->hyper = true; + clause.clear (); + lrat_chain.clear (); + if (contained) { + stats.hbrsubs++; + LOG (reason, "subsumed original"); + mark_garbage (reason); + } + } else if (non_root_level_literals && lrat) { + // still calculate LRAT and remember for later + assert (!opts.probehbr); + probe_dominator_lrat (dom, reason); + clear_analyzed_literals (); + set_probehbr_lrat (dom, lits[0]); + } + return dom; +} + +/*------------------------------------------------------------------------*/ + +// The following functions 'probe_assign' and 'probe_propagate' are used for +// propagating during failed literal probing in simplification mode, as +// replacement of the generic propagation routine 'propagate' and +// 'search_assign'. + +// The code is mostly copied from 'propagate.cpp' and specialized. We only +// comment on the differences. More explanations are in 'propagate.cpp'. + +inline void Internal::probe_assign (int lit, int parent) { + require_mode (PROBE); + int idx = vidx (lit); + assert (!val (idx)); + assert (!flags (idx).eliminated () || !parent); + assert (!parent || val (parent) > 0); + Var &v = var (idx); + v.level = level; + v.trail = (int) trail.size (); + assert ((int) num_assigned < max_var); + num_assigned++; + v.reason = level ? probe_reason : 0; + probe_reason = 0; + set_parent_reason_literal (lit, parent); + if (!level) + learn_unit_clause (lit); + else + assert (level == 1); + const signed char tmp = sign (lit); + set_val (idx, tmp); + assert (val (lit) > 0); + assert (val (-lit) < 0); + trail.push_back (lit); + + // Do not save the current phase during inprocessing but remember the + // number of units on the trail of the last time this literal was + // assigned. This allows us to avoid some redundant failed literal + // probing attempts. Search for 'propfixed' in 'probe.cpp' for details. + // + if (level) + propfixed (lit) = stats.all.fixed; + + if (parent) + LOG ("probe assign %d parent %d", lit, parent); + else if (level) + LOG ("probe assign %d probe", lit); + else + LOG ("probe assign %d negated failed literal UIP", lit); +} + +void Internal::probe_assign_decision (int lit) { + require_mode (PROBE); + assert (!level); + assert (propagated == trail.size ()); + level++; + control.push_back (Level (lit, trail.size ())); + probe_assign (lit, 0); +} + +void Internal::probe_assign_unit (int lit) { + require_mode (PROBE); + assert (!level); + assert (active (lit)); + probe_assign (lit, 0); +} + +/*------------------------------------------------------------------------*/ + +// same as in propagate but inlined here +// +inline void Internal::probe_lrat_for_units (int lit) { + if (!lrat) + return; + if (level) + return; // not decision level 0 + LOG ("building chain for units"); + assert (lrat_chain.empty ()); + assert (probe_reason); + for (auto &reason_lit : *probe_reason) { + if (lit == reason_lit) + continue; + assert (val (reason_lit)); + if (!val (reason_lit)) + continue; + const int signed_reason_lit = val (reason_lit) * reason_lit; + int64_t id = unit_id (signed_reason_lit); + lrat_chain.push_back (id); + } + lrat_chain.push_back (probe_reason->id); +} + +/*------------------------------------------------------------------------*/ + +// This is essentially the same as 'propagate' except that we prioritize and +// always propagate binary clauses first (see our CPAIOR'13 paper on tree +// based look ahead), then immediately stop at a conflict and of course use +// 'probe_assign' instead of 'search_assign'. The binary propagation part +// is factored out too. If a new unit on decision level one is found we +// perform hyper binary resolution and thus actually build an implication +// tree instead of a DAG. Statistics counters are also different. + +inline void Internal::probe_propagate2 () { + require_mode (PROBE); + int64_t &ticks = stats.ticks.probe; + while (propagated2 != trail.size ()) { + const int lit = -trail[propagated2++]; + LOG ("probe propagating %d over binary clauses", -lit); + Watches &ws = watches (lit); + ticks += 1 + cache_lines (ws.size (), sizeof (const_watch_iterator *)); + for (const auto &w : ws) { + if (!w.binary ()) + continue; + const signed char b = val (w.blit); + if (b > 0) + continue; + ticks++; + if (b < 0) + conflict = w.clause; // but continue + else { + assert (lrat_chain.empty ()); + assert (!probe_reason); + probe_reason = w.clause; + probe_lrat_for_units (w.blit); + probe_assign (w.blit, -lit); + lrat_chain.clear (); + } + } + } +} + +bool Internal::probe_propagate () { + require_mode (PROBE); + assert (!unsat); + START (propagate); + int64_t before = propagated2 = propagated; + int64_t &ticks = stats.ticks.probe; + while (!conflict) { + if (propagated2 != trail.size ()) + probe_propagate2 (); + else if (propagated != trail.size ()) { + const int lit = -trail[propagated++]; + LOG ("probe propagating %d over large clauses", -lit); + Watches &ws = watches (lit); + ticks += 1 + cache_lines (ws.size (), + sizeof (sizeof (const_watch_iterator *))); + size_t i = 0, j = 0; + while (i != ws.size ()) { + const Watch w = ws[j++] = ws[i++]; + if (w.binary ()) + continue; + const signed char b = val (w.blit); + if (b > 0) + continue; + ticks++; + if (w.clause->garbage) + continue; + const literal_iterator lits = w.clause->begin (); + const int other = lits[0] ^ lits[1] ^ lit; + // lits[0] = other, lits[1] = lit; + const signed char u = val (other); + if (u > 0) + ws[j - 1].blit = other; + else { + const int size = w.clause->size; + const const_literal_iterator end = lits + size; + const literal_iterator middle = lits + w.clause->pos; + literal_iterator k = middle; + int r = 0; + signed char v = -1; + while (k != end && (v = val (r = *k)) < 0) + k++; + if (v < 0) { + k = lits + 2; + assert (w.clause->pos <= size); + while (k != middle && (v = val (r = *k)) < 0) + k++; + } + w.clause->pos = k - lits; + assert (lits + 2 <= k), assert (k <= w.clause->end ()); + if (v > 0) + ws[j - 1].blit = r; + else if (!v) { + ticks++; + LOG (w.clause, "unwatch %d in", r); + *k = lit; + lits[0] = other; + lits[1] = r; + watch_literal (r, lit, w.clause); + j--; + } else if (!u) { + ticks++; + if (level == 1) { + lits[0] = other, lits[1] = lit; + assert (lrat_chain.empty ()); + assert (!probe_reason); + int dom = hyper_binary_resolve (w.clause); + probe_assign (other, dom); + } else { + ticks++; + assert (lrat_chain.empty ()); + assert (!probe_reason); + probe_reason = w.clause; + probe_lrat_for_units (other); + probe_assign_unit (other); + lrat_chain.clear (); + } + probe_propagate2 (); + } else + conflict = w.clause; + } + } + if (j != i) { + while (i != ws.size ()) + ws[j++] = ws[i++]; + ws.resize (j); + } + } else + break; + } + int64_t delta = propagated2 - before; + stats.propagations.probe += delta; + if (conflict) + LOG (conflict, "conflict"); + STOP (propagate); + return !conflict; +} + +/*------------------------------------------------------------------------*/ + +// This a specialized instance of 'analyze'. + +void Internal::failed_literal (int failed) { + + LOG ("analyzing failed literal probe %d", failed); + stats.failed++; + stats.probefailed++; + + assert (!unsat); + assert (conflict); + assert (level == 1); + assert (analyzed.empty ()); + assert (lrat_chain.empty ()); + + START (analyze); + + LOG (conflict, "analyzing failed literal conflict"); + + int uip = 0; + for (const auto &lit : *conflict) { + const int other = -lit; + if (!var (other).level) { + assert (val (other) > 0); + continue; + } + uip = uip ? probe_dominator (uip, other) : other; + } + probe_dominator_lrat (uip, conflict); + if (lrat) + clear_analyzed_literals (); + + LOG ("found probing UIP %d", uip); + assert (uip); + + vector<int> work; + + int parent = uip; + while (parent != failed) { + const int next = get_parent_reason_literal (parent); + parent = next; + assert (parent); + work.push_back (parent); + } + + backtrack (); + conflict = 0; + + assert (!val (uip)); + probe_assign_unit (-uip); + lrat_chain.clear (); + + if (!probe_propagate ()) + learn_empty_clause (); + + size_t j = 0; + while (!unsat && j < work.size ()) { + // assert (!opts.probehbr); assertion fails ... + const int parent = work[j++]; + const signed char tmp = val (parent); + if (tmp > 0) { + assert (!opts.probehbr); // ... assertion should hold here + get_probehbr_lrat (parent, uip); + LOG ("clashing failed parent %d", parent); + learn_empty_clause (); + } else if (tmp == 0) { + assert (!opts.probehbr); // ... and here + LOG ("found unassigned failed parent %d", parent); + get_probehbr_lrat (parent, uip); // this is computed during + probe_assign_unit (-parent); // propagation and can include + lrat_chain.clear (); // multiple chains where only one + if (!probe_propagate ()) + learn_empty_clause (); // is needed! + } + uip = parent; + } + work.clear (); + erase_vector (work); + + STOP (analyze); + + assert (unsat || val (failed) < 0); +} + +/*------------------------------------------------------------------------*/ + +bool Internal::is_binary_clause (Clause *c, int &a, int &b) { + assert (!level); + if (c->garbage) + return false; + int first = 0, second = 0; + for (const auto &lit : *c) { + const signed char tmp = val (lit); + if (tmp > 0) + return false; + if (tmp < 0) + continue; + if (second) + return false; + if (first) + second = lit; + else + first = lit; + } + if (!second) + return false; + a = first, b = second; + return true; +} + +// We probe on literals first, which occur more often negated and thus we +// sort the 'probes' stack in such a way that literals which occur negated +// less frequently come first. Probes are taken from the back of the stack. + +struct probe_negated_noccs_rank { + Internal *internal; + probe_negated_noccs_rank (Internal *i) : internal (i) {} + typedef size_t Type; + Type operator() (int a) const { return internal->noccs (-a); } +}; + +// Fill the 'probes' schedule. + +void Internal::generate_probes () { + + assert (probes.empty ()); + + int64_t &ticks = stats.ticks.probe; + + // First determine all the literals which occur in binary clauses. It is + // way faster to go over the clauses once, instead of walking the watch + // lists for each literal. + // + init_noccs (); + ticks += 1 + cache_lines (clauses.size (), sizeof (Clause *)); + for (const auto &c : clauses) { + int a, b; + ticks++; + if (!is_binary_clause (c, a, b)) + continue; + noccs (a)++; + noccs (b)++; + } + + for (auto idx : vars) { + + // Then focus on roots of the binary implication graph, which are + // literals occurring negatively in a binary clause, but not positively. + // If neither 'idx' nor '-idx' is a root it makes less sense to probe + // this variable. + + // This argument requires that equivalent literal substitution through + // 'decompose' is performed, because otherwise there might be 'cyclic + // roots' which are not tried, i.e., -1 2 0, 1 -2 0, 1 2 3 0, 1 2 -3 0. + + ticks += 2; + + const bool have_pos_bin_occs = noccs (idx) > 0; + const bool have_neg_bin_occs = noccs (-idx) > 0; + + if (have_pos_bin_occs == have_neg_bin_occs) + continue; + + int probe = have_neg_bin_occs ? idx : -idx; + + // See the discussion where 'propfixed' is used below. + // + if (propfixed (probe) >= stats.all.fixed) + continue; + + LOG ("scheduling probe %d negated occs %" PRId64 "", probe, + noccs (-probe)); + probes.push_back (probe); + } + + rsort (probes.begin (), probes.end (), probe_negated_noccs_rank (this)); + + reset_noccs (); + shrink_vector (probes); + + PHASE ("probe-round", stats.probingrounds, + "scheduled %zd literals %.0f%%", probes.size (), + percent (probes.size (), 2u * max_var)); +} + +// Follow the ideas in 'generate_probes' but flush non root probes and +// reorder remaining probes. + +void Internal::flush_probes () { + + assert (!probes.empty ()); + int64_t &ticks = stats.ticks.probe; + + init_noccs (); + ticks += 1 + cache_lines (clauses.size (), sizeof (Clause *)); + for (const auto &c : clauses) { + int a, b; + ticks++; + if (!is_binary_clause (c, a, b)) + continue; + noccs (a)++; + noccs (b)++; + } + + const auto eop = probes.end (); + auto j = probes.begin (); + for (auto i = j; i != eop; i++) { + int lit = *i; + if (!active (lit)) + continue; + ticks += 2; + const bool have_pos_bin_occs = noccs (lit) > 0; + const bool have_neg_bin_occs = noccs (-lit) > 0; + if (have_pos_bin_occs == have_neg_bin_occs) + continue; + if (have_pos_bin_occs) + lit = -lit; + assert (!noccs (lit)), assert (noccs (-lit) > 0); + if (propfixed (lit) >= stats.all.fixed) + continue; + LOG ("keeping probe %d negated occs %" PRId64 "", lit, noccs (-lit)); + *j++ = lit; + } + size_t remain = j - probes.begin (); +#ifndef QUIET + size_t flushed = probes.size () - remain; +#endif + probes.resize (remain); + + rsort (probes.begin (), probes.end (), probe_negated_noccs_rank (this)); + + reset_noccs (); + shrink_vector (probes); + + PHASE ("probe-round", stats.probingrounds, + "flushed %zd literals %.0f%% remaining %zd", flushed, + percent (flushed, remain + flushed), remain); +} + +int Internal::next_probe () { + + int generated = 0; + + for (;;) { + + if (probes.empty ()) { + if (generated++) + return 0; + generate_probes (); + } + + while (!probes.empty ()) { + + int probe = probes.back (); + probes.pop_back (); + + // Eliminated or assigned. + // + if (!active (probe)) + continue; + + // There is now new unit since the last time we propagated this probe, + // thus we propagated it before without obtaining a conflict and + // nothing changed since then. Thus there is no need to propagate it + // again. This observation was independently made by Partik Simons + // et.al. in the context of implementing 'smodels' (see for instance + // Alg. 4 in his JAIR article from 2002) and it has also been + // contributed to the thesis work of Yacine Boufkhad. + // + if (propfixed (probe) >= stats.all.fixed) + continue; + + return probe; + } + } +} + +bool Internal::probe () { + + if (!opts.probe) + return false; + if (unsat) + return false; + if (terminated_asynchronously ()) + return false; + + SET_EFFORT_LIMIT (limit, probe, true); + + START_SIMPLIFIER (probe, PROBE); + stats.probingrounds++; + + // Probing is limited in terms of non-probing propagations + // 'stats.propagations'. We allow a certain percentage 'opts.probeeffort' + // (say %5) of probing propagations in each probing with a lower bound of + // 'opts.probmineff'. + // + + PHASE ("probe-round", stats.probingrounds, + "probing limit of %" PRId64 " propagations ", limit); + + int old_failed = stats.failed; +#ifndef QUIET + int64_t old_probed = stats.probed; +#endif + int64_t old_hbrs = stats.hbrs; + + if (!probes.empty ()) + flush_probes (); + + // We reset 'propfixed' since there was at least another conflict thus + // a new learned clause, which might produce new propagations (and hyper + // binary resolvents). During 'generate_probes' we keep the old value. + // + for (auto idx : vars) + propfixed (idx) = propfixed (-idx) = -1; + + assert (unsat || propagated == trail.size ()); + propagated = propagated2 = trail.size (); + + int probe; + init_probehbr_lrat (); + while (!unsat && !terminated_asynchronously () && + stats.ticks.probe < limit && (probe = next_probe ())) { + stats.probed++; + LOG ("probing %d", probe); + probe_assign_decision (probe); + if (probe_propagate ()) + backtrack (); + else + failed_literal (probe); + clean_probehbr_lrat (); + } + + if (unsat) + LOG ("probing derived empty clause"); + else if (propagated < trail.size ()) { + LOG ("probing produced %zd units", + (size_t) (trail.size () - propagated)); + if (!propagate ()) { + LOG ("propagating units after probing results in empty clause"); + learn_empty_clause (); + } else + sort_watches (); + } + + int failed = stats.failed - old_failed; +#ifndef QUIET + int64_t probed = stats.probed - old_probed; +#endif + int64_t hbrs = stats.hbrs - old_hbrs; + + PHASE ("probe-round", stats.probingrounds, + "probed %" PRId64 " and found %d failed literals", probed, failed); + + if (hbrs) + PHASE ("probe-round", stats.probingrounds, + "found %" PRId64 " hyper binary resolvents", hbrs); + + STOP_SIMPLIFIER (probe, PROBE); + + report ('p', !opts.reportall && !(unsat + failed + hbrs)); + + return !unsat && failed; +} + +/*------------------------------------------------------------------------*/ + +// This schedules a number of inprocessing techniques. +// These range from very cheap and beneficial (decompose) to +// more expensive and sometimes less beneficial. We want to limit +// expensive techniques to some fraction of total time or search time. +// this is done using 'ticks'. +// Generally, there are options for each of the techniques to set the +// efficiency, i.e., the fraction of ticks they are allowed as budget. +// Whenever e.g. vivify is called, the budget is calculated from the +// search ticks that have passed since the last vivify round and this +// efficiency. +// We want to be able to run inprocessing frequently, without it dominating +// runtimes. This entire inprocessing scheme is scheduled after a certain +// amount of conflicts were found, the gap between two inprocessing rounds +// increasing by a constant number each time. In effect, the number of +// inprocessing rounds is allways the square root of the number of conflicts +// with some constant factor. +// This factor can also be with the option 'inprobeint' +// Some of the techniques are not run always, for different reasons. +// 'factor' or BVA depends on certain structures of the irredundant clauses +// and as such will only be run when new irredundant clauses are derived or +// it was not able to finish with the entire search space. +// 'sweeping' is especially usefull on certain classes of formulas, and uses +// a increasing or decreasing delay that depends on how usefull it was. +// In cases where it is less usefull, we obviously want to reset the budged, +// even if the routine was delayed. +// Additionally 'vivify', 'sweep' and 'factor' can also have a big initial +// overhead in setting up the datastructures. This has to be accounted for +// with the 'ticks', however, since inprocessing is done frequently, this +// overhead is too expensive to pay. So instead, we accumulate the budget +// of 'ticks' and delay the technique until it passes a certain threshhold, +// which depends on the the cost of initialization. Note that in the case of +// sweeping, we have two different delays, one which resets the budged, and +// one which passes it to the next round. In this case the former takes +// precendent, until we would run sweeping once, at which point the focus +// switches to the latter delay until the budget is big enough, such that +// sweeping can be run. Then we switch back to the other delay. + +void CaDiCaL::Internal::inprobe (bool update_limits) { + + if (unsat) + return; + if (level) + backtrack (); + if (!propagate ()) { + learn_empty_clause (); + return; + } + + stats.inprobingphases++; + if (external_prop) { + assert (!level); + private_steps = true; + } + const int before = active (); + const int before_extended = stats.variables_extension; + + // schedule of inprobing techniques. + // + { + mark_duplicated_binary_clauses_as_garbage (); + decompose (); + if (ternary ()) + decompose (); // If we derived a binary clause + if (probe ()) + decompose (); + + if (extract_gates ()) + decompose (); + if (sweep ()) // full occurrence list + decompose (); // ... and (ELS) afterwards. + (void) vivify (); // resets watches + transred (); // builds big. + factor (); // resets watches, partial occurrence list + } + + if (external_prop) { + assert (!level); + private_steps = false; + } + + if (!update_limits) + return; + + const int after = active (); + const int after_extended = stats.variables_extension; + const int diff_extended = after_extended - before_extended; + assert (diff_extended >= 0); + const int removed = before - after + diff_extended; + assert (removed >= 0); + + if (removed) { + stats.inprobesuccess++; + PHASE ("probe-phase", stats.inprobingphases, + "successfully removed %d active variables %.0f%%", removed, + percent (removed, before)); + } else + PHASE ("probe-phase", stats.inprobingphases, + "could not remove any active variable"); + + const int64_t delta = + 25 * opts.inprobeint * log10 (stats.inprobingphases + 9); + lim.inprobe = stats.conflicts + delta; + + PHASE ("probe-phase", stats.inprobingphases, + "new limit at %" PRId64 " conflicts after %" PRId64 " conflicts", + lim.inprobe, delta); + + last.inprobe.reductions = stats.reductions; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/profile.cpp b/src/sat/cadical/profile.cpp new file mode 100644 index 000000000..68d80cbed --- /dev/null +++ b/src/sat/cadical/profile.cpp @@ -0,0 +1,107 @@ +#ifndef QUIET + +#include "internal.hpp" + +namespace CaDiCaL { + +// Initialize all profile counters with constant name and profiling level. + +Profiles::Profiles (Internal *s) + : internal (s) +#define PROFILE(NAME, LEVEL) , NAME (#NAME, LEVEL) + PROFILES +#undef PROFILE +{ +} + +void Internal::start_profiling (Profile &profile, double s) { + assert (profile.level <= opts.profile); + assert (!profile.active); + profile.started = s; + profile.active = true; +} + +void Internal::stop_profiling (Profile &profile, double s) { + assert (profile.level <= opts.profile); + assert (profile.active); + profile.value += s - profile.started; + profile.active = false; +} + +double Internal::update_profiles () { + double now = time (); +#define PROFILE(NAME, LEVEL) \ + do { \ + Profile &profile = profiles.NAME; \ + if (profile.active) { \ + assert (profile.level <= opts.profile); \ + profile.value += now - profile.started; \ + profile.started = now; \ + } \ + } while (0); + PROFILES +#undef PROFILE + return now; +} + +double Internal::solve_time () { + (void) update_profiles (); + return profiles.solve.value; +} + +#define PRT(S, T) \ + MSG ("%s" S "%s", tout.magenta_code (), T, tout.normal_code ()) + +void Internal::print_profile () { + double now = update_profiles (); + const char *time_type = opts.realtime ? "real" : "process"; + SECTION ("run-time profiling"); + PRT ("%s time taken by individual solving procedures", time_type); + PRT ("(percentage relative to %s time for solving)", time_type); + LINE (); + const size_t size = sizeof profiles / sizeof (Profile); + struct Profile *profs[size]; + size_t n = 0; +#define PROFILE(NAME, LEVEL) \ + do { \ + if (LEVEL > opts.profile) \ + break; \ + Profile *p = &profiles.NAME; \ + if (p == &profiles.solve) \ + break; \ + if (!profiles.NAME.value && p != &profiles.parse && \ + p != &profiles.search && p != &profiles.simplify) \ + break; \ + profs[n++] = p; \ + } while (0); + PROFILES +#undef PROFILE + + assert (n <= size); + + // Explicit bubble sort to avoid heap allocation since 'print_profile' + // is also called during catching a signal after out of heap memory. + // This only makes sense if 'profs' is allocated on the stack, and + // not the heap, which should be the case. + + double solve = profiles.solve.value; + + for (size_t i = 0; i < n; i++) { + for (size_t j = i + 1; j < n; j++) + if (profs[j]->value > profs[i]->value) + swap (profs[i], profs[j]); + MSG ("%12.2f %7.2f%% %s", profs[i]->value, + percent (profs[i]->value, solve), profs[i]->name); + } + + MSG (" ================================="); + MSG ("%12.2f %7.2f%% solve", solve, percent (solve, now)); + + LINE (); + PRT ("last line shows %s time for solving", time_type); + PRT ("(percentage relative to total %s time)", time_type); +} + +} // namespace CaDiCaL + +#endif // ifndef QUIET diff --git a/src/sat/cadical/profile.hpp b/src/sat/cadical/profile.hpp new file mode 100644 index 000000000..4267eb29c --- /dev/null +++ b/src/sat/cadical/profile.hpp @@ -0,0 +1,274 @@ +#ifndef _profiles_h_INCLUDED +#define _profiles_h_INCLUDED + +/*------------------------------------------------------------------------*/ +#ifndef QUIET +/*------------------------------------------------------------------------*/ + +namespace CaDiCaL { + +struct Internal; + +/*------------------------------------------------------------------------*/ + +// The solver contains some built in profiling (even for optimized code). +// The idea is that even without using external tools it is possible to get +// an overview of where time is spent. This is enabled with the option +// 'profile', e.g., you might want to use '--profile=3', or even higher +// values for more detailed profiling information. Currently the default is +// '--profile=2', which should only induce a tiny profiling overhead. +// +// Profiling has a Heisenberg effect, since we rely on calling 'getrusage' +// instead of using profile counters and sampling. For functions which are +// executed many times, this overhead is substantial (say 10%-20%). For +// functions which are not executed many times there is in essence no +// overhead in measuring time spent in them. These get a smaller profiling +// level, which is the second argument in the 'PROFILE' macro below. Thus +// using '--profile=1' for instance should not add any penalty to the +// run-time, while '--profile=3' and higher levels slow down the solver. +// +// To profile say 'foo', just add another line 'PROFILE(foo,LEVEL)' and wrap +// the code to be profiled within a 'START (foo)' / 'STOP (foo)' block. + +/*------------------------------------------------------------------------*/ + +// Profile counters for functions which are not compiled in should be +// removed. This is achieved by adding a wrapper macro for them here. + +/*------------------------------------------------------------------------*/ + +#ifdef PROFILE_MODE +#define MROFILE PROFILE +#else +#define MROFILE(...) /**/ +#endif + +#define PROFILES \ + PROFILE (analyze, 3) \ + MROFILE (analyzestable, 4) \ + MROFILE (analyzeunstable, 4) \ + PROFILE (backward, 3) \ + PROFILE (block, 2) \ + PROFILE (bump, 4) \ + PROFILE (checking, 2) \ + PROFILE (cdcl, 1) \ + PROFILE (collect, 3) \ + PROFILE (compact, 3) \ + PROFILE (condition, 2) \ + PROFILE (congruence, 2) \ + PROFILE (congruencemerge, 4) \ + PROFILE (congruencematching, 4) \ + PROFILE (connect, 3) \ + PROFILE (copy, 4) \ + PROFILE (cover, 2) \ + PROFILE (decide, 3) \ + PROFILE (decompose, 3) \ + PROFILE (definition, 2) \ + PROFILE (elim, 2) \ + PROFILE (factor, 2) \ + PROFILE (fastelim, 2) \ + PROFILE (extend, 3) \ + PROFILE (extract, 3) \ + PROFILE (extractands, 4) \ + PROFILE (extractbinaries, 4) \ + PROFILE (extractites, 4) \ + PROFILE (extractxors, 4) \ + PROFILE (instantiate, 2) \ + PROFILE (lucky, 2) \ + PROFILE (lookahead, 2) \ + PROFILE (minimize, 4) \ + PROFILE (shrink, 4) \ + PROFILE (parse, 0) /* As 'opts.profile' might change in parsing*/ \ + PROFILE (probe, 2) \ + PROFILE (deduplicate, 3) \ + PROFILE (propagate, 4) \ + MROFILE (propstable, 4) \ + MROFILE (propunstable, 4) \ + PROFILE (reduce, 3) \ + PROFILE (restart, 3) \ + PROFILE (restore, 2) \ + PROFILE (search, 1) \ + PROFILE (solve, 0) \ + PROFILE (stable, 2) \ + PROFILE (sweep, 2) \ + PROFILE (sweepbackbone, 3) \ + PROFILE (sweepequivalences, 3) \ + PROFILE (sweepflip, 4) \ + PROFILE (sweepimplicant, 4) \ + PROFILE (sweepsolve, 4) \ + PROFILE (preprocess, 2) \ + PROFILE (simplify, 1) \ + PROFILE (subsume, 2) \ + PROFILE (ternary, 2) \ + PROFILE (transred, 3) \ + PROFILE (unstable, 2) \ + PROFILE (vivify, 2) \ + PROFILE (walk, 2) + +/*------------------------------------------------------------------------*/ + +// See 'START' and 'STOP' in 'macros.hpp' too. + +struct Profile { + + bool active; + double value; // accumulated time + double started; // started time if active + const char *name; // name of the profiled function (or 'phase') + const int level; // allows to cheaply test if profiling is enabled + + Profile (const char *n, int l) + : active (false), value (0), name (n), level (l) {} +}; + +struct Profiles { + Internal *internal; +#define PROFILE(NAME, LEVEL) Profile NAME; + PROFILES +#undef PROFILE + Profiles (Internal *); +}; + +} // namespace CaDiCaL + +#define NON_QUIET_PROFILE_CODE(CODE) CODE + +#else // !defined(QUIET) + +#define NON_QUIET_PROFILE_CODE(CODE) /**/ + +#endif + +/*------------------------------------------------------------------------*/ + +// Macros for Profiling support and checking and changing the mode. + +#define START(P) \ + do { \ + NON_QUIET_PROFILE_CODE ( \ + if (internal->profiles.P.level <= internal->opts.profile) \ + internal->start_profiling (internal->profiles.P, \ + internal->time ());) \ + } while (0) + +#define STOP(P) \ + do { \ + NON_QUIET_PROFILE_CODE ( \ + if (internal->profiles.P.level <= internal->opts.profile) \ + internal->stop_profiling (internal->profiles.P, \ + internal->time ());) \ + } while (0) + +#define PROFILE_ACTIVE(P) \ + ((internal->profiles.P.level <= internal->opts.profile) && \ + (internal->profiles.P.active)) + +/*------------------------------------------------------------------------*/ + +#define START_SIMPLIFIER(S, M) \ + do { \ + NON_QUIET_PROFILE_CODE (const double N = time (); \ + const int L = internal->opts.profile;) \ + if (!preprocessing && !lookingahead) { \ + NON_QUIET_PROFILE_CODE ( \ + if (stable && internal->profiles.stable.level <= L) \ + internal->stop_profiling (internal->profiles.stable, N); \ + if (!stable && internal->profiles.unstable.level <= L) \ + internal->stop_profiling (internal->profiles.unstable, N); \ + if (internal->profiles.search.level <= L) \ + internal->stop_profiling (internal->profiles.search, N);) \ + reset_mode (SEARCH); \ + } \ + NON_QUIET_PROFILE_CODE ( \ + if (internal->profiles.simplify.level <= L) \ + internal->start_profiling (internal->profiles.simplify, N); \ + if (internal->profiles.S.level <= L) \ + internal->start_profiling (internal->profiles.S, N);) \ + set_mode (SIMPLIFY); \ + set_mode (M); \ + } while (0) + +/*------------------------------------------------------------------------*/ + +#define STOP_SIMPLIFIER(S, M) \ + do { \ + NON_QUIET_PROFILE_CODE ( \ + const double N = time (); const int L = internal->opts.profile; \ + if (internal->profiles.S.level <= L) \ + internal->stop_profiling (internal->profiles.S, N); \ + if (internal->profiles.simplify.level <= L) \ + internal->stop_profiling (internal->profiles.simplify, N);) \ + reset_mode (M); \ + reset_mode (SIMPLIFY); \ + if (!preprocessing && !lookingahead) { \ + NON_QUIET_PROFILE_CODE ( \ + if (internal->profiles.search.level <= L) \ + internal->start_profiling (internal->profiles.search, N); \ + if (stable && internal->profiles.stable.level <= L) \ + internal->start_profiling (internal->profiles.stable, N); \ + if (!stable && internal->profiles.unstable.level <= L) \ + internal->start_profiling (internal->profiles.unstable, N);) \ + set_mode (SEARCH); \ + } \ + } while (0) + +/*------------------------------------------------------------------------*/ +// Used in 'walk' before calling 'walk_round' within the CDCL loop. + +#define START_INNER_WALK() \ + do { \ + require_mode (SEARCH); \ + assert (!preprocessing); \ + NON_QUIET_PROFILE_CODE ( \ + const double N = time (); const int L = internal->opts.profile; \ + if (stable && internal->profiles.stable.level <= L) \ + internal->stop_profiling (internal->profiles.stable, N); \ + if (!stable && internal->profiles.unstable.level <= L) \ + internal->stop_profiling (internal->profiles.unstable, N); \ + if (internal->profiles.walk.level <= L) \ + internal->start_profiling (internal->profiles.walk, N);) \ + set_mode (WALK); \ + } while (0) + +/*------------------------------------------------------------------------*/ +// Used in 'walk' after calling 'walk_round' within the CDCL loop. + +#define STOP_INNER_WALK() \ + do { \ + require_mode (SEARCH); \ + assert (!preprocessing); \ + reset_mode (WALK); \ + NON_QUIET_PROFILE_CODE ( \ + const double N = time (); const int L = internal->opts.profile; \ + if (internal->profiles.walk.level <= L) \ + internal->stop_profiling (internal->profiles.walk, N); \ + if (stable && internal->profiles.stable.level <= L) \ + internal->start_profiling (internal->profiles.stable, N); \ + if (!stable && internal->profiles.unstable.level <= L) \ + internal->start_profiling (internal->profiles.unstable, N); \ + internal->profiles.walk.started = (N);) \ + } while (0) + +/*------------------------------------------------------------------------*/ +// Used in 'local_search' before calling 'walk_round'. + +#define START_OUTER_WALK() \ + do { \ + require_mode (SEARCH); \ + assert (!preprocessing); \ + NON_QUIET_PROFILE_CODE (START (walk);) \ + set_mode (WALK); \ + } while (0) + +/*------------------------------------------------------------------------*/ +// Used in 'local_search' after calling 'walk_round'. + +#define STOP_OUTER_WALK() \ + do { \ + require_mode (SEARCH); \ + assert (!preprocessing); \ + reset_mode (WALK); \ + NON_QUIET_PROFILE_CODE (STOP (walk);) \ + } while (0) + +#endif // ifndef _profiles_h_INCLUDED diff --git a/src/sat/cadical/proof.cpp b/src/sat/cadical/proof.cpp new file mode 100644 index 000000000..36b629423 --- /dev/null +++ b/src/sat/cadical/proof.cpp @@ -0,0 +1,657 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +using namespace std; + +/*------------------------------------------------------------------------*/ + +// Enable proof logging and checking by allocating a 'Proof' object. + +void Internal::new_proof_on_demand () { + if (!proof) { + LOG ("connecting proof to internal solver"); + proof = new Proof (this); + } +} + +void Internal::resize_unit_clauses_idx () { + size_t new_vsize = vsize ? 2 * vsize : 1 + (size_t) max_var; + unit_clauses_idx.resize (2 * new_vsize, 0); +} + +void Internal::force_lrat () { + if (lrat) + return; + assert (proof); + lrat = true; +} + +void Internal::connect_proof_tracer (Tracer *tracer, bool antecedents, + bool finalize_clauses) { + new_proof_on_demand (); + if (antecedents) + force_lrat (); + if (finalize_clauses) + frat = true; + resize_unit_clauses_idx (); + proof->connect (tracer); + tracers.push_back (tracer); +} + +void Internal::connect_proof_tracer (InternalTracer *tracer, + bool antecedents, + bool finalize_clauses) { + new_proof_on_demand (); + if (antecedents) + force_lrat (); + if (finalize_clauses) + frat = true; + resize_unit_clauses_idx (); + tracer->connect_internal (this); + proof->connect (tracer); + tracers.push_back (tracer); +} + +void Internal::connect_proof_tracer (StatTracer *tracer, bool antecedents, + bool finalize_clauses) { + new_proof_on_demand (); + if (antecedents) + force_lrat (); + if (finalize_clauses) + frat = true; + resize_unit_clauses_idx (); + tracer->connect_internal (this); + proof->connect (tracer); + stat_tracers.push_back (tracer); +} + +void Internal::connect_proof_tracer (FileTracer *tracer, bool antecedents, + bool finalize_clauses) { + new_proof_on_demand (); + if (antecedents) + force_lrat (); + if (finalize_clauses) + frat = true; + resize_unit_clauses_idx (); + tracer->connect_internal (this); + proof->connect (tracer); + file_tracers.push_back (tracer); +} + +bool Internal::disconnect_proof_tracer (Tracer *tracer) { + auto it = std::find (tracers.begin (), tracers.end (), tracer); + if (it != tracers.end ()) { + tracers.erase (it); + assert (proof); + proof->disconnect (tracer); + return true; + } + return false; +} + +bool Internal::disconnect_proof_tracer (StatTracer *tracer) { + auto it = std::find (stat_tracers.begin (), stat_tracers.end (), tracer); + if (it != stat_tracers.end ()) { + stat_tracers.erase (it); + assert (proof); + proof->disconnect (tracer); + return true; + } + return false; +} + +bool Internal::disconnect_proof_tracer (FileTracer *tracer) { + auto it = std::find (file_tracers.begin (), file_tracers.end (), tracer); + if (it != file_tracers.end ()) { + file_tracers.erase (it); + assert (proof); + proof->disconnect (tracer); + return true; + } + return false; +} + +void Proof::disconnect (Tracer *t) { + tracers.erase (std::remove (tracers.begin (), tracers.end (), t), + tracers.end ()); +} + +// Enable proof tracing. + +void Internal::trace (File *file) { + if (opts.veripb) { + LOG ("PROOF connecting VeriPB tracer"); + bool antecedents = opts.veripb == 1 || opts.veripb == 2; + bool deletions = opts.veripb == 2 || opts.veripb == 4; + FileTracer *ft = + new VeripbTracer (this, file, opts.binary, antecedents, deletions); + connect_proof_tracer (ft, antecedents); + } else if (opts.frat) { + LOG ("PROOF connecting FRAT tracer"); + bool antecedents = opts.frat == 1; + resize_unit_clauses_idx (); + FileTracer *ft = + new FratTracer (this, file, opts.binary, opts.frat == 1); + connect_proof_tracer (ft, antecedents, true); + } else if (opts.lrat) { + LOG ("PROOF connecting LRAT tracer"); + FileTracer *ft = new LratTracer (this, file, opts.binary); + connect_proof_tracer (ft, true); + } else if (opts.idrup) { + LOG ("PROOF connecting IDRUP tracer"); + FileTracer *ft = new IdrupTracer (this, file, opts.binary); + connect_proof_tracer (ft, true); + } else if (opts.lidrup) { + LOG ("PROOF connecting LIDRUP tracer"); + FileTracer *ft = new LidrupTracer (this, file, opts.binary); + connect_proof_tracer (ft, true); + } else { + LOG ("PROOF connecting DRAT tracer"); + FileTracer *ft = new DratTracer (this, file, opts.binary); + connect_proof_tracer (ft, false); + } +} + +// Enable proof checking. + +void Internal::check () { + new_proof_on_demand (); + if (opts.checkproof > 1) { + StatTracer *lratchecker = new LratChecker (this); + DeferDeletePtr<LratChecker> delete_lratchecker ( + (LratChecker *) lratchecker); + LOG ("PROOF connecting LRAT proof checker"); + force_lrat (); + frat = true; + resize_unit_clauses_idx (); + proof->connect (lratchecker); + stat_tracers.push_back (lratchecker); + delete_lratchecker.release (); + } + if (opts.checkproof == 1 || opts.checkproof == 3) { + StatTracer *checker = new Checker (this); + DeferDeletePtr<Checker> delete_checker ((Checker *) checker); + LOG ("PROOF connecting proof checker"); + proof->connect (checker); + stat_tracers.push_back (checker); + delete_checker.release (); + } +} + +// We want to close a proof trace and stop checking as soon we are done. + +void Internal::close_trace (bool print) { + for (auto &tracer : file_tracers) + tracer->close (print); +} + +// We can flush a proof trace file before actually closing it. + +void Internal::flush_trace (bool print) { + for (auto &tracer : file_tracers) + tracer->flush (print); +} + +/*------------------------------------------------------------------------*/ + +Proof::Proof (Internal *s) : internal (s) { LOG ("PROOF new"); } + +Proof::~Proof () { LOG ("PROOF delete"); } + +/*------------------------------------------------------------------------*/ + +inline void Proof::add_literal (int internal_lit) { + const int external_lit = internal->externalize (internal_lit); + clause.push_back (external_lit); +} + +inline void Proof::add_literals (Clause *c) { + for (auto const &lit : *c) + add_literal (lit); +} + +inline void Proof::add_literals (const vector<int> &c) { + for (auto const &lit : c) + add_literal (lit); +} + +/*------------------------------------------------------------------------*/ + +void Proof::add_original_clause (int64_t id, bool r, const vector<int> &c) { + LOG (c, "PROOF adding original internal clause"); + add_literals (c); + clause_id = id; + redundant = r; + add_original_clause (); +} + +void Proof::add_external_original_clause (int64_t id, bool r, + const vector<int> &c, + bool restore) { + // literals of c are already external + assert (clause.empty ()); + for (auto const &lit : c) + clause.push_back (lit); + clause_id = id; + redundant = r; + add_original_clause (restore); +} + +void Proof::delete_external_original_clause (int64_t id, bool r, + const vector<int> &c) { + // literals of c are already external + assert (clause.empty ()); + for (auto const &lit : c) + clause.push_back (lit); + clause_id = id; + redundant = r; + delete_clause (); +} + +void Proof::add_derived_empty_clause (int64_t id, + const vector<int64_t> &chain) { + LOG ("PROOF adding empty clause"); + assert (clause.empty ()); + assert (proof_chain.empty ()); + for (const auto &cid : chain) + proof_chain.push_back (cid); + clause_id = id; + redundant = false; + add_derived_clause (); +} + +void Proof::add_derived_unit_clause (int64_t id, int internal_unit, + const vector<int64_t> &chain) { + LOG ("PROOF adding unit clause %d", internal_unit); + assert (proof_chain.empty ()); + assert (clause.empty ()); + add_literal (internal_unit); + for (const auto &cid : chain) + proof_chain.push_back (cid); + clause_id = id; + redundant = false; + add_derived_clause (); +} + +/*------------------------------------------------------------------------*/ + +void Proof::add_derived_clause (Clause *c, const vector<int64_t> &chain) { + LOG (c, "PROOF adding to proof derived"); + assert (clause.empty ()); + assert (proof_chain.empty ()); + add_literals (c); + for (const auto &cid : chain) + proof_chain.push_back (cid); + clause_id = c->id; + redundant = c->redundant; + add_derived_clause (); +} + +void Proof::add_derived_clause (int64_t id, bool r, const vector<int> &c, + const vector<int64_t> &chain) { + LOG (c, "PROOF adding derived clause"); + assert (clause.empty ()); + assert (proof_chain.empty ()); + for (const auto &lit : c) + add_literal (lit); + for (const auto &cid : chain) + proof_chain.push_back (cid); + clause_id = id; + redundant = r; + add_derived_clause (); +} + +void Proof::add_assumption_clause (int64_t id, const vector<int> &c, + const vector<int64_t> &chain) { + // literals of c are already external + assert (clause.empty ()); + assert (proof_chain.empty ()); + for (const auto &lit : c) + clause.push_back (lit); + for (const auto &cid : chain) + proof_chain.push_back (cid); + clause_id = id; + add_assumption_clause (); +} + +void Proof::add_assumption (int a) { + // a is already external + assert (clause.empty ()); + assert (proof_chain.empty ()); + clause.push_back (a); + add_assumption (); +} + +void Proof::add_constraint (const vector<int> &c) { + // literals of c are already external + assert (clause.empty ()); + assert (proof_chain.empty ()); + for (const auto &lit : c) + clause.push_back (lit); + add_constraint (); +} + +void Proof::add_assumption_clause (int64_t id, int lit, + const vector<int64_t> &chain) { + assert (clause.empty ()); + assert (proof_chain.empty ()); + clause.push_back (lit); + for (const auto &cid : chain) + proof_chain.push_back (cid); + clause_id = id; + add_assumption_clause (); +} + +void Proof::delete_clause (Clause *c) { + LOG (c, "PROOF deleting from proof"); + clause.clear (); // Can be non-empty if an allocation fails during adding. + add_literals (c); + clause_id = c->id; + redundant = c->redundant; + delete_clause (); // Increments 'statistics.deleted'. +} + +void Proof::delete_clause (int64_t id, bool r, const vector<int> &c) { + LOG (c, "PROOF deleting from proof"); + assert (clause.empty ()); + add_literals (c); + clause_id = id; + redundant = r; + delete_clause (); // Increments 'statistics.deleted'. +} + +void Proof::weaken_minus (Clause *c) { + LOG (c, "PROOF weaken minus of"); + assert (clause.empty ()); + add_literals (c); + clause_id = c->id; + weaken_minus (); +} + +void Proof::weaken_minus (int64_t id, const vector<int> &c) { + LOG (c, "PROOF deleting from proof"); + assert (clause.empty ()); + add_literals (c); + clause_id = id; + weaken_minus (); +} + +void Proof::weaken_plus (Clause *c) { + weaken_minus (c); + delete_clause (c); // Increments 'statistics.deleted'. +} + +void Proof::weaken_plus (int64_t id, const vector<int> &c) { + weaken_minus (id, c); + delete_clause (id, false, c); // Increments 'statistics.deleted'. +} + +void Proof::delete_unit_clause (int64_t id, const int lit) { + LOG ("PROOF deleting unit from proof %d", lit); + assert (clause.empty ()); + add_literal (lit); + clause_id = id; + redundant = false; + delete_clause (); +} + +void Proof::finalize_clause (Clause *c) { + LOG (c, "PROOF finalizing clause"); + assert (clause.empty ()); + add_literals (c); + clause_id = c->id; + finalize_clause (); +} + +void Proof::finalize_clause (int64_t id, const vector<int> &c) { + LOG (c, "PROOF finalizing clause"); + assert (clause.empty ()); + for (const auto &lit : c) + add_literal (lit); + clause_id = id; + finalize_clause (); +} + +void Proof::finalize_unit (int64_t id, int lit) { + LOG ("PROOF finalizing clause %d", lit); + assert (clause.empty ()); + add_literal (lit); + clause_id = id; + finalize_clause (); +} + +void Proof::finalize_external_unit (int64_t id, int lit) { + LOG ("PROOF finalizing clause %d", lit); + assert (clause.empty ()); + clause.push_back (lit); + clause_id = id; + finalize_clause (); +} + +/*------------------------------------------------------------------------*/ + +// During garbage collection clauses are shrunken by removing falsified +// literals. To avoid copying the clause, we provide a specialized tracing +// function here, which traces the required 'add' and 'remove' operations. + +void Proof::flush_clause (Clause *c) { + LOG (c, "PROOF flushing falsified literals in"); + assert (clause.empty ()); + const bool antecedents = (internal->lrat || internal->frat); + for (int i = 0; i < c->size; i++) { + int internal_lit = c->literals[i]; + if (internal->fixed (internal_lit) < 0) { + if (antecedents) { + int64_t id = internal->unit_id (-internal_lit); + proof_chain.push_back (id); + } + continue; + } + add_literal (internal_lit); + } + proof_chain.push_back (c->id); + redundant = c->redundant; + int64_t id = ++internal->clause_id; + clause_id = id; + add_derived_clause (); + delete_clause (c); + c->id = id; +} + +// While strengthening clauses, e.g., through self-subsuming resolutions, +// during subsumption checking, we have a similar situation, except that we +// have to remove exactly one literal. Again the following function allows +// to avoid copying the clause and instead provides tracing of the required +// 'add' and 'remove' operations. + +void Proof::strengthen_clause (Clause *c, int remove, + const vector<int64_t> &chain) { + LOG (c, "PROOF strengthen by removing %d in", remove); + assert (clause.empty ()); + for (int i = 0; i < c->size; i++) { + int internal_lit = c->literals[i]; + if (internal_lit == remove) + continue; + add_literal (internal_lit); + } + int64_t id = ++internal->clause_id; + clause_id = id; + redundant = c->redundant; + for (const auto &cid : chain) + proof_chain.push_back (cid); + add_derived_clause (); + delete_clause (c); + c->id = id; +} + +void Proof::otfs_strengthen_clause (Clause *c, const std::vector<int> &old, + const vector<int64_t> &chain) { + LOG (c, "PROOF otfs strengthen"); + assert (clause.empty ()); + for (int i = 0; i < c->size; i++) { + int internal_lit = c->literals[i]; + add_literal (internal_lit); + } + int64_t id = ++internal->clause_id; + clause_id = id; + redundant = c->redundant; + for (const auto &cid : chain) + proof_chain.push_back (cid); + add_derived_clause (); + delete_clause (c->id, c->redundant, old); + c->id = id; +} + +void Proof::strengthen (int64_t id) { + clause_id = id; + strengthen (); +} + +/*------------------------------------------------------------------------*/ + +void Proof::add_original_clause (bool restore) { + LOG (clause, "PROOF adding original external clause"); + assert (clause_id); + + for (auto &tracer : tracers) { + tracer->add_original_clause (clause_id, false, clause, restore); + } + clause.clear (); + clause_id = 0; +} + +void Proof::add_derived_clause () { + LOG (clause, "PROOF adding derived external clause (redundant: %d)", + redundant); + assert (clause_id); + for (auto &tracer : tracers) { + tracer->add_derived_clause (clause_id, redundant, clause, proof_chain); + } + proof_chain.clear (); + clause.clear (); + clause_id = 0; +} + +void Proof::delete_clause () { + LOG (clause, "PROOF deleting external clause"); + for (auto &tracer : tracers) { + tracer->delete_clause (clause_id, redundant, clause); + } + clause.clear (); + clause_id = 0; +} + +void Proof::demote_clause () { + LOG (clause, "PROOF demoting external clause"); + assert (!redundant); + for (auto &tracer : tracers) { + tracer->demote_clause (clause_id, clause); + } + clause.clear (); + clause_id = 0; +} + +void Proof::weaken_minus () { + LOG (clause, "PROOF marking as clause to restore"); + for (auto &tracer : tracers) { + tracer->weaken_minus (clause_id, clause); + } + clause.clear (); + clause_id = 0; +} + +void Proof::strengthen () { + LOG ("PROOF strengthen clause with id %" PRId64, clause_id); + for (auto &tracer : tracers) { + tracer->strengthen (clause_id); + } + clause_id = 0; +} + +void Proof::finalize_clause () { + for (auto &tracer : tracers) { + tracer->finalize_clause (clause_id, clause); + } + clause.clear (); + clause_id = 0; +} + +void Proof::add_assumption_clause () { + LOG (clause, "PROOF adding assumption clause"); + for (auto &tracer : tracers) { + tracer->add_assumption_clause (clause_id, clause, proof_chain); + } + proof_chain.clear (); + clause.clear (); + clause_id = 0; +} + +void Proof::add_assumption () { + LOG (clause, "PROOF adding assumption"); + assert (clause.size () == 1); + for (auto &tracer : tracers) { + tracer->add_assumption (clause.back ()); + } + clause.clear (); +} + +void Proof::add_constraint () { + LOG (clause, "PROOF adding constraint"); + for (auto &tracer : tracers) { + tracer->add_constraint (clause); + } + clause.clear (); +} + +void Proof::reset_assumptions () { + LOG ("PROOF reset assumptions"); + for (auto &tracer : tracers) { + tracer->reset_assumptions (); + } +} + +void Proof::report_status (int status, int64_t id) { + LOG ("PROOF reporting status %d", status); + for (auto &tracer : tracers) { + tracer->report_status (status, id); + } +} + +void Proof::begin_proof (int64_t id) { + LOG (clause, "PROOF begin proof"); + for (auto &tracer : tracers) { + tracer->begin_proof (id); + } +} + +void Proof::solve_query () { + LOG (clause, "PROOF solve query"); + for (auto &tracer : tracers) { + tracer->solve_query (); + } +} + +void Proof::conclude_unsat (ConclusionType con, + const vector<int64_t> &conclusion) { + LOG (clause, "PROOF conclude unsat"); + for (auto &tracer : tracers) { + tracer->conclude_unsat (con, conclusion); + } +} + +void Proof::conclude_sat (const vector<int> &model) { + LOG (clause, "PROOF conclude sat"); + for (auto &tracer : tracers) { + tracer->conclude_sat (model); + } +} + +void Proof::conclude_unknown (const vector<int> &trail) { + LOG (clause, "PROOF conclude unknown"); + for (auto &tracer : tracers) { + tracer->conclude_unknown (trail); + } +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/proof.hpp b/src/sat/cadical/proof.hpp new file mode 100644 index 000000000..36ae2f493 --- /dev/null +++ b/src/sat/cadical/proof.hpp @@ -0,0 +1,114 @@ +#ifndef _proof_h_INCLUDED +#define _proof_h_INCLUDED + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +class File; +struct Clause; +struct Internal; +class Tracer; +class FileTracer; + +/*------------------------------------------------------------------------*/ + +// Provides proof checking and writing. + +class Proof { + + Internal *internal; + + vector<int> clause; // of external literals + vector<int64_t> proof_chain; // LRAT style proof chain of clause + int64_t clause_id; // id of added clause + bool redundant; + + // the 'tracers' + vector<Tracer *> tracers; // tracers (ie checker) + vector<FileTracer *> file_tracers; // file tracers (ie LRAT tracer) + + void add_literal (int internal_lit); // add to 'clause' + void add_literals (Clause *); // add to 'clause' + + void add_literals (const vector<int> &); // ditto + + void add_original_clause ( + bool restore = false); // notify observers of original clauses + void add_derived_clause (); + void add_assumption_clause (); + void delete_clause (); + void demote_clause (); + void weaken_minus (); + void strengthen (); + void finalize_clause (); + void add_assumption (); + void add_constraint (); + +public: + Proof (Internal *); + ~Proof (); + + void connect (Tracer *t) { tracers.push_back (t); } + void disconnect (Tracer *t); + // Add original clauses to the proof (for online proof checking). + // + void add_original_clause (int64_t, bool, const vector<int> &); + + void add_assumption_clause (int64_t, const vector<int> &, + const vector<int64_t> &); + void add_assumption_clause (int64_t, int, const vector<int64_t> &); + void add_assumption (int); + void add_constraint (const vector<int> &); + void reset_assumptions (); + + // Add/delete original clauses to/from the proof using their original + // external literals (from external->eclause) + // + void add_external_original_clause (int64_t, bool, const vector<int> &, + bool restore = false); + void delete_external_original_clause (int64_t, bool, const vector<int> &); + + // Add derived (such as learned) clauses to the proof. + // + void add_derived_empty_clause (int64_t, const vector<int64_t> &); + void add_derived_unit_clause (int64_t, int unit, const vector<int64_t> &); + void add_derived_clause (Clause *c, const vector<int64_t> &); + void add_derived_clause (int64_t, bool, const vector<int> &, + const vector<int64_t> &); + + // deletion of clauses. It comes in several variants, depending if the + // clause should be restored or not + void delete_clause (int64_t, bool, const vector<int> &); + void weaken_minus (int64_t, const vector<int> &); + void weaken_plus (int64_t, const vector<int> &); + void delete_unit_clause (int64_t id, const int lit); + void delete_clause (Clause *); + void weaken_minus (Clause *); + void weaken_plus (Clause *); + void strengthen (int64_t); + + void finalize_unit (int64_t, int); + void finalize_external_unit (int64_t, int); + void finalize_clause (int64_t, const vector<int> &c); + void finalize_clause (Clause *); + + void report_status (int, int64_t); + void begin_proof (int64_t); + void conclude_unsat (ConclusionType, const vector<int64_t> &); + void conclude_sat (const vector<int> &model); + void conclude_unknown (const vector<int> &trace); + void solve_query (); + // These two actually pretend to add and remove a clause. + // + void flush_clause (Clause *); // remove falsified literals + void strengthen_clause (Clause *, int, const vector<int64_t> &); + void otfs_strengthen_clause (Clause *, const vector<int> &, + const vector<int64_t> &); + + void flush (); +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/propagate.cpp b/src/sat/cadical/propagate.cpp new file mode 100644 index 000000000..de3dfbb45 --- /dev/null +++ b/src/sat/cadical/propagate.cpp @@ -0,0 +1,578 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// We are using the address of 'decision_reason' as pseudo reason for +// decisions to distinguish assignment decisions from other assignments. +// Before we added chronological backtracking all learned units were +// assigned at decision level zero ('Solver.level == 0') and we just used a +// zero pointer as reason. After allowing chronological backtracking units +// were also assigned at higher decision level (but with assignment level +// zero), and it was not possible anymore to just distinguish the case +// 'unit' versus 'decision' by just looking at the current level. Both had +// a zero pointer as reason. Now only units have a zero reason and +// decisions need to use the pseudo reason 'decision_reason'. + +// External propagation steps use the pseudo reason 'external_reason'. +// The corresponding actual reason clauses are learned only when they are +// relevant in conflict analysis or in root-level fixing steps. + +static Clause decision_reason_clause; +static Clause *decision_reason = &decision_reason_clause; + +// If chronological backtracking is used the actual assignment level might +// be lower than the current decision level. In this case the assignment +// level is defined as the maximum level of the literals in the reason +// clause except the literal for which the clause is a reason. This +// function determines this assignment level. For non-chronological +// backtracking as in classical CDCL this function always returns the +// current decision level, the concept of assignment level does not make +// sense, and accordingly this function can be skipped. + +// In case of external propagation, it is implicitly assumed that the +// assignment level is the level of the literal (since the reason clause, +// i.e., the set of other literals, is unknown). + +inline int Internal::assignment_level (int lit, Clause *reason) { + + assert (opts.chrono || external_prop); + if (!reason || reason == external_reason) + return level; + + int res = 0; + + for (const auto &other : *reason) { + if (other == lit) + continue; + assert (val (other)); + int tmp = var (other).level; + if (tmp > res) + res = tmp; + } + + return res; +} + +// calculate lrat_chain +// +void Internal::build_chain_for_units (int lit, Clause *reason, + bool forced) { + if (!lrat) + return; + if (opts.chrono && assignment_level (lit, reason) && !forced) + return; + else if (!opts.chrono && level && !forced) + return; // not decision level 0 + assert (lrat_chain.empty ()); + for (auto &reason_lit : *reason) { + if (lit == reason_lit) + continue; + assert (val (reason_lit)); + if (!val (reason_lit)) + continue; + const int signed_reason_lit = val (reason_lit) * reason_lit; + int64_t id = unit_id (signed_reason_lit); + lrat_chain.push_back (id); + } + lrat_chain.push_back (reason->id); +} + +// same code as above but reason is assumed to be conflict and lit is not +// needed +// +void Internal::build_chain_for_empty () { + if (!lrat || !lrat_chain.empty ()) + return; + assert (!level); + assert (lrat_chain.empty ()); + assert (conflict); + LOG (conflict, "lrat for global empty clause with conflict"); + for (auto &lit : *conflict) { + assert (val (lit) < 0); + int64_t id = unit_id (-lit); + lrat_chain.push_back (id); + } + lrat_chain.push_back (conflict->id); +} + +/*------------------------------------------------------------------------*/ + +inline void Internal::search_assign (int lit, Clause *reason) { + + if (level) + require_mode (SEARCH); + + const int idx = vidx (lit); + const bool from_external = reason == external_reason; + assert (!val (idx)); + assert (!flags (idx).eliminated () || reason == decision_reason || + reason == external_reason); + Var &v = var (idx); + int lit_level; + assert (!lrat || level || reason == external_reason || + reason == decision_reason || !lrat_chain.empty ()); + // The following cases are explained in the two comments above before + // 'decision_reason' and 'assignment_level'. + // + // External decision reason means that the propagation was done by + // an external propagation and the reason clause not known (yet). + // In that case it is assumed that the propagation is NOT out of + // order (i.e. lit_level = level), because due to lazy explanation, + // we can not calculate the real assignment level. + // The function assignment_level () will also assign the current level + // to literals with external reason. + if (!reason) + lit_level = 0; // unit + else if (reason == decision_reason) + lit_level = level, reason = 0; + else if (opts.chrono) + lit_level = assignment_level (lit, reason); + else + lit_level = level; + if (!lit_level) + reason = 0; + + v.level = lit_level; + v.trail = trail.size (); + v.reason = reason; + assert ((int) num_assigned < max_var); + assert (num_assigned == trail.size ()); + num_assigned++; + if (!lit_level && !from_external) + learn_unit_clause (lit); // increases 'stats.fixed' + assert (lit_level || !from_external); + const signed char tmp = sign (lit); + set_val (idx, tmp); + assert (val (lit) > 0); // Just a bit paranoid but useful. + assert (val (-lit) < 0); // Ditto. + if (!searching_lucky_phases) + phases.saved[idx] = tmp; // phase saving during search + trail.push_back (lit); +#ifdef LOGGING + if (!lit_level) + LOG ("root-level unit assign %d @ 0", lit); + else + LOG (reason, "search assign %d @ %d", lit, lit_level); +#endif + + if (watching ()) { + const Watches &ws = watches (-lit); + if (!ws.empty ()) { + const Watch &w = ws[0]; + __builtin_prefetch (&w, 0, 1); + } + } + lrat_chain.clear (); +} + +/*------------------------------------------------------------------------*/ + +// External versions of 'search_assign' which are not inlined. They either +// are used to assign unit clauses on the root-level, in 'decide' to assign +// a decision or in 'analyze' to assign the literal 'driven' by a learned +// clause. This happens far less frequently than the 'search_assign' above, +// which is called directly in 'propagate' below and thus is inlined. + +void Internal::assign_unit (int lit) { + assert (!level); + search_assign (lit, 0); +} + +// Just assume the given literal as decision (increase decision level and +// assign it). This is used below in 'decide'. + +void Internal::search_assume_decision (int lit) { + require_mode (SEARCH); + assert (propagated == trail.size ()); + new_trail_level (lit); + notify_decision (); + LOG ("search decide %d", lit); + search_assign (lit, decision_reason); +} + +void Internal::search_assign_driving (int lit, Clause *c) { + require_mode (SEARCH); + search_assign (lit, c); + notify_assignments (); +} + +void Internal::search_assign_external (int lit) { + require_mode (SEARCH); + search_assign (lit, external_reason); + notify_assignments (); +} + +/*------------------------------------------------------------------------*/ + +// The 'propagate' function is usually the hot-spot of a CDCL SAT solver. +// The 'trail' stack saves assigned variables and is used here as BFS queue +// for checking clauses with the negation of assigned variables for being in +// conflict or whether they produce additional assignments. + +// This version of 'propagate' uses lazy watches and keeps two watched +// literals at the beginning of the clause. We also use 'blocking literals' +// to reduce the number of times clauses have to be visited (2008 JSAT paper +// by Chu, Harwood and Stuckey). The watches know if a watched clause is +// binary, in which case it never has to be visited. If a binary clause is +// falsified we continue propagating. + +// Finally, for long clauses we save the position of the last watch +// replacement in 'pos', which in turn reduces certain quadratic accumulated +// propagation costs (2013 JAIR article by Ian Gent) at the expense of four +// more bytes for each clause. + +bool Internal::propagate () { + + if (level) + require_mode (SEARCH); + assert (!unsat); + LOG ("starting propagate"); + START (propagate); + + // Updating statistics counter in the propagation loops is costly so we + // delay until propagation ran to completion. + // + int64_t before = propagated; + int64_t ticks = 0; + + while (!conflict && propagated != trail.size ()) { + + const int lit = -trail[propagated++]; + LOG ("propagating %d", -lit); + Watches &ws = watches (lit); + + const const_watch_iterator eow = ws.end (); + watch_iterator j = ws.begin (); + const_watch_iterator i = j; + ticks += 1 + cache_lines (ws.size (), sizeof *i); + + while (i != eow) { + + const Watch w = *j++ = *i++; + const signed char b = val (w.blit); + LOG (w.clause, "checking"); + + if (b > 0) + continue; // blocking literal satisfied + + if (w.binary ()) { + + // assert (w.clause->redundant || !w.clause->garbage); + + // In principle we can ignore garbage binary clauses too, but that + // would require to dereference the clause pointer all the time with + // + // if (w.clause->garbage) { j--; continue; } // (*) + // + // This is too costly. It is however necessary to produce correct + // proof traces if binary clauses are traced to be deleted ('d ...' + // line) immediately as soon they are marked as garbage. Actually + // finding instances where this happens is pretty difficult (six + // parallel fuzzing jobs in parallel took an hour), but it does + // occur. Our strategy to avoid generating incorrect proofs now is + // to delay tracing the deletion of binary clauses marked as garbage + // until they are really deleted from memory. For large clauses + // this is not necessary since we have to access the clause anyhow. + // + // Thanks go to Mathias Fleury, who wanted me to explain why the + // line '(*)' above was in the code. Removing it actually really + // improved running times and thus I tried to find concrete + // instances where this happens (which I found), and then + // implemented the described fix. + + // Binary clauses are treated separately since they do not require + // to access the clause at all (only during conflict analysis, and + // there also only to simplify the code). + + if (b < 0) + conflict = w.clause; // but continue ... + else { + build_chain_for_units (w.blit, w.clause, 0); + search_assign (w.blit, w.clause); + // lrat_chain.clear (); done in search_assign + ticks++; + } + + } else { + assert (w.clause->size > 2); + + if (conflict) + break; // Stop if there was a binary conflict already. + + // The cache line with the clause data is forced to be loaded here + // and thus this first memory access below is the real hot-spot of + // the solver. Note, that this check is positive very rarely and + // thus branch prediction should be almost perfect here. + + ticks++; + + if (w.clause->garbage) { + j--; + continue; + } + + literal_iterator lits = w.clause->begin (); + + // Simplify code by forcing 'lit' to be the second literal in the + // clause. This goes back to MiniSAT. We use a branch-less version + // for conditionally swapping the first two literals, since it + // turned out to be substantially faster than this one + // + // if (lits[0] == lit) swap (lits[0], lits[1]); + // + // which achieves the same effect, but needs a branch. + // + const int other = lits[0] ^ lits[1] ^ lit; + const signed char u = val (other); // value of the other watch + + if (u > 0) + j[-1].blit = other; // satisfied, just replace blit + else { + + // This follows Ian Gent's (JAIR'13) idea of saving the position + // of the last watch replacement. In essence it needs two copies + // of the default search for a watch replacement (in essence the + // code in the 'if (v < 0) { ... }' block below), one starting at + // the saved position until the end of the clause and then if that + // one failed to find a replacement another one starting at the + // first non-watched literal until the saved position. + + const int size = w.clause->size; + const literal_iterator middle = lits + w.clause->pos; + const const_literal_iterator end = lits + size; + literal_iterator k = middle; + + // Find replacement watch 'r' at position 'k' with value 'v'. + + int r = 0; + signed char v = -1; + + while (k != end && (v = val (r = *k)) < 0) + k++; + + if (v < 0) { // need second search starting at the head? + + k = lits + 2; + assert (w.clause->pos <= size); + while (k != middle && (v = val (r = *k)) < 0) + k++; + } + + w.clause->pos = k - lits; // always save position + + assert (lits + 2 <= k), assert (k <= w.clause->end ()); + + if (v > 0) { + + // Replacement satisfied, so just replace 'blit'. + + j[-1].blit = r; + + } else if (!v) { + + // Found new unassigned replacement literal to be watched. + + LOG (w.clause, "unwatch %d in", lit); + + lits[0] = other; + lits[1] = r; + *k = lit; + + watch_literal (r, lit, w.clause); + + j--; // Drop this watch from the watch list of 'lit'. + + ticks++; + + } else if (!u) { + + assert (v < 0); + + // The other watch is unassigned ('!u') and all other literals + // assigned to false (still 'v < 0'), thus we found a unit. + // + build_chain_for_units (other, w.clause, 0); + search_assign (other, w.clause); + // lrat_chain.clear (); done in search_assign + ticks++; + + // Similar code is in the implementation of the SAT'18 paper on + // chronological backtracking but in our experience, this code + // first does not really seem to be necessary for correctness, + // and further does not improve running time either. + // + if (opts.chrono > 1) { + + const int other_level = var (other).level; + + if (other_level > var (lit).level) { + + // The assignment level of the new unit 'other' is larger + // than the assignment level of 'lit'. Thus we should find + // another literal in the clause at that higher assignment + // level and watch that instead of 'lit'. + + assert (size > 2); + + int pos, s = 0; + + for (pos = 2; pos < size; pos++) + if (var (s = lits[pos]).level == other_level) + break; + + assert (s); + assert (pos < size); + + LOG (w.clause, "unwatch %d in", lit); + lits[pos] = lit; + lits[0] = other; + lits[1] = s; + watch_literal (s, lit, w.clause); + + j--; // Drop this watch from the watch list of 'lit'. + } + } + } else { + + assert (u < 0); + assert (v < 0); + + // The other watch is assigned false ('u < 0') and all other + // literals as well (still 'v < 0'), thus we found a conflict. + + conflict = w.clause; + break; + } + } + } + } + + if (j != i) { + + while (i != eow) + *j++ = *i++; + + ws.resize (j - ws.begin ()); + } + } + + if (searching_lucky_phases) { + + if (conflict) + LOG (conflict, "ignoring lucky conflict"); + + } else { + + // Avoid updating stats eagerly in the hot-spot of the solver. + // + stats.propagations.search += propagated - before; + stats.ticks.search[stable] += ticks; + + if (!conflict) + no_conflict_until = propagated; + else { + + if (stable) + stats.stabconflicts++; + stats.conflicts++; + + LOG (conflict, "conflict"); + + // The trail before the current decision level was conflict free. + // + no_conflict_until = control[level].trail; + } + } + + STOP (propagate); + + return !conflict; +} + +/*------------------------------------------------------------------------*/ + +void Internal::propergate () { + + assert (!conflict); + assert (propagated == trail.size ()); + + while (propergated != trail.size ()) { + + const int lit = -trail[propergated++]; + LOG ("propergating %d", -lit); + Watches &ws = watches (lit); + + const const_watch_iterator eow = ws.end (); + watch_iterator j = ws.begin (); + const_watch_iterator i = j; + + while (i != eow) { + + const Watch w = *j++ = *i++; + + if (w.binary ()) { + assert (val (w.blit) > 0); + continue; + } + if (w.clause->garbage) { + j--; + continue; + } + + literal_iterator lits = w.clause->begin (); + + const int other = lits[0] ^ lits[1] ^ lit; + const signed char u = val (other); + + // TODO: check if u == 0 can happen. + if (u > 0) + continue; + assert (u < 0); + + const int size = w.clause->size; + const literal_iterator middle = lits + w.clause->pos; + const const_literal_iterator end = lits + size; + literal_iterator k = middle; + + int r = 0; + signed char v = -1; + + while (k != end && (v = val (r = *k)) < 0) + k++; + + if (v < 0) { + k = lits + 2; + assert (w.clause->pos <= size); + while (k != middle && (v = val (r = *k)) < 0) + k++; + } + + assert (lits + 2 <= k), assert (k <= w.clause->end ()); + w.clause->pos = k - lits; + + assert (v > 0); + + LOG (w.clause, "unwatch %d in", lit); + + lits[0] = other; + lits[1] = r; + *k = lit; + + watch_literal (r, lit, w.clause); + + j--; + } + + if (j != i) { + + while (i != eow) + *j++ = *i++; + + ws.resize (j - ws.begin ()); + } + } +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/queue.cpp b/src/sat/cadical/queue.cpp new file mode 100644 index 000000000..cd2b19491 --- /dev/null +++ b/src/sat/cadical/queue.cpp @@ -0,0 +1,90 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +// Slightly different than 'bump_variable' since the variable is not +// enqueued at all. + +inline void Internal::init_enqueue (int idx) { + Link &l = links[idx]; + if (opts.reverse) { + l.prev = 0; + if (queue.first) { + assert (!links[queue.first].prev); + links[queue.first].prev = idx; + btab[idx] = btab[queue.first] - 1; + } else { + assert (!queue.last); + queue.last = idx; + btab[idx] = 0; + } + assert (btab[idx] <= stats.bumped); + l.next = queue.first; + queue.first = idx; + if (!queue.unassigned) + update_queue_unassigned (queue.last); + } else { + l.next = 0; + if (queue.last) { + assert (!links[queue.last].next); + links[queue.last].next = idx; + } else { + assert (!queue.first); + queue.first = idx; + } + btab[idx] = ++stats.bumped; + l.prev = queue.last; + queue.last = idx; + update_queue_unassigned (queue.last); + } +} + +// Initialize VMTF queue from current 'old_max_var + 1' to 'new_max_var'. +// This incorporates an initial variable order. We currently simply assume +// that variables with smaller index are more important. This is the same +// as in MiniSAT (implicitly) and also matches the 'scores' initialization. +// +void Internal::init_queue (int old_max_var, int new_max_var) { + LOG ("initializing VMTF queue from %d to %d", old_max_var + 1, + new_max_var); + assert (old_max_var < new_max_var); + // New variables can be created that can invoke enlarge anytime (eg via + // calls during ipasir-up call-backs), thus assuming (!level) is not + // correct + for (int idx = old_max_var; idx < new_max_var; idx++) + init_enqueue (idx + 1); +} + +// Shuffle the VMTF queue. + +void Internal::shuffle_queue () { + if (!opts.shuffle) + return; + if (!opts.shufflequeue) + return; + stats.shuffled++; + LOG ("shuffling queue"); + vector<int> shuffle; + if (opts.shufflerandom) { + for (int idx = max_var; idx; idx--) + shuffle.push_back (idx); + Random random (opts.seed); // global seed + random += stats.shuffled; // different every time + for (int i = 0; i <= max_var - 2; i++) { + const int j = random.pick_int (i, max_var - 1); + swap (shuffle[i], shuffle[j]); + } + } else { + for (int idx = queue.last; idx; idx = links[idx].prev) + shuffle.push_back (idx); + } + queue.first = queue.last = 0; + for (const int idx : shuffle) + queue.enqueue (links, idx); + int64_t bumped = queue.bumped; + for (int idx = queue.last; idx; idx = links[idx].prev) + btab[idx] = bumped--; + queue.unassigned = queue.last; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/queue.hpp b/src/sat/cadical/queue.hpp new file mode 100644 index 000000000..3b98bed17 --- /dev/null +++ b/src/sat/cadical/queue.hpp @@ -0,0 +1,64 @@ +#ifndef _queue_hpp_INCLUDED +#define _queue_hpp_INCLUDED + +#include <vector> + +namespace CaDiCaL { + +// Links for double linked decision queue. + +struct Link { + + int prev, next; // variable indices + + // initialized explicitly in 'init_queue' +}; + +typedef std::vector<Link> Links; + +// Variable move to front (VMTF) decision queue ordered by 'bumped'. See +// our SAT'15 paper for an explanation on how this works. + +struct Queue { + + // We use integers instead of variable pointers. This is more compact and + // also avoids issues due to moving the variable table during 'resize'. + + int first, last; // anchors (head/tail) for doubly linked list + int unassigned; // all variables after this one are assigned + int64_t bumped; // see 'Internal.update_queue_unassigned' + + Queue () : first (0), last (0), unassigned (0), bumped (0) {} + + // We explicitly provide the mapping of integer indices to links to the + // following two (inlined) functions. This avoids including + // 'internal.hpp' and breaks a cyclic dependency, so we can keep their + // code here in this header file. Otherwise they are just ordinary doubly + // linked list 'dequeue' and 'enqueue' operations. + + inline void dequeue (Links &links, int idx) { + Link &l = links[idx]; + if (l.prev) + links[l.prev].next = l.next; + else + first = l.next; + if (l.next) + links[l.next].prev = l.prev; + else + last = l.prev; + } + + inline void enqueue (Links &links, int idx) { + Link &l = links[idx]; + if ((l.prev = last)) + links[last].next = idx; + else + first = idx; + last = idx; + l.next = 0; + } +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/radix.hpp b/src/sat/cadical/radix.hpp new file mode 100644 index 000000000..a28a80aee --- /dev/null +++ b/src/sat/cadical/radix.hpp @@ -0,0 +1,180 @@ +#ifndef _radix_hpp_INCLUDED +#define _radix_hpp_INCLUDED + +#include <cassert> +#include <cstring> +#include <iterator> +#include <vector> + +namespace CaDiCaL { + +using namespace std; + +// This provides an implementation of a generic radix sort algorithm. The +// reason for having it is that for certain benchmarks and certain parts of +// CaDiCaL where sorting is used, the standard sorting algorithm 'sort' +// turned out to be a hot-spot. Up to 30% of the total running time was for +// instance used for some benchmarks in sorting variables during bumping +// to make sure to bump them in 'enqueued' order. +// +// Further, in most cases, where we need to sort something, sorting is +// actually performed on positive numbers (such as the 'enqueued' time stamp +// during bumping), which allows to use radix sort or variants. At least +// starting with medium sized arrays to be sorted (say above 1000 elements, +// but see discussion on 'MSORT' below), radix sort can be way faster. +// +// Finally it is stable, which is actually preferred most of the time too. +// +// This template algorithm 'rsort' takes as first template parameter the +// iterator class similar to the standard 'sort' algorithm template, but +// then as second parameter a function class (similar to the second 'less +// than' parameter of 'sort') which can obtain a 'rank' from each element, +// on which they are compared. The 'rank' should be able to turn an element +// into a number. The type of these ranks is determined automatically but +// should be 'unsigned'. + +struct pointer_rank { + typedef size_t Type; + Type operator() (void *ptr) { return (size_t) ptr; } +}; + +template <class I, class Rank> void rsort (I first, I last, Rank rank) { + typedef typename iterator_traits<I>::value_type T; + typedef typename Rank::Type R; + + assert (first <= last); + const size_t n = last - first; + if (n <= 1) + return; + + const size_t l = 8; // Radix 8, thus byte-wise. + const size_t w = (1 << l); // So many buckets. + + const unsigned mask = w - 1; // Fast mod 'w'. + +// Uncomment the following define for large values of 'w' in order to keep +// the large bucket array 'count' on the heap instead of the stack. +// +// #define CADICAL_RADIX_BUCKETS_ON_THE_HEAP +// +#ifdef CADICAL_RADIX_BUCKETS_ON_THE_HEAP + size_t *count = new size_t[w]; // Put buckets on the heap. +#else + size_t count[w]; // Put buckets on the stack. +#endif + + I a = first, b = last, c = a; + bool initialized = false; + std::vector<T> v; + + R upper = 0, lower = ~upper; + R shifted = mask; + bool bounded = false; + + R masked_lower = 0, masked_upper = mask; + + for (size_t i = 0; i < 8 * sizeof (rank (*first)); + i += l, shifted <<= l) { + + if (bounded && (lower & shifted) == (upper & shifted)) + continue; + + memset (count + masked_lower, 0, + (masked_upper - masked_lower + 1) * sizeof *count); + + const I end = c + n; + bool sorted = true; + R last = 0; + + for (I p = c; p != end; p++) { + const auto r = rank (*p); + if (!bounded) + lower &= r, upper |= r; + const auto s = r >> i; + const auto m = s & mask; + if (sorted && last > m) + sorted = false; + else + last = m; + count[m]++; + } + + masked_lower = (lower >> i) & mask; + masked_upper = (upper >> i) & mask; + + if (!bounded) { + bounded = true; + if ((lower & shifted) == (upper & shifted)) + continue; + } + + if (sorted) + continue; + + size_t pos = 0; + for (R j = masked_lower; j <= masked_upper; j++) { + const size_t delta = count[j]; + count[j] = pos; + pos += delta; + } + + if (!initialized) { + assert (&*c == &*a); // MS VC++ + v.resize (n); + b = v.begin (); + initialized = true; + } + + I d = (&*c == &*a) ? b : a; // MS VC++ + + for (I p = c; p != end; p++) { + const auto r = rank (*p); + const auto s = r >> i; + const auto m = s & mask; + d[count[m]++] = *p; + } + c = d; + } + + if (c == b) { + for (size_t i = 0; i < n; i++) + a[i] = b[i]; + } + +#ifdef CADICAL_RADIX_BUCKETS_ON_THE_HEAP + delete[] count; +#endif + +#ifndef NDEBUG + for (I p = first; p + 1 != last; p++) + assert (rank (p[0]) <= rank (p[1])); +#endif +} + +// It turns out that for small number of elements (like '100') and in +// particular for large value ranges the standard sorting function is +// considerably faster than our radix sort (like 2.5x). This negative effect +// vanishes at around 800 elements (sorting integers) and thus we provide a +// function 'MSORT' which selects between standard sort and radix sort based +// on the number of elements. However we failed to put this into proper C++ +// style template code and thus have to use a macro instead. We also do not +// use it everywhere instead of 'rsort' since it requires a fourth +// parameter, which is awkward, particular in those situation where we +// expect large arrays to be sorted anyhow (such as during sorting the +// clauses in an arena or the probes during probing). The first argument +// is the limit up to which we use the standard sort. Above the limit we +// use radix sort. As usual we do not want to hard code it here (default +// is '800') in order to make fuzzing and delta debugging more effective. + +#define MSORT(LIMIT, FIRST, LAST, RANK, LESS) \ + do { \ + const size_t N = LAST - FIRST; \ + if (N <= (size_t) (LIMIT)) \ + sort (FIRST, LAST, LESS); \ + else \ + rsort (FIRST, LAST, RANK); \ + } while (0) + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/random.cpp b/src/sat/cadical/random.cpp new file mode 100644 index 000000000..b5f02e71e --- /dev/null +++ b/src/sat/cadical/random.cpp @@ -0,0 +1,207 @@ +#include "internal.hpp" + +/*------------------------------------------------------------------------*/ + +// Our random number generator is seeded by default (i.e., in the default +// constructor) with random seeds, which should be unique across machines, +// processes and time. This makes this code below rather operating system +// dependent. We also use in essence defensive programming, overlaying +// several methods to get randomness since in the past we were bitten a +// couple of times (and got the same seeds). Having several methods makes +// it also simpler to port randomly initializing seeds to different +// operating systems (even though currently it is only tested on Linux). +// This functionality is only used in the 'Mobical' model based tester at +// this point, since the main solver explicitly sets a random seed ('0' by +// default in 'options.hpp') and also currently only uses this seed in the +// local search procedure explicitly without using the default constructor. +// It is crucial for 'Mobical' to make sure that concurrent runs are really +// independent. + +/*------------------------------------------------------------------------*/ + +// Uncomment the following definition to force printing the computed hash +// values for individual machine and process properties. This is only needed +// for testing, porting and debugging different ports of this seeding and +// hashing functions (uncomment and run 'mobical' for instance). + +/* +#define DO_PRINT_HASH +*/ + +#ifdef DO_PRINT_HASH +#define PRINT_HASH(H) \ + do { \ + printf ("c PRINT_HASH %32s () = %020" PRIu64 "\n", __func__, H); \ + fflush (stdout); \ + } while (0) +#else +#define PRINT_HASH(...) \ + do { \ + } while (0) +#endif + +/*------------------------------------------------------------------------*/ + +// This is Linux specific but if '/var/lib/dbus/machine-id' does not exist +// does not have any effect. TODO: add a similar machine identity hashing +// function for other operating systems (Windows and macOS). + +namespace CaDiCaL { + +static uint64_t hash_machine_identifier () { + FILE *file = fopen ("/var/lib/dbus/machine-id", "r"); + uint64_t res = 0; + if (file) { + char buffer[128]; + memset (buffer, 0, sizeof buffer); + size_t bytes = fread (buffer, 1, sizeof buffer - 1, file); + assert (bytes); + fclose (file); + if (bytes && bytes < sizeof buffer) { + buffer[bytes] = 0; + res = hash_string (buffer); + } + } + PRINT_HASH (res); + return res; +} + +} // namespace CaDiCaL + +/*------------------------------------------------------------------------*/ + +// On our Linux cluster where we used an NFS mounted root disk the +// 'machine-id' above (even on a locally mounted '/var' disk on each node) +// was copied from '/etc/machine-id' which was shared among all nodes +// (before figuring this out and fixing it). Thus the main idea of getting +// different hash values through this machine identifier machines did not +// work. As an additional measure to increase the possibility to get +// different seeds we are now also using network addresses (explicitly). + +#ifndef __WIN32 + +extern "C" { +#include <ifaddrs.h> +#include <netdb.h> +#include <netinet/in.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/socket.h> +} + +#endif + +namespace CaDiCaL { + +static uint64_t hash_network_addresses () { + uint64_t res = 0; + + // We still need to properly port this to Windows, but since accessing the + // IP address is only required for better randomization during testing + // (running 'mobical' on a cluster for instance) it is not crucial unless + // you really need to run 'mobical' on a Windows cluster where each node + // has identical IP addresses. + +#ifndef __WIN32 + struct ifaddrs *addrs; + if (!getifaddrs (&addrs)) { + for (struct ifaddrs *addr = addrs; addr; addr = addr->ifa_next) { + if (!addr->ifa_addr) + continue; + const int family = addr->ifa_addr->sa_family; + if (family == AF_INET || family == AF_INET6) { + const int size = (family == AF_INET) ? sizeof (struct sockaddr_in) + : sizeof (struct sockaddr_in6); + char buffer[128]; + if (!getnameinfo (addr->ifa_addr, size, buffer, sizeof buffer, 0, 0, + NI_NUMERICHOST)) { + uint64_t tmp = hash_string (buffer); +#ifdef DO_PRINT_HASH + printf ("c PRINT_HASH %35s = %020" PRIu64 "\n", buffer, tmp); + fflush (stdout); +#endif + res ^= tmp; + res *= 10000000000000000051ul; + } + } + } + freeifaddrs (addrs); + } +#endif + + PRINT_HASH (res); + return res; +} + +} // namespace CaDiCaL + +/*------------------------------------------------------------------------*/ + +// Hash the current wall-clock time in seconds. + +extern "C" { +#include <time.h> +} + +namespace CaDiCaL { + +static uint64_t hash_time () { + uint64_t res = ::time (0); + PRINT_HASH (res); + return res; +} + +} // namespace CaDiCaL + +/*------------------------------------------------------------------------*/ + +// Hash the process identified. + +extern "C" { +#include <sys/types.h> +#include <unistd.h> +} + +namespace CaDiCaL { + +static uint64_t hash_process () { + uint64_t res = getpid (); + PRINT_HASH (res); + return res; +} + +} // namespace CaDiCaL + +/*------------------------------------------------------------------------*/ + +// Hash the current number of clock cycles. + +#include <ctime> + +namespace CaDiCaL { + +static uint64_t hash_clock_cycles () { + uint64_t res = std::clock (); + PRINT_HASH (res); + return res; +} + +} // namespace CaDiCaL + +/*------------------------------------------------------------------------*/ + +namespace CaDiCaL { + +Random::Random () : state (1) { + add (hash_machine_identifier ()); + add (hash_network_addresses ()); + add (hash_clock_cycles ()); + add (hash_process ()); + add (hash_time ()); +#ifdef DO_PRINT_HASH + printf ("c PRINT_HASH %32s = %020" PRIu64 "\n", "combined", state); + fflush (stdout); +#endif +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/random.h b/src/sat/cadical/random.h new file mode 100644 index 000000000..7f00fbf99 --- /dev/null +++ b/src/sat/cadical/random.h @@ -0,0 +1,44 @@ +#ifndef _random_h_INCLUDED +#define _random_h_INCLUDED + +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> + +typedef uint64_t generator; + +static inline uint64_t kissat_next_random64 (generator *rng) { + *rng *= 6364136223846793005ul; + *rng += 1442695040888963407ul; + return *rng; +} + +static inline unsigned kissat_next_random32 (generator *rng) { + return kissat_next_random64 (rng) >> 32; +} + +static inline unsigned kissat_pick_random (generator *rng, unsigned l, + unsigned r) { + assert (l <= r); + if (l == r) + return l; + const unsigned delta = r - l; + const unsigned tmp = kissat_next_random32 (rng); + const double fraction = tmp / 4294967296.0; + assert (0 <= fraction), assert (fraction < 1); + const unsigned scaled = delta * fraction; + assert (scaled < delta); + const unsigned res = l + scaled; + assert (l <= res), assert (res < r); + return res; +} + +static inline bool kissat_pick_bool (generator *rng) { + return kissat_pick_random (rng, 0, 2); +} + +static inline double kissat_pick_double (generator *rng) { + return kissat_next_random32 (rng) / 4294967296.0; +} + +#endif diff --git a/src/sat/cadical/random.hpp b/src/sat/cadical/random.hpp new file mode 100644 index 000000000..c57c03bef --- /dev/null +++ b/src/sat/cadical/random.hpp @@ -0,0 +1,98 @@ +#ifndef _random_hpp_INCLUDED +#define _random_hpp_INCLUDED + +#include <cstdint> + +// Random number generator. + +namespace CaDiCaL { + +class Random { + + uint64_t state; + + void add (uint64_t a) { + if (!(state += a)) + state = 1; + next (); + } + +public: + // Without argument use a machine, process and time dependent seed. + // + Random (); + + Random (uint64_t seed) : state (seed) {} + void operator= (uint64_t seed) { state = seed; } + Random (const Random &other) : state (other.seed ()) {} + + void operator+= (uint64_t a) { add (a); } + uint64_t seed () const { return state; } + + uint64_t next () { + state *= 6364136223846793005ul; + state += 1442695040888963407ul; + assert (state); + return state; + } + + uint32_t generate () { + next (); + return state >> 32; + } + int generate_int () { return (int) generate (); } + bool generate_bool () { return generate () < 2147483648u; } + + // Generate 'double' value in the range '[0,1]' excluding '1'. + // + double generate_double () { return generate () / 4294967295.0; } + + // Generate 'int' value in the range '[l,r]'. + // + int pick_int (int l, int r) { + assert (l <= r); + const unsigned delta = 1 + r - (unsigned) l; + unsigned tmp = generate (), scaled; + if (delta) { + const double fraction = tmp / 4294967296.0; + scaled = delta * fraction; + } else + scaled = tmp; + const int res = scaled + l; + assert (l <= res); + assert (res <= r); + return res; + } + + int pick_log (int l, int r) { + assert (l <= r); + const unsigned delta = 1 + r - (unsigned) l; + int log_delta = delta ? 0 : 32; + while (log_delta < 32 && (1u << log_delta) < delta) + log_delta++; + const int log_res = pick_int (0, log_delta); + unsigned tmp = generate (); + if (log_res < 32) + tmp &= (1u << log_res) - 1; + if (delta) + tmp %= delta; + const int res = l + tmp; + assert (l <= res), assert (res <= r); + return res; + } + + // Generate 'double' value in the range '[l,r]'. + // + double pick_double (double l, double r) { + assert (l <= r); + double res = (r - l) * generate_double (); + res += l; + assert (l <= res); + assert (res <= r); + return res; + } +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/range.hpp b/src/sat/cadical/range.hpp new file mode 100644 index 000000000..f30c34d4b --- /dev/null +++ b/src/sat/cadical/range.hpp @@ -0,0 +1,99 @@ +#ifndef _range_hpp_INCLUDED +#define _range_hpp_INCLUDED + +#include <cassert> + +namespace CaDiCaL { + +struct Clause; + +/*----------------------------------------------------------------------*/ + +// Used for compact and safe iteration over positive ranges of integers, +// particularly for iterating over all variable indices. +// +// Range vars (max_var); +// for (auto idx : vars) ... +// +// This iterates over '1, ..., max_var' and is safe for non-negative +// numbers, thus also for 'max_var == 0' or 'max_var == INT_MAX'. +// +// Note that +// +// for (int idx = 1; idx <= max_var; idx++) ... +// +// leads to an overflow if 'max_var == INT_MAX' and thus depending on what +// the compiler does ('int' overflow is undefined) might lead to any +// behaviour (infinite loop or worse array access way out of bounds). +// +// If we make 'idx' in this last 'for' loop an 'unsigned' then it is safe to +// use this idiom, but we would need to cast 'max_var' explicitly to 'int' +// in order to avoid a warning in the loop condition and actually everywhere +// where 'idx' is compared to a 'signed' expression. Worse for instance +// 'vals[-idx]' will lead to out of bounds access too. This is awkward and +// using the range iterator provided here is safer in general. +// +// Another issue is that the dereferencing operator '*' below is required to +// return a reference to the internal index of the iterator. Thus the 'idx' +// in the auto loop is actually of the same type as the internal state of +// the iterator. To keep it 'signed' and still avoid overflow issues we +// just have to make sure to use the proper increment (with two implicit +// casts, i.e., from 'int' to 'unsigned', then 'unsigned' addition and the +// result is cast back from 'unsigned' to 'int'). +// +// For simplicity we keep a reference to the actual maximum integer, e.g., +// 'max_var', which makes the idiom 'for (auto idx : vars) ...' possible. +// Further note that the referenced integer has to be non-negative before +// starting to iterate (it can be zero though), otherwise it breaks. + +class Range { + static unsigned inc (unsigned u) { return u + 1u; } + class iterator { + int idx; + + public: + iterator (int i) : idx (i) {} + void operator++ () { idx = inc (idx); } + const int &operator* () const { return idx; } + friend bool operator!= (const iterator &a, const iterator &b) { + return a.idx != b.idx; + } + }; + int &n; + +public: + iterator begin () const { return assert (n >= 0), iterator (inc (0)); } + iterator end () const { return assert (n >= 0), iterator (inc (n)); } + Range (int &m) : n (m) { assert (m >= 0); } +}; + +// Same, but iterating over literals '-1,1,-2,2,....,-max_var,max_var'. +// +// The only difference to 'Range' is the 'inc' function, but I am too lazy +// to figure out how to properly factor the code into a generic range +// template with 'inc' as only parameter. This gives at least clean code. + +class Sange { + static unsigned inc (unsigned u) { return ~u + (u >> 31); } + class iterator { + int lit; + + public: + iterator (int i) : lit (i) {} + void operator++ () { lit = inc (lit); } + const int &operator* () const { return lit; } + friend bool operator!= (const iterator &a, const iterator &b) { + return a.lit != b.lit; + } + }; + int &n; + +public: + iterator begin () const { return assert (n >= 0), iterator (inc (0)); } + iterator end () const { return assert (n >= 0), iterator (inc (n)); } + Sange (int &m) : n (m) { assert (m >= 0); } +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/reap.cpp b/src/sat/cadical/reap.cpp new file mode 100644 index 000000000..8c383cb66 --- /dev/null +++ b/src/sat/cadical/reap.cpp @@ -0,0 +1,127 @@ +#include "reap.hpp" +#include <cassert> +#include <climits> +#include <cstring> + +void Reap::init () { + for (auto &bucket : buckets) + bucket = {0}; + assert (!num_elements); + assert (!last_deleted); + min_bucket = 32; + assert (!max_bucket); +} + +void Reap::release () { + num_elements = 0; + last_deleted = 0; + min_bucket = 32; + max_bucket = 0; +} + +Reap::Reap () { + num_elements = 0; + last_deleted = 0; + min_bucket = 32; + max_bucket = 0; +} + +static inline unsigned leading_zeroes_of_unsigned (unsigned x) { + return x ? __builtin_clz (x) : sizeof (unsigned) * 8; +} + +void Reap::push (unsigned e) { + assert (last_deleted <= e); + const unsigned diff = e ^ last_deleted; + const unsigned bucket = 32 - leading_zeroes_of_unsigned (diff); + buckets[bucket].push_back (e); + if (min_bucket > bucket) + min_bucket = bucket; + if (max_bucket < bucket) + max_bucket = bucket; + assert (num_elements != UINT_MAX); + num_elements++; +} + +unsigned Reap::pop () { + assert (num_elements > 0); + unsigned i = min_bucket; + for (;;) { + assert (i < 33); + assert (i <= max_bucket); + std::vector<unsigned> &s = buckets[i]; + if (s.empty ()) { + min_bucket = ++i; + continue; + } + unsigned res; + if (i) { + res = UINT_MAX; + const auto begin = std::begin (s); + const auto end = std::end (s); + auto q = std::begin (s); + assert (begin < end); + for (auto p = begin; p != end; ++p) { + const unsigned tmp = *p; + if (tmp >= res) + continue; + res = tmp; + q = p; + } + + for (auto p = begin; p != end; ++p) { + if (p == q) + continue; + const unsigned other = *p; + const unsigned diff = other ^ res; + assert (sizeof (unsigned) == 4); + const unsigned j = 32 - leading_zeroes_of_unsigned (diff); + assert (j < i); + buckets[j].push_back (other); + if (min_bucket > j) + min_bucket = j; + } + + s.clear (); + + if (i && max_bucket == i) { +#ifndef NDEBUG + for (unsigned j = i + 1; j < 33; j++) + assert (buckets[j].empty ()); +#endif + if (s.empty ()) + max_bucket = i - 1; + } + } else { + res = last_deleted; + assert (!buckets[0].empty ()); + assert (buckets[0].at (0) == res); + buckets[0].pop_back (); + } + + if (min_bucket == i) { +#ifndef NDEBUG + for (unsigned j = 0; j < i; j++) + assert (buckets[j].empty ()); +#endif + if (s.empty ()) + min_bucket = std::min ((int) (i + 1), 32); + } + + --num_elements; + assert (last_deleted <= res); + last_deleted = res; + + return res; + } +} + +void Reap::clear () { + assert (max_bucket <= 32); + for (unsigned i = 0; i < 33; i++) + buckets[i].clear (); + num_elements = 0; + last_deleted = 0; + min_bucket = 32; + max_bucket = 0; +} diff --git a/src/sat/cadical/reap.hpp b/src/sat/cadical/reap.hpp new file mode 100644 index 000000000..9986b95b9 --- /dev/null +++ b/src/sat/cadical/reap.hpp @@ -0,0 +1,28 @@ +#ifndef _reap_h_INCLUDED +#define _reap_h_INCLUDED + +#include <cstddef> +#include <vector> + +class Reap { +public: + Reap (); + void init (); + void release (); + inline bool empty () { return !num_elements; } + + inline size_t size () { return num_elements; } + + void push (unsigned); + void clear (); + unsigned pop (); + +private: + size_t num_elements; + unsigned last_deleted; + unsigned min_bucket; + unsigned max_bucket; + std::vector<unsigned> buckets[33]; +}; + +#endif diff --git a/src/sat/cadical/reduce.cpp b/src/sat/cadical/reduce.cpp new file mode 100644 index 000000000..632db3673 --- /dev/null +++ b/src/sat/cadical/reduce.cpp @@ -0,0 +1,278 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Once in a while we reduce, e.g., we remove learned clauses which are +// supposed to be less useful in the future. This is done in increasing +// intervals, which has the effect of allowing more and more learned clause +// to be kept for a longer period. The number of learned clauses kept +// in memory corresponds to an upper bound on the 'space' of a resolution +// proof needed to refute a formula in proof complexity sense. + +bool Internal::reducing () { + if (!opts.reduce) + return false; + if (!stats.current.redundant) + return false; + return stats.conflicts >= lim.reduce; +} + +/*------------------------------------------------------------------------*/ + +// Even less regularly we are flushing all redundant clauses. + +bool Internal::flushing () { + if (!opts.flush) + return false; + return stats.conflicts >= lim.flush; +} + +/*------------------------------------------------------------------------*/ + +void Internal::mark_clauses_to_be_flushed () { + const int tier1limit = tier1[false]; + const int tier2limit = max (tier1limit, tier2[false]); + for (const auto &c : clauses) { + if (!c->redundant) + continue; // keep irredundant + if (c->garbage) + continue; // already marked as garbage + if (c->reason) + continue; // need to keep reasons + const unsigned used = c->used; + if (used) + c->used--; + if (c->glue < tier1limit && used) + continue; + if (c->glue < tier2limit && used >= max_used - 1) + continue; + mark_garbage (c); // flush unused clauses + if (c->hyper) + stats.flush.hyper++; + else + stats.flush.learned++; + } + // No change to 'lim.kept{size,glue}'. +} + +/*------------------------------------------------------------------------*/ + +// Clauses of larger glue or larger size are considered less useful. +// +// We also follow the observations made by the Glucose team in their +// IJCAI'09 paper and keep all low glue clauses limited by +// 'options.keepglue' (typically '2'). +// +// In earlier versions we pre-computed a 64-bit sort key per clause and +// wrapped a pointer to the clause and the 64-bit sort key into a separate +// data structure for sorting. This was probably faster but awkward and +// so we moved back to a simpler scheme which also uses 'stable_sort' +// instead of 'rsort' below. Sorting here is not a hot-spot anyhow. + +struct reduce_less_useful { + bool operator() (const Clause *c, const Clause *d) const { + if (c->glue > d->glue) + return true; + if (c->glue < d->glue) + return false; + return c->size > d->size; + } +}; + +// This function implements the important reduction policy. It determines +// which redundant clauses are considered not useful and thus will be +// collected in a subsequent garbage collection phase. + +void Internal::mark_useless_redundant_clauses_as_garbage () { + + // We use a separate stack for sorting candidates for removal. This uses + // (slightly) more memory but has the advantage to keep the relative order + // in 'clauses' intact, which actually due to using stable sorting goes + // into the candidate selection (more recently learned clauses are kept if + // they otherwise have the same glue and size). + + vector<Clause *> stack; + const int tier1limit = tier1[false]; + const int tier2limit = max (tier1limit, tier2[false]); + + stack.reserve (stats.current.redundant); + + for (const auto &c : clauses) { + if (!c->redundant) + continue; // Keep irredundant. + if (c->garbage) + continue; // Skip already marked. + if (c->reason) + continue; // Need to keep reasons. + const unsigned used = c->used; + if (used) + c->used--; + if (c->glue <= tier1limit && used) + continue; + if (c->glue <= tier2limit && used >= max_used - 1) + continue; + if (c->hyper) { // Hyper binary and ternary resolvents + assert (c->size <= 3); // are only kept for one reduce round + if (!used) + mark_garbage (c); // unless + continue; // used recently. + } + stack.push_back (c); + } + + stable_sort (stack.begin (), stack.end (), reduce_less_useful ()); + size_t target = 1e-2 * opts.reducetarget * stack.size (); + + // This is defensive code, which I usually consider a bug, but here I am + // just not sure that using floating points in the line above is precise + // in all situations and instead of figuring that out, I just use this. + // + if (target > stack.size ()) + target = stack.size (); + + PHASE ("reduce", stats.reductions, "reducing %zd clauses %.0f%%", target, + percent (target, stats.current.redundant)); + + auto i = stack.begin (); + const auto t = i + target; + while (i != t) { + Clause *c = *i++; + LOG (c, "marking useless to be collected"); + mark_garbage (c); + stats.reduced++; + } + + lim.keptsize = lim.keptglue = 0; + + const auto end = stack.end (); + for (i = t; i != end; i++) { + Clause *c = *i; + LOG (c, "keeping"); + if (c->size > lim.keptsize) + lim.keptsize = c->size; + if (c->glue > lim.keptglue) + lim.keptglue = c->glue; + } + + erase_vector (stack); + + PHASE ("reduce", stats.reductions, "maximum kept size %d glue %d", + lim.keptsize, lim.keptglue); +} + +/*------------------------------------------------------------------------*/ + +// If chronological backtracking produces out-of-order assigned units, then +// it is necessary to completely propagate them at the root level in order +// to derive all implied units. Otherwise the blocking literals in +// 'flush_watches' are messed up and assertion 'FW1' fails. + +bool Internal::propagate_out_of_order_units () { + if (!level) + return true; + int oou = 0; + for (size_t i = control[1].trail; !oou && i < trail.size (); i++) { + const int lit = trail[i]; + assert (val (lit) > 0); + if (var (lit).level) + continue; + LOG ("found out-of-order assigned unit %d", oou); + oou = lit; + } + if (!oou) + return true; + assert (opts.chrono || external_prop); + backtrack (0); + if (propagate ()) + return true; + learn_empty_clause (); + return false; +} + +/*------------------------------------------------------------------------*/ + +// reduction is scheduled with reduceint, reducetarget and reduceopt. +// with reduceopt=1 the number of learnt clauses scale with +// sqrt of conflicts times reduceint +// the scaling is the same as with reduceopt=0 (the classical default) +// however, the constants are different. To avoid this (and get roughly the +// same behaviour with reduceopt=0 and reduceopt=1) we need to scale the +// interval, namely (reduceint^2/2) +// Lastly, reduceopt=2 just replaces sqrt conflicts with log conflicts. +// The learnt clauses should not be bigger than +// 1/reducetarget * reduceint * function (conflicts) +// for function being log if reduceint=2 an sqrt otherwise. +// This is however only the theoretical target and second chance for +// tier2 clauses and very long lifespan of tier1 clauses (through used flag) +// make this behave differently. +// reduceinit shifts the curve to the right, increasing the number of +// clauses in the solver. This impact will decrease over time. + +void Internal::reduce () { + START (reduce); + + stats.reductions++; + report ('.', 1); + + bool flush = flushing (); + if (flush) + stats.flush.count++; + + if (!propagate_out_of_order_units ()) + goto DONE; + + mark_satisfied_clauses_as_garbage (); + protect_reasons (); + if (flush) + mark_clauses_to_be_flushed (); + else + mark_useless_redundant_clauses_as_garbage (); + garbage_collection (); + + { + int64_t delta = opts.reduceint; + double factor = stats.reductions + 1; + if (opts.reduceopt == + 0) // adjust delta such this is the same as reduceopt=1 + delta = delta * delta / 2; + else if (opts.reduceopt == 1) { + // this is the same as reduceopt=0 if reduceint = sqrt (reduceint) = + // 17 + factor = sqrt ((double) stats.conflicts); + } else if (opts.reduceopt == 2) + // log scaling instead + factor = log ((double) stats.conflicts); + if (factor < 1) + factor = 1; + delta = delta * factor; + if (irredundant () > 1e5) { + delta *= log (irredundant () / 1e4) / log (10); + } + if (delta < 1) + delta = 1; + lim.reduce = stats.conflicts + delta; + PHASE ("reduce", stats.reductions, + "new reduce limit %" PRId64 " after %" PRId64 " conflicts", + lim.reduce, delta); + } + + if (flush) { + PHASE ("flush", stats.flush.count, "new flush increment %" PRId64 "", + inc.flush); + inc.flush *= opts.flushfactor; + lim.flush = stats.conflicts + inc.flush; + PHASE ("flush", stats.flush.count, "new flush limit %" PRId64 "", + lim.flush); + } + + last.reduce.conflicts = stats.conflicts; + +DONE: + + report (flush ? 'f' : '-'); + STOP (reduce); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/reluctant.hpp b/src/sat/cadical/reluctant.hpp new file mode 100644 index 000000000..0448672e4 --- /dev/null +++ b/src/sat/cadical/reluctant.hpp @@ -0,0 +1,82 @@ +#ifndef _reluctant_hpp_INCLUDED +#define _reluctant_hpp_INCLUDED + +#include <cassert> +#include <cstdint> + +namespace CaDiCaL { + +// This is Donald Knuth's version of the Luby restart sequence which he +// called 'reluctant doubling'. His bit-twiddling formulation in line (DK) +// requires to keep two words around which are updated every time the +// reluctant doubling sequence is advanced. The original version in the +// literature uses a complex recursive function which computes the length of +// the next inactive sub-sequence every time (but is state-less). +// +// In our code we incorporate a base interval 'period' and only after period +// many calls to 'tick' times the current sequence value we update the +// reluctant doubling sequence value. The 'tick' call is decoupled from +// the activation signal of the sequence (the 'bool ()' operator) through +// 'trigger'. It is also possible to set an upper limit to the length of an +// inactive sub-sequence. If that limit is reached the whole reluctant +// doubling sequence starts over with the initial values. + +class Reluctant { + + uint64_t u, v, limit; + uint64_t period, countdown; + bool trigger, limited; + +public: + Reluctant () : period (0), trigger (false) {} + + void enable (int p, int64_t l) { + assert (p > 0); + u = v = 1; + period = countdown = p; + trigger = false; + if (l <= 0) + limited = false; + else + limited = true, limit = l; + }; + + void disable () { period = 0, trigger = false; } + + // Increments the count until the 'period' is hit. Then it performs the + // actual increment of reluctant doubling. This gives the common 'Luby' + // sequence with the specified base interval period. As soon the limit is + // reached (countdown goes to zero) we remember this event and then + // disable updating the reluctant sequence until the signal is delivered. + + void tick () { + + if (!period) + return; // disabled + if (trigger) + return; // already triggered + if (--countdown) + return; // not there yet + + if ((u & -u) == v) + u = u + 1, v = 1; + else + v = 2 * v; // (DK) + + if (limited && v >= limit) + u = v = 1; + countdown = v * period; + trigger = true; + } + + operator bool () { + if (!trigger) + return false; + trigger = false; + return true; + } +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/rephase.cpp b/src/sat/cadical/rephase.cpp new file mode 100644 index 000000000..74ebb988e --- /dev/null +++ b/src/sat/cadical/rephase.cpp @@ -0,0 +1,336 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// We experimented with resetting and reinitializing the saved phase with +// many solvers. Actually RSAT had already such a scheme. Our newest +// version seems to be quite beneficial for satisfiable instances. In an +// arithmetic increasing interval in the number of conflicts we either use +// the original phase (set by the option 'phase'), its inverted value, flip +// the current phase, pick random phases, then pick the best since the last +// time a best phase was picked and finally also use local search to find +// phases which minimize the number of falsified clauses. During +// stabilization (see 'stabilizing' in 'restart.cpp' when 'stable' is true) +// we execute a different rephasing schedule. The same applies if local +// search is disabled. + +/*------------------------------------------------------------------------*/ + +bool Internal::rephasing () { + if (!opts.rephase) + return false; + if (opts.forcephase) + return false; + return stats.conflicts > lim.rephase; +} + +/*------------------------------------------------------------------------*/ + +// Pick the original default phase. + +char Internal::rephase_original () { + stats.rephased.original++; + signed char val = opts.phase ? 1 : -1; // original = initial + PHASE ("rephase", stats.rephased.total, "switching to original phase %d", + val); + for (auto idx : vars) + phases.saved[idx] = val; + return 'O'; +} + +// Pick the inverted original default phase. + +char Internal::rephase_inverted () { + stats.rephased.inverted++; + signed char val = opts.phase ? -1 : 1; // original = -initial + PHASE ("rephase", stats.rephased.total, + "switching to inverted original phase %d", val); + for (auto idx : vars) + phases.saved[idx] = val; + return 'I'; +} + +// Flip the current phase. + +char Internal::rephase_flipping () { + stats.rephased.flipped++; + PHASE ("rephase", stats.rephased.total, + "flipping all phases individually"); + for (auto idx : vars) + phases.saved[idx] *= -1; + return 'F'; +} + +// Complete random picking of phases. + +char Internal::rephase_random () { + stats.rephased.random++; + PHASE ("rephase", stats.rephased.total, "resetting all phases randomly"); + Random random (opts.seed); // global seed + random += stats.rephased.random; // different every time + for (auto idx : vars) + phases.saved[idx] = random.generate_bool () ? -1 : 1; + return '#'; +} + +// Best phases are those saved at the largest trail height without conflict. +// See code and comments in 'update_target_and_best' in 'backtrack.cpp' + +char Internal::rephase_best () { + stats.rephased.best++; + PHASE ("rephase", stats.rephased.total, + "overwriting saved phases by best phases"); + signed char val; + for (auto idx : vars) + if ((val = phases.best[idx])) + phases.saved[idx] = val; + return 'B'; +} + +// Trigger local search 'walk' in 'walk.cpp'. + +char Internal::rephase_walk () { + stats.rephased.walk++; + PHASE ("rephase", stats.rephased.total, + "starting local search to improve current phase"); + walk (); + return 'W'; +} + +/*------------------------------------------------------------------------*/ + +void Internal::rephase () { + + stats.rephased.total++; + PHASE ("rephase", stats.rephased.total, + "reached rephase limit %" PRId64 " after %" PRId64 " conflicts", + lim.rephase, stats.conflicts); + + // Report current 'target' and 'best' and then set 'rephased' below, which + // will trigger reporting the new 'target' and 'best' after updating it in + // the next 'update_target_and_best' called from the next 'backtrack'. + // + report ('~', 1); + + backtrack (); + clear_phases (phases.target); + target_assigned = 0; + + size_t count = lim.rephased[stable]++; + bool single; + char type; + + if (opts.stabilize && opts.stabilizeonly) + single = true; + else + single = !opts.stabilize; + + if (single && !opts.walk) { + // (inverted,best,flipping,best,random,best,original,best)^\omega + switch (count % 8) { + case 0: + type = rephase_inverted (); + break; + case 1: + type = rephase_best (); + break; + case 2: + type = rephase_flipping (); + break; + case 3: + type = rephase_best (); + break; + case 4: + type = rephase_random (); + break; + case 5: + type = rephase_best (); + break; + case 6: + type = rephase_original (); + break; + case 7: + type = rephase_best (); + break; + default: + type = 0; + break; + } + } else if (single && opts.walk) { + // (inverted,best,walk, + // flipping,best,walk, + // random,best,walk, + // original,best,walk)^\omega + switch (count % 12) { + case 0: + type = rephase_inverted (); + break; + case 1: + type = rephase_best (); + break; + case 2: + type = rephase_walk (); + break; + case 3: + type = rephase_flipping (); + break; + case 4: + type = rephase_best (); + break; + case 5: + type = rephase_walk (); + break; + case 6: + type = rephase_random (); + break; + case 7: + type = rephase_best (); + break; + case 8: + type = rephase_walk (); + break; + case 9: + type = rephase_original (); + break; + case 10: + type = rephase_best (); + break; + case 11: + type = rephase_walk (); + break; + default: + type = 0; + break; + } + } else if (stable && !opts.walk) { + // original,inverted,(best,original,best,inverted)^\omega + if (!count) + type = rephase_original (); + else if (count == 1) + type = rephase_inverted (); + else + switch ((count - 2) % 4) { + case 0: + type = rephase_best (); + break; + case 1: + type = rephase_original (); + break; + case 2: + type = rephase_best (); + break; + case 3: + type = rephase_inverted (); + break; + default: + type = 0; + break; + } + } else if (stable && opts.walk) { + // original,inverted,(best,walk,original,best,walk,inverted)^\omega + if (!count) + type = rephase_original (); + else if (count == 1) + type = rephase_inverted (); + else + switch ((count - 2) % 6) { + case 0: + type = rephase_best (); + break; + case 1: + type = rephase_walk (); + break; + case 2: + type = rephase_original (); + break; + case 3: + type = rephase_best (); + break; + case 4: + type = rephase_walk (); + break; + case 5: + type = rephase_inverted (); + break; + default: + type = 0; + break; + } + } else if (!stable && (!opts.walk || !opts.walknonstable)) { + // flipping,(random,best,flipping,best)^\omega + if (!count) + type = rephase_flipping (); + else + switch ((count - 1) % 4) { + case 0: + type = rephase_random (); + break; + case 1: + type = rephase_best (); + break; + case 2: + type = rephase_flipping (); + break; + case 3: + type = rephase_best (); + break; + default: + type = 0; + break; + } + } else { + assert (!stable && opts.walk && opts.walknonstable); + // flipping,(random,best,walk,flipping,best,walk)^\omega + if (!count) + type = rephase_flipping (); + else + switch ((count - 1) % 6) { + case 0: + type = rephase_random (); + break; + case 1: + type = rephase_best (); + break; + case 2: + type = rephase_walk (); + break; + case 3: + type = rephase_flipping (); + break; + case 4: + type = rephase_best (); + break; + case 5: + type = rephase_walk (); + break; + default: + type = 0; + break; + } + } + assert (type); + + int64_t delta = opts.rephaseint * (stats.rephased.total + 1); + lim.rephase = stats.conflicts + delta; + + PHASE ("rephase", stats.rephased.total, + "new rephase limit %" PRId64 " after %" PRId64 " conflicts", + lim.rephase, delta); + + // This will trigger to report the effect of this new set of phases at the + // 'backtrack' (actually 'update_target_and_best') after the next + // conflict, as well as resetting 'best_assigned' then to allow to compute + // a new "best" assignment at that point. + // + last.rephase.conflicts = stats.conflicts; + rephased = type; + + if (stable) + shuffle_scores (); + else + shuffle_queue (); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/report.cpp b/src/sat/cadical/report.cpp new file mode 100644 index 000000000..cda252edb --- /dev/null +++ b/src/sat/cadical/report.cpp @@ -0,0 +1,296 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +#ifndef QUIET + +/*------------------------------------------------------------------------*/ + +// Provide nicely formatted progress report messages while running through +// the 'report' function below. The code is so complex, because it should +// be easy to add and remove reporting of certain statistics, while still +// providing a nicely looking format with automatically aligned headers. + +/*------------------------------------------------------------------------*\ + +The 'reports' are shown as 'c <char> ...' with '<char>' as follows: + +i propagated learned unit clause +O backtracked after phases reset to original phase +F backtracked after flipping phases +# backtracked after randomly setting phases +B backtracked after resetting to best phases +W backtracked after local search improved phases +b blocked clause elimination +G before garbage collection +C after garbage collection +/ compacted internal literals and remapped external to internal +c covered clause elimination +d decomposed binary implication graph and substituted equivalent literals +2 removed duplicated binary clauses +e bounded variable elimination round +^ variable elimination bound increased +I variable instantiation +[ start of stable search phase +] end of stable search phase +{ start of unstable search phase +} end of unstable search phase +P preprocessing round (capital 'P') +L local search round +* start of solving without the need to restore clauses ++ start of solving before restoring clauses +r start of solving after restoring clauses +1 end of solving returns satisfiable +0 end of solving returns unsatisfiable +? end of solving due to interrupt +l lucky phase solving +p failed literal probing round (lower case 'p') +. before reducing redundant clauses +f flushed redundant clauses +- reduced redundant clauses +~ start of resetting phases +R restart +s subsumed clause removal round +3 ternary resolution round +t transition reduction of binary implication graph +u vivified tier1 clauses +v vivified tier2 clauses +x vivified tier3 clauses +w vivified irredundant clauses + +The order of the list follows the occurrences of 'report' in the source +files, i.e., obtained from "grep 'report (' *.cpp". Note that some of the +reports are only printed for higher verbosity level (for instance 'R'). + +\*------------------------------------------------------------------------*/ + +struct Report { + + const char *header; + char buffer[32]; + int pos; + + Report (const char *h, int precision, int min, double value); + Report () {} + + void print_header (char *line); +}; + +/*------------------------------------------------------------------------*/ + +void Report::print_header (char *line) { + int len = strlen (header); + for (int i = -1, j = pos - (len + 1) / 2 - 3; i < len; i++, j++) + line[j] = i < 0 ? ' ' : header[i]; +} + +Report::Report (const char *h, int precision, int min, double value) + : header (h) { + char fmt[32]; + if (precision < 0) + snprintf (fmt, sizeof fmt, "%%.%df", -precision - 1); + else + snprintf (fmt, sizeof fmt, "%%.%df", precision); + snprintf (buffer, sizeof buffer, fmt, value); + const int width = strlen (buffer); + if (precision < 0) + strcat (buffer, "%"); + if (width >= min) + return; + if (precision < 0) + snprintf (fmt, sizeof fmt, "%%%d.%df%%%%", min, -precision - 1); + else + snprintf (fmt, sizeof fmt, "%%%d.%df", min, precision); + snprintf (buffer, sizeof buffer, fmt, value); +} + +/*------------------------------------------------------------------------*/ + +// The following statistics are printed in columns, whenever 'report' is +// called. For instance 'reduce' with prefix '-' will call it. The other +// more interesting report is due to learning a unit, called iteration, with +// prefix 'i'. To add another statistics column, add a corresponding line +// here. If you want to report something else add 'report (..)' functions. + +#define TIME opts.reportsolve ? solve_time () : time () + +#define MB (current_resident_set_size () / (double) (1l << 20)) + +#define REMAINING (percent (active (), stats.variables_original)) + +#define TRAIL (percent (averages.current.trail.slow, max_var)) + +#define TARGET (percent (target_assigned, max_var)) + +#define BEST (percent (best_assigned, max_var)) + +#define REPORTS \ + /* HEADER, PRECISION, MIN, VALUE */ \ + REPORT ("seconds", 2, 5, TIME) \ + REPORT ("MB", 0, 2, MB) \ + REPORT ("level", 0, 2, averages.current.level) \ + REPORT ("reductions", 0, 1, stats.reductions) \ + REPORT ("restarts", 0, 3, stats.restarts) \ + REPORT ("rate", 0, 2, averages.current.decisions) \ + REPORT ("conflicts", 0, 4, stats.conflicts) \ + REPORT ("redundant", 0, 4, stats.current.redundant) \ + REPORT ("trail", -1, 2, TRAIL) \ + REPORT ("glue", 0, 1, averages.current.glue.slow) \ + REPORT ("irredundant", 0, 4, stats.current.irredundant) \ + REPORT ("variables", 0, 3, active ()) \ + REPORT ("remaining", -1, 2, REMAINING) + +// Note, keep an empty line before this line (because of '\')! + +#if 0 // ADDITIONAL STATISTICS TO REPORT + +REPORT("best", -1, 2, BEST) \ +REPORT("target", -1, 2, TARGET) \ +REPORT("maxvar", 0, 2, external->max_var) + +#endif + +static const int num_reports = // as compile time constant +#define REPORT(HEAD, PREC, MIN, EXPR) 1 + + REPORTS +#undef REPORT + 0; + +/*------------------------------------------------------------------------*/ + +void Internal::report (char type, int verbose) { + if (!opts.report) + return; +#ifdef LOGGING + if (!opts.log) +#endif + if (opts.quiet || (verbose > opts.verbose)) + return; + if (!reported) { + assert (!lim.report); + reported = true; + MSG ("%stime measured in %s time %s%s", tout.magenta_code (), + internal->opts.realtime ? "real" : "process", + internal->opts.reportsolve ? "in solving" : "since initialization", + tout.normal_code ()); + } + Report reports[num_reports]; + int n = 0; +#define REPORT(HEAD, PREC, MIN, EXPR) \ + assert (n < num_reports); \ + reports[n++] = Report (HEAD, PREC, MIN, (double) (EXPR)); + REPORTS +#undef REPORT + if (!lim.report) { + print_prefix (); + fputc ('\n', stdout); + int pos = 4; + for (int i = 0; i < n; i++) { + int len = strlen (reports[i].buffer); + reports[i].pos = pos + (len + 1) / 2; + pos += len + 1; + } + const int max_line = pos + 20, nrows = 3; + char *line = new char[max_line]; + for (int start = 0; start < nrows; start++) { + int i; + for (i = 0; i < max_line; i++) + line[i] = ' '; + for (i = start; i < n; i += nrows) + reports[i].print_header (line); + for (i = max_line - 1; line[i - 1] == ' '; i--) + ; + line[i] = 0; + print_prefix (); + tout.yellow (); + fputs (line, stdout); + tout.normal (); + fputc ('\n', stdout); + } + print_prefix (); + fputc ('\n', stdout); + delete[] line; + lim.report = 19; + } else + lim.report--; + print_prefix (); + switch (type) { + case '[': + case ']': + tout.magenta (true); + break; + case 's': + case 'b': + case 'c': + tout.green (false); + break; + case 'e': + tout.green (true); + break; + case 'p': + case '2': + case '3': + case 'u': + case 'v': + case 'w': + case 'x': + case 'f': + case '=': + tout.blue (false); + break; + case 't': + tout.cyan (false); + break; + case 'd': + tout.blue (true); + break; + case 'z': + case '!': + tout.cyan (true); + break; + case '-': + tout.normal (); + break; + case '/': + tout.yellow (true); + break; + case 'a': + case 'n': + tout.red (false); + break; + case '0': + case '1': + case '?': + case 'i': + tout.bold (); + break; + case 'L': + case 'P': + tout.bold (); + tout.underline (); + break; + default: + break; + } + fputc (type, stdout); + if (stable || type == ']') + tout.magenta (); + else if (type != 'L' && type != 'P') + tout.normal (); + for (int i = 0; i < n; i++) { + fputc (' ', stdout); + fputs (reports[i].buffer, stdout); + } + if (stable || type == 'L' || type == 'P' || type == ']') + tout.normal (); + fputc ('\n', stdout); + fflush (stdout); +} + +#else // ifndef QUIET + +void Internal::report (char, int) {} + +#endif + +} // namespace CaDiCaL diff --git a/src/sat/cadical/resources.cpp b/src/sat/cadical/resources.cpp new file mode 100644 index 000000000..712a8ec67 --- /dev/null +++ b/src/sat/cadical/resources.cpp @@ -0,0 +1,160 @@ +#include "internal.hpp" + +/*------------------------------------------------------------------------*/ + +// This is operating system specific code. We mostly develop on Linux and +// there it should be fine and mostly works out-of-the-box on MacOS too but +// Windows needs special treatment (as probably other operating systems +// too). + +extern "C" { + +#ifdef __WIN32 + +#ifndef __WIN32_WINNT +#define __WIN32_WINNT 0x0600 +#endif + +// Clang-format would reorder the includes which breaks the Windows code +// as it expects 'windows.h' to be included first. So disable it here. + +// clang-format off + +#include <windows.h> +#include <psapi.h> + +// clang-format on + +#else + +#include <sys/resource.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +#endif + +#include <string.h> +} + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +#ifdef __WIN32 + +double absolute_real_time () { + FILETIME f; + GetSystemTimeAsFileTime (&f); + ULARGE_INTEGER t; + t.LowPart = f.dwLowDateTime; + t.HighPart = f.dwHighDateTime; + double res = (__int64) t.QuadPart; + res *= 1e-7; + return res; +} + +double absolute_process_time () { + double res = 0; + FILETIME fc, fe, fu, fs; + if (GetProcessTimes (GetCurrentProcess (), &fc, &fe, &fu, &fs)) { + ULARGE_INTEGER u, s; + u.LowPart = fu.dwLowDateTime; + u.HighPart = fu.dwHighDateTime; + s.LowPart = fs.dwLowDateTime; + s.HighPart = fs.dwHighDateTime; + res = (__int64) u.QuadPart + (__int64) s.QuadPart; + res *= 1e-7; + } + return res; +} + +#else + +double absolute_real_time () { + struct timeval tv; + if (gettimeofday (&tv, 0)) + return 0; + return 1e-6 * tv.tv_usec + tv.tv_sec; +} + +// We use 'getrusage' for 'process_time' and 'maximum_resident_set_size' +// which is pretty standard on Unix but probably not available on Windows +// etc. For different variants of Unix not all fields are meaningful. + +double absolute_process_time () { + double res; + struct rusage u; + if (getrusage (RUSAGE_SELF, &u)) + return 0; + res = u.ru_utime.tv_sec + 1e-6 * u.ru_utime.tv_usec; // user time + res += u.ru_stime.tv_sec + 1e-6 * u.ru_stime.tv_usec; // + system time + return res; +} + +#endif + +double Internal::real_time () const { + return absolute_real_time () - stats.time.real; +} + +double Internal::process_time () const { + return absolute_process_time () - stats.time.process; +} + +/*------------------------------------------------------------------------*/ + +#ifdef __WIN32 + +uint64_t current_resident_set_size () { + PROCESS_MEMORY_COUNTERS pmc; + if (GetProcessMemoryInfo (GetCurrentProcess (), &pmc, sizeof (pmc))) { + return pmc.WorkingSetSize; + } else + return 0; +} + +uint64_t maximum_resident_set_size () { + PROCESS_MEMORY_COUNTERS pmc; + if (GetProcessMemoryInfo (GetCurrentProcess (), &pmc, sizeof (pmc))) { + return pmc.PeakWorkingSetSize; + } else + return 0; +} + +#else + +// This seems to work on Linux (man page says since Linux 2.6.32). + +uint64_t maximum_resident_set_size () { + struct rusage u; + if (getrusage (RUSAGE_SELF, &u)) + return 0; + return ((uint64_t) u.ru_maxrss) << 10; +} + +// Unfortunately 'getrusage' on Linux does not support current resident set +// size (the field 'ru_ixrss' is there but according to the man page +// 'unused'). Thus we fall back to use the '/proc' file system instead. So +// this is not portable at all and needs to be replaced on other systems +// The code would still compile though (assuming 'sysconf' and +// '_SC_PAGESIZE' are available). + +uint64_t current_resident_set_size () { + char path[64]; + snprintf (path, sizeof path, "/proc/%" PRId64 "/statm", + (int64_t) getpid ()); + FILE *file = fopen (path, "r"); + if (!file) + return 0; + uint64_t dummy, rss; + int scanned = fscanf (file, "%" PRIu64 " %" PRIu64 "", &dummy, &rss); + fclose (file); + return scanned == 2 ? rss * sysconf (_SC_PAGESIZE) : 0; +} + +#endif + +/*------------------------------------------------------------------------*/ + +} // namespace CaDiCaL diff --git a/src/sat/cadical/resources.hpp b/src/sat/cadical/resources.hpp new file mode 100644 index 000000000..377c95f3d --- /dev/null +++ b/src/sat/cadical/resources.hpp @@ -0,0 +1,16 @@ +#ifndef _resources_hpp_INCLUDED +#define _resources_hpp_INCLUDED + +#include <cstdint> + +namespace CaDiCaL { + +double absolute_real_time (); +double absolute_process_time (); + +uint64_t maximum_resident_set_size (); +uint64_t current_resident_set_size (); + +} // namespace CaDiCaL + +#endif // ifndef _resources_hpp_INCLUDED diff --git a/src/sat/cadical/restart.cpp b/src/sat/cadical/restart.cpp new file mode 100644 index 000000000..799df94e9 --- /dev/null +++ b/src/sat/cadical/restart.cpp @@ -0,0 +1,159 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +// As observed by Chanseok Oh and implemented in MapleSAT solvers too, +// various mostly satisfiable instances benefit from long quiet phases +// with less or almost no restarts. We implement this idea by prohibiting +// the Glucose style restart scheme in a geometric fashion, which is very +// similar to how originally restarts were scheduled in MiniSAT and earlier +// solvers. We start with say 1e3 = 1000 (opts.stabilizeinit) conflicts of +// Glucose restarts. Then in a "stabilizing" phase we disable these +// until 1e4 = 2000 conflicts (if 'opts.stabilizefactor' is '200' percent) +// have passed. After that we switch back to regular Glucose style restarts +// until again 2 times more conflicts than the previous limit are reached. +// Actually, in the latest version we still restarts during stabilization +// but only in a reluctant doubling scheme with a rather high interval. + +bool Internal::stabilizing () { + if (!opts.stabilize) + return false; + if (stable && opts.stabilizeonly) + return true; + if (!inc.stabilize) { + assert (!stable); + if (stats.conflicts <= lim.stabilize) + return false; + } else if (stats.ticks.search[stable] <= lim.stabilize) + return stable; + report (stable ? ']' : '}'); + if (stable) + STOP (stable); + else + STOP (unstable); + const int64_t delta_conflicts = + stats.conflicts - last.stabilize.conflicts; + const int64_t delta_ticks = + stats.ticks.search[stable] - last.stabilize.ticks; + const char *current_mode = stable ? "stable" : "unstable"; + const char *next_mode = stable ? "unstable" : "stable"; + PHASE ("stabilizing", stats.stabphases, + "reached %s stabilization limit %" PRId64 " after %" PRId64 + " conflicts and %" PRId64 " ticks at %" PRId64 + " conflicts and %" PRId64 " ticks", + current_mode, lim.stabilize, delta_conflicts, delta_ticks, + stats.conflicts, stats.ticks.search[stable]); + if (!inc.stabilize) + inc.stabilize = delta_ticks; + if (!inc.stabilize) // rare occurence in incremental calls requiring no + // ticks + inc.stabilize = 1; + + stable = !stable; // Switch!!!!! + + int64_t next_delta_ticks = inc.stabilize; + int64_t stabphases = stats.stabphases + 1; + next_delta_ticks *= stabphases * stabphases; + + lim.stabilize = stats.ticks.search[stable] + next_delta_ticks; + if (lim.stabilize <= stats.ticks.search[stable]) + lim.stabilize = stats.ticks.search[stable] + 1; + + if (stable) + stats.stabphases++; + + swap_averages (); + PHASE ("stabilizing", stats.stabphases, + "next %s stabilization limit %" PRId64 + " at ticks interval %" PRId64, + next_mode, lim.stabilize, next_delta_ticks); + report (stable ? '[' : '{'); + if (stable) + START (stable); + else + START (unstable); + return stable; +} + +// Restarts are scheduled by a variant of the Glucose scheme as presented +// in our POS'15 paper using exponential moving averages. There is a slow +// moving average of the average recent glucose level of learned clauses +// as well as a fast moving average of those glues. If the end of a base +// restart conflict interval has passed and the fast moving average is +// above a certain margin over the slow moving average then we restart. + +bool Internal::restarting () { + if (!opts.restart) + return false; + if ((size_t) level < assumptions.size () + 2) + return false; + if (stabilizing ()) + return reluctant; + if (stats.conflicts <= lim.restart) + return false; + double f = averages.current.glue.fast; + double margin = (100.0 + opts.restartmargin) / 100.0; + double s = averages.current.glue.slow, l = margin * s; + LOG ("EMA glue slow %.2f fast %.2f limit %.2f", s, f, l); + return l <= f; +} + +// This is Marijn's reuse trail idea. Instead of always backtracking to +// the top we figure out which decisions will be made again anyhow and +// only backtrack to the level of the last such decision or to the top if +// no such decision exists top (in which case we do not reuse any level). + +int Internal::reuse_trail () { + const int trivial_decisions = + assumptions.size () + // Plus 1 if the constraint is satisfied via implications of + // assumptions and a pseudo-decision level was introduced. + + !control[assumptions.size () + 1].decision; + if (!opts.restartreusetrail) + return trivial_decisions; + int next_decision = next_decision_variable (); + assert (1 <= next_decision); + int res = trivial_decisions; + if (use_scores ()) { + while (res < level) { + int decision = control[res + 1].decision; + if (decision && score_smaller (this) (abs (decision), next_decision)) + break; + res++; + } + } else { + int64_t limit = bumped (next_decision); + while (res < level) { + int decision = control[res + 1].decision; + if (decision && bumped (decision) < limit) + break; + res++; + } + } + int reused = res - trivial_decisions; + if (reused > 0) { + stats.reused++; + stats.reusedlevels += reused; + if (stable) + stats.reusedstable++; + } + return res; +} + +void Internal::restart () { + START (restart); + stats.restarts++; + stats.restartlevels += level; + if (stable) + stats.restartstable++; + LOG ("restart %" PRId64 "", stats.restarts); + backtrack (reuse_trail ()); + + lim.restart = stats.conflicts + opts.restartint; + LOG ("new restart limit at %" PRId64 " conflicts", lim.restart); + + report ('R', 2); + STOP (restart); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/restore.cpp b/src/sat/cadical/restore.cpp new file mode 100644 index 000000000..cd1c9721d --- /dev/null +++ b/src/sat/cadical/restore.cpp @@ -0,0 +1,267 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// In incremental solving after a first call to 'solve' has finished and +// before calling the internal 'solve' again incrementally we have to +// restore clauses which have the negation of a literal as a witness literal +// on the extension stack, which was added as original literal in a new +// clause or in an assumption. This procedure has to be applied +// recursively, i.e., the literals of restored clauses are treated in the +// same way as literals of a new original clause. +// +// To figure out whether literals are such witnesses we have a 'witness' +// bit for each external literal, which is set in 'block', 'elim', and +// 'decompose' if a clause is pushed on the extension stack. The witness +// bits are recomputed after restoring clauses. +// +// We further mark in the external solver newly internalized external +// literals in 'add' and 'assume' since the last call to 'solve' as tainted +// if they occur negated as a witness literal on the extension stack. Then +// we go through the extension stack and restore all clauses which have a +// tainted literal (and its negation a marked as witness). +// +// Since the API contract disallows to call 'val' and 'failed' in an +// 'UNKNOWN' state. We do not have to internalize literals there. +// +// In order to have tainted literals accepted by the internal solver they +// have to be active and thus we might need to 'reactivate' them before +// restoring clauses if they are inactive. In case they have completely +// been eliminated and removed from the internal solver in 'compact', then +// we just use a new internal variable. This is performed in 'internalize' +// during marking external literals as tainted. +// +// To check that this approach is correct the external solver can maintain a +// stack of original clauses and current assumptions both in terms of +// external literals. Whenever 'solve' determines that the current +// incremental call is satisfiable we check that the (extended) witness does +// satisfy the saved original clauses, as well as all the assumptions. To +// enable these checks set 'opts.check' as well as 'opts.checkwitness' and +// 'opts.checkassumptions' all to 'true'. The model based tester actually +// prefers to enable the 'opts.check' option and the other two are 'true' by +// default anyhow. +// +// See our SAT'19 paper [FazekasBiereScholl-SAT'19] for more details. + +/*------------------------------------------------------------------------*/ + +void External::restore_clause (const vector<int>::const_iterator &begin, + const vector<int>::const_iterator &end, + const int64_t id) { + LOG (begin, end, "restoring external clause[%" PRId64 "]", id); + assert (eclause.empty ()); + assert (id); + for (auto p = begin; p != end; p++) { + eclause.push_back (*p); + if (internal->proof && internal->lrat) { + const auto &elit = *p; + unsigned eidx = (elit > 0) + 2u * (unsigned) abs (elit); + assert ((size_t) eidx < ext_units.size ()); + const int64_t id = ext_units[eidx]; + bool added = ext_flags[abs (elit)]; + if (id && !added) { + ext_flags[abs (elit)] = true; + internal->lrat_chain.push_back (id); + } + } + int ilit = internalize (*p); + internal->add_original_lit (ilit), internal->stats.restoredlits++; + } + if (internal->proof && internal->lrat) { + for (const auto &elit : eclause) { + ext_flags[abs (elit)] = false; + } + } + internal->finish_added_clause_with_id (id, true); + eclause.clear (); + internal->stats.restored++; +} + +/*------------------------------------------------------------------------*/ + +void External::restore_clauses () { + + assert (internal->opts.restoreall == 2 || !tainted.empty ()); + + START (restore); + internal->stats.restorations++; + + struct { + int64_t weakened, satisfied, restored, removed; + } clauses; + memset (&clauses, 0, sizeof clauses); + + if (internal->opts.restoreall && tainted.empty ()) + PHASE ("restore", internal->stats.restorations, + "forced to restore all clauses"); + +#ifndef QUIET + { + unsigned numtainted = 0; + for (const auto b : tainted) + if (b) + numtainted++; + + PHASE ("restore", internal->stats.restorations, + "starting with %u tainted literals %.0f%%", numtainted, + percent (numtainted, 2u * max_var)); + } +#endif + + auto end_of_extension = extension.end (); + auto p = extension.begin (), q = p; + + // Go over all witness labelled clauses on the extension stack, restore + // those necessary, remove restored and flush satisfied clauses. + // + while (p != end_of_extension) { + + clauses.weakened++; + + assert (!*p); + const auto saved = q; // Save old start. + *q++ = *p++; // Copy zero '0'. + + // Copy witness part and try to find a tainted witness literal in it. + // + int tlit = 0; // Negation tainted. + int elit; + // + assert (p != end_of_extension); + // + while ((elit = *q++ = *p++)) { + + if (marked (tainted, -elit)) { + tlit = elit; + LOG ("negation of witness literal %d tainted", tlit); + } + + assert (p != end_of_extension); + } + + // now copy the id of the clause + const int64_t id = ((int64_t) (*p) << 32) + (int64_t) * (p + 1); + LOG ("id is %" PRId64, id); + *q++ = *p++; + *q++ = *p++; + assert (id); + assert (!*p); + *q++ = *p++; + + // Now find 'end_of_clause' (clause starts at 'p') and at the same time + // figure out whether the clause is actually root level satisfied. + // + int satisfied = 0; + auto end_of_clause = p; + while (end_of_clause != end_of_extension && (elit = *end_of_clause)) { + if (!satisfied && fixed (elit) > 0) + satisfied = elit; + end_of_clause++; + } + assert (id); + + // Do not apply our 'FLUSH' rule to remove satisfied (implied) clauses + // if the corresponding option is set simply by resetting 'satisfied'. + // + if (satisfied && !internal->opts.restoreflush) { + LOG (p, end_of_clause, "forced to not remove %d satisfied", + satisfied); + satisfied = 0; + } + + if (satisfied || tlit || internal->opts.restoreall) { + + if (satisfied) { + LOG (p, end_of_clause, + "flushing implied clause satisfied by %d from extension stack", + satisfied); + clauses.satisfied++; + } else { + restore_clause (p, end_of_clause, id); // Might taint literals. + clauses.restored++; + } + + clauses.removed++; + p = end_of_clause; + q = saved; + + } else { + + LOG (p, end_of_clause, "keeping clause on extension stack"); + + while (p != end_of_clause) // Copy clause too. + *q++ = *p++; + } + } + + extension.resize (q - extension.begin ()); + shrink_vector (extension); + +#ifndef QUIET + if (clauses.satisfied) + PHASE ("restore", internal->stats.restorations, + "removed %" PRId64 " satisfied %.0f%% of %" PRId64 + " weakened clauses", + clauses.satisfied, percent (clauses.satisfied, clauses.weakened), + clauses.weakened); + else + PHASE ("restore", internal->stats.restorations, + "no satisfied clause removed out of %" PRId64 + " weakened clauses", + clauses.weakened); + + if (clauses.restored) + PHASE ("restore", internal->stats.restorations, + "restored %" PRId64 " clauses %.0f%% out of %" PRId64 + " weakened clauses", + clauses.restored, percent (clauses.restored, clauses.weakened), + clauses.weakened); + else + PHASE ("restore", internal->stats.restorations, + "no clause restored out of %" PRId64 " weakened clauses", + clauses.weakened); + { + unsigned numtainted = 0; + for (const auto &b : tainted) + if (b) + numtainted++; + + PHASE ("restore", internal->stats.restorations, + "finishing with %u tainted literals %.0f%%", numtainted, + percent (numtainted, 2u * max_var)); + } + +#endif + LOG ("extension stack clean"); + tainted.clear (); + + // Finally recompute the witness bits. + // + witness.clear (); + const auto begin_of_extension = extension.begin (); + p = extension.end (); + while (p != begin_of_extension) { + while (*--p) + assert (p != begin_of_extension); + int elit; + assert (p != begin_of_extension); + --p; + assert (p != begin_of_extension); + assert (*p || *(p - 1)); + --p; + assert (p != begin_of_extension); + assert (!*p); + --p; + assert (p != begin_of_extension); + while ((elit = *--p)) { + mark (witness, elit); + assert (p != begin_of_extension); + } + } + + STOP (restore); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/score.cpp b/src/sat/cadical/score.cpp new file mode 100644 index 000000000..578d553a8 --- /dev/null +++ b/src/sat/cadical/score.cpp @@ -0,0 +1,51 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +// This initializes variables on the binary 'scores' heap also with +// smallest variable index first (thus picked first) and larger indices at +// the end. +// +void Internal::init_scores (int old_max_var, int new_max_var) { + LOG ("initializing EVSIDS scores from %d to %d", old_max_var + 1, + new_max_var); + for (int i = old_max_var; i < new_max_var; i++) + scores.push_back (i + 1); +} + +// Shuffle the EVSIDS heap. + +void Internal::shuffle_scores () { + if (!opts.shuffle) + return; + if (!opts.shufflescores) + return; + assert (!level); + stats.shuffled++; + LOG ("shuffling scores"); + vector<int> shuffle; + if (opts.shufflerandom) { + scores.erase (); + for (int idx = max_var; idx; idx--) + shuffle.push_back (idx); + Random random (opts.seed); // global seed + random += stats.shuffled; // different every time + for (int i = 0; i <= max_var - 2; i++) { + const int j = random.pick_int (i, max_var - 1); + swap (shuffle[i], shuffle[j]); + } + } else { + while (!scores.empty ()) { + int idx = scores.front (); + (void) scores.pop_front (); + shuffle.push_back (idx); + } + } + score_inc = 0; + for (const auto &idx : shuffle) { + stab[idx] = score_inc++; + scores.push_back (idx); + } +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/score.hpp b/src/sat/cadical/score.hpp new file mode 100644 index 000000000..9fde98af3 --- /dev/null +++ b/src/sat/cadical/score.hpp @@ -0,0 +1,16 @@ +#ifndef _score_hpp_INCLUDED +#define _score_hpp_INCLUDED + +namespace CaDiCaL { + +struct score_smaller { + Internal *internal; + score_smaller (Internal *i) : internal (i) {} + bool operator() (unsigned a, unsigned b); +}; + +typedef heap<score_smaller> ScoreSchedule; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/shrink.cpp b/src/sat/cadical/shrink.cpp new file mode 100644 index 000000000..e61935117 --- /dev/null +++ b/src/sat/cadical/shrink.cpp @@ -0,0 +1,507 @@ +#include "internal.hpp" +#include "reap.hpp" + +namespace CaDiCaL { + +void Internal::reset_shrinkable () { +#ifdef LOGGING + size_t reset = 0; +#endif + for (const auto &lit : shrinkable) { + LOG ("resetting lit %i", lit); + Flags &f = flags (lit); + assert (f.shrinkable); + f.shrinkable = false; +#ifdef LOGGING + ++reset; +#endif + } + LOG ("resetting %zu shrinkable variables", reset); +} + +void Internal::mark_shrinkable_as_removable ( + int blevel, std::vector<int>::size_type minimized_start) { +#ifdef LOGGING + size_t marked = 0, reset = 0; +#endif +#ifndef NDEBUG + unsigned kept = 0, minireset = 0; + for (; minimized_start < minimized.size (); ++minimized_start) { + const int lit = minimized[minimized_start]; + Flags &f = flags (lit); + const Var &v = var (lit); + if (v.level == blevel) { + assert (!f.poison); + ++minireset; + } else + ++kept; + } + (void) kept; + (void) minireset; +#else + (void) blevel; + (void) minimized_start; +#endif + + for (const int lit : shrinkable) { + Flags &f = flags (lit); + assert (f.shrinkable); + assert (!f.poison); + f.shrinkable = false; +#ifdef LOGGING + ++reset; +#endif + if (f.removable) + continue; + f.removable = true; + minimized.push_back (lit); +#ifdef LOGGING + ++marked; +#endif + } + LOG ("resetting %zu shrinkable variables", reset); + LOG ("marked %zu removable variables", marked); +} + +int inline Internal::shrink_literal (int lit, int blevel, + unsigned max_trail) { + assert (val (lit) < 0); + + Flags &f = flags (lit); + Var &v = var (lit); + assert (v.level <= blevel); + + if (!v.level) { + LOG ("skipping root level assigned %d", (lit)); + return 0; + } + + if (v.reason == external_reason) { + assert (!opts.exteagerreasons); + v.reason = learn_external_reason_clause (-lit, 0, true); + if (!v.reason) { + assert (!v.level); + return 0; + } + } + assert (v.reason != external_reason); + if (f.shrinkable) { + LOG ("skipping already shrinkable literal %d", (lit)); + return 0; + } + + if (v.level < blevel) { + if (f.removable) { + LOG ("skipping removable thus shrinkable %d", (lit)); + return 0; + } + const bool always_minimize_on_lower_blevel = (opts.shrink > 2); + if (always_minimize_on_lower_blevel && minimize_literal (-lit, 1)) { + LOG ("minimized thus shrinkable %d", (lit)); + return 0; + } + LOG ("literal %d on lower blevel %u < %u not removable/shrinkable", + (lit), v.level, blevel); + return -1; + } + + LOG ("marking %d as shrinkable", lit); + f.shrinkable = true; + f.poison = false; + shrinkable.push_back (lit); + if (opts.shrinkreap) { + assert (max_trail < trail.size ()); + const unsigned dist = max_trail - v.trail; + reap.push (dist); + } + return 1; +} + +unsigned Internal::shrunken_block_uip ( + int uip, int blevel, std::vector<int>::reverse_iterator &rbegin_block, + std::vector<int>::reverse_iterator &rend_block, + std::vector<int>::size_type minimized_start, const int uip0) { + assert (clause[0] == uip0); + + LOG ("UIP on level %u, uip: %i (replacing by %i)", blevel, uip, uip0); + assert (rend_block > rbegin_block); + assert (rend_block < clause.rend ()); + unsigned block_shrunken = 0; + *rbegin_block = -uip; + Var &v = var (-uip); + Level &l = control[v.level]; + l.seen.trail = v.trail; + l.seen.count = 1; + + Flags &f = flags (-uip); + if (!f.seen) { + analyzed.push_back (-uip); + f.seen = true; + } + + flags (-uip).keep = true; + for (auto p = rbegin_block + 1; p != rend_block; ++p) { + const int lit = *p; + if (lit == -uip0) + continue; + *p = uip0; + // if (lit == -uip) continue; + ++block_shrunken; + assert (clause[0] == uip0); + } + mark_shrinkable_as_removable (blevel, minimized_start); + assert (clause[0] == uip0); + return block_shrunken; +} + +void inline Internal::shrunken_block_no_uip ( + const std::vector<int>::reverse_iterator &rbegin_block, + const std::vector<int>::reverse_iterator &rend_block, + unsigned &block_minimized, const int uip0) { + STOP (shrink); + START (minimize); + assert (rend_block > rbegin_block); + LOG ("no UIP found, now minimizing"); + for (auto p = rbegin_block; p != rend_block; ++p) { + assert (p != clause.rend () - 1); + const int lit = *p; + if (opts.minimize && minimize_literal (-lit)) { + assert (!flags (lit).keep); + ++block_minimized; + *p = uip0; + } else { + flags (lit).keep = true; + assert (flags (lit).keep); + } + } + STOP (minimize); + START (shrink); +} + +void Internal::push_literals_of_block ( + const std::vector<int>::reverse_iterator &rbegin_block, + const std::vector<int>::reverse_iterator &rend_block, int blevel, + unsigned max_trail) { + assert (rbegin_block < rend_block); + for (auto p = rbegin_block; p != rend_block; ++p) { + assert (p != clause.rend () - 1); + assert (!flags (*p).keep); + const int lit = *p; + LOG ("pushing lit %i of blevel %i", lit, var (lit).level); +#ifndef NDEBUG + int tmp = +#endif + shrink_literal (lit, blevel, max_trail); + assert (tmp > 0); + } +} + +unsigned inline Internal::shrink_next (int blevel, unsigned &open, + unsigned &max_trail) { + const auto &t = &trail; + if (opts.shrinkreap) { + assert (!reap.empty ()); + const unsigned dist = reap.pop (); + --open; + assert (dist <= max_trail); + const unsigned pos = max_trail - dist; + assert (pos < t->size ()); + const int uip = (*t)[pos]; + assert (val (uip) > 0); + LOG ("trying to shrink literal %d at trail[%u] and level %d", uip, pos, + blevel); + return uip; + } else { + int uip; +#ifndef NDEBUG + unsigned init_max_trail = max_trail; +#endif + do { + assert (max_trail <= init_max_trail); + uip = (*t)[max_trail--]; + } while (!flags (uip).shrinkable); + --open; + LOG ("open is now %d, uip = %d, level %d", open, uip, blevel); + return uip; + } + (void) blevel; +} + +unsigned inline Internal::shrink_along_reason (int uip, int blevel, + bool resolve_large_clauses, + bool &failed_ptr, + unsigned max_trail) { + LOG ("shrinking along the reason of lit %i", uip); + unsigned open = 0; +#ifndef NDEBUG + const Flags &f = flags (uip); +#endif + const Var &v = var (uip); + + assert (f.shrinkable); + assert (v.level == blevel); + assert (v.reason); + + if (opts.minimizeticks) + stats.ticks.search[stable]++; + + if (resolve_large_clauses || v.reason->size == 2) { + const Clause &c = *v.reason; + LOG (v.reason, "resolving with reason"); + for (int lit : c) { + if (lit == uip) + continue; + assert (val (lit) < 0); + int tmp = shrink_literal (lit, blevel, max_trail); + if (tmp < 0) { + failed_ptr = true; + break; + } + if (tmp > 0) { + ++open; + } + } + } else { + failed_ptr = true; + } + return open; +} + +unsigned +Internal::shrink_block (std::vector<int>::reverse_iterator &rbegin_lits, + std::vector<int>::reverse_iterator &rend_block, + int blevel, unsigned &open, + unsigned &block_minimized, const int uip0, + unsigned max_trail) { + assert (shrinkable.empty ()); + assert (blevel <= this->level); + assert (open < clause.size ()); + assert (rbegin_lits >= clause.rbegin ()); + assert (rend_block < clause.rend ()); + assert (rbegin_lits < rend_block); + assert (opts.shrink); + +#ifdef LOGGING + + LOG ("trying to shrink %u literals on level %u", open, blevel); + + const auto &t = &trail; + + LOG ("maximum trail position %zd on level %u", t->size (), blevel); + if (opts.shrinkreap) + LOG ("shrinking up to %u", max_trail); +#endif + + const bool resolve_large_clauses = (opts.shrink > 1); + bool failed = false; + unsigned block_shrunken = 0; + std::vector<int>::size_type minimized_start = minimized.size (); + int uip = uip0; + unsigned max_trail2 = max_trail; + + if (!failed) { + push_literals_of_block (rbegin_lits, rend_block, blevel, max_trail); + assert (!opts.shrinkreap || reap.size () == open); + + assert (open > 0); + while (!failed) { + assert (!opts.shrinkreap || reap.size () == open); + uip = shrink_next (blevel, open, max_trail); + if (open == 0) { + break; + } + open += shrink_along_reason (uip, blevel, resolve_large_clauses, + failed, max_trail2); + assert (open >= 1); + } + + if (!failed) + LOG ("shrinking found UIP %i on level %i (open: %d)", uip, blevel, + open); + else + LOG ("shrinking failed on level %i", blevel); + } + + if (failed) + reset_shrinkable (), shrunken_block_no_uip (rbegin_lits, rend_block, + block_minimized, uip0); + else + block_shrunken = shrunken_block_uip (uip, blevel, rbegin_lits, + rend_block, minimized_start, uip0); + + if (opts.shrinkreap) + reap.clear (); + shrinkable.clear (); + return block_shrunken; +} + +// Smaller level and trail. Comparing literals on their level is necessary +// for chronological backtracking, since trail order might in this case not +// respect level order. + +struct shrink_trail_negative_rank { + Internal *internal; + shrink_trail_negative_rank (Internal *s) : internal (s) {} + typedef uint64_t Type; + Type operator() (int a) { + Var &v = internal->var (a); + uint64_t res = v.level; + res <<= 32; + res |= v.trail; + return ~res; + } +}; + +struct shrink_trail_larger { + Internal *internal; + shrink_trail_larger (Internal *s) : internal (s) {} + bool operator() (const int &a, const int &b) const { + return shrink_trail_negative_rank (internal) (a) < + shrink_trail_negative_rank (internal) (b); + } +}; + +// Finds the beginning of the block (rend_block, non-included) ending at +// rend_block (included). Then tries to shrinks and minimizes literals the +// block +std::vector<int>::reverse_iterator Internal::minimize_and_shrink_block ( + std::vector<int>::reverse_iterator &rbegin_block, + unsigned &total_shrunken, unsigned &total_minimized, const int uip0) + +{ + LOG ("shrinking block"); + assert (rbegin_block < clause.rend () - 1); + int blevel; + unsigned open = 0; + unsigned max_trail; + + // find begining of block; + std::vector<int>::reverse_iterator rend_block; + { + assert (rbegin_block <= clause.rend ()); + const int lit = *rbegin_block; + const int idx = vidx (lit); + blevel = vtab[idx].level; + max_trail = vtab[idx].trail; + LOG ("Block at level %i (first lit: %i)", blevel, lit); + + rend_block = rbegin_block; + bool finished; + do { + assert (rend_block < clause.rend () - 1); + const int lit = *(++rend_block); + const int idx = vidx (lit); + finished = (blevel != vtab[idx].level); + if (!finished && (unsigned) vtab[idx].trail > max_trail) + max_trail = vtab[idx].trail; + ++open; + LOG ( + "testing if lit %i is on the same level (of lit: %i, global: %i)", + lit, vtab[idx].level, blevel); + + } while (!finished); + } + assert (open > 0); + assert (open < clause.size ()); + assert (rbegin_block < clause.rend ()); + assert (rend_block < clause.rend ()); + + unsigned block_shrunken = 0, block_minimized = 0; + if (open < 2) { + flags (*rbegin_block).keep = true; + minimized.push_back (*rbegin_block); + } else + block_shrunken = shrink_block (rbegin_block, rend_block, blevel, open, + block_minimized, uip0, max_trail); + + LOG ("shrunken %u literals on level %u (including %u minimized)", + block_shrunken, blevel, block_minimized); + + total_shrunken += block_shrunken; + total_minimized += block_minimized; + + return rend_block; +} + +void Internal::shrink_and_minimize_clause () { + assert (opts.minimize || opts.shrink > 0); + LOG (clause, "shrink first UIP clause"); + + START (shrink); + external->check_learned_clause (); // check 1st UIP learned clause first + MSORT (opts.radixsortlim, clause.begin (), clause.end (), + shrink_trail_negative_rank (this), shrink_trail_larger (this)); + unsigned total_shrunken = 0; + unsigned total_minimized = 0; + + LOG (clause, "shrink first UIP clause (asserting lit: %i)", clause[0]); + + auto rend_lits = clause.rend () - 1; + auto rend_block = clause.rbegin (); + const int uip0 = clause[0]; + + // for direct LRAT we remember how the clause used to look + vector<int> old_clause_lrat; + assert (minimize_chain.empty ()); + if (lrat) + for (auto &i : clause) + old_clause_lrat.push_back (i); + + while (rend_block != rend_lits) { + rend_block = minimize_and_shrink_block (rend_block, total_shrunken, + total_minimized, uip0); + } + + LOG (clause, + "post shrink pass (with uips, not removed) first UIP clause"); + LOG (old_clause_lrat, "(used for lratdirect) before shrink: clause"); +#if defined(LOGGING) || !defined(NDEBUG) + const unsigned old_size = clause.size (); +#endif + std::vector<int> stack; + { + std::vector<int>::size_type i = 1; + for (std::vector<int>::size_type j = 1; j < clause.size (); ++j) { + assert (i <= j); + clause[i] = clause[j]; + if (lrat) { + assert (j < old_clause_lrat.size ()); + assert (mini_chain.empty ()); + if (clause[j] != old_clause_lrat[j]) { + calculate_minimize_chain (-old_clause_lrat[j], stack); + for (auto p : mini_chain) { + minimize_chain.push_back (p); + } + mini_chain.clear (); + } + } + if (clause[j] == uip0) { + continue; + } + assert (flags (clause[i]).keep); + ++i; + LOG ("keeping literal %i", clause[j]); + } + clause.resize (i); + } + assert (old_size == + (unsigned) clause.size () + total_shrunken + total_minimized); + LOG (clause, "after shrinking first UIP clause"); + LOG ("clause shrunken by %zd literals (including %u minimized)", + old_size - clause.size (), total_minimized); + + stats.shrunken += total_shrunken; + stats.minishrunken += total_minimized; + STOP (shrink); + + START (minimize); + clear_minimized_literals (); + for (auto p = minimize_chain.rbegin (); p != minimize_chain.rend (); + p++) { + lrat_chain.push_back (*p); + } + minimize_chain.clear (); + STOP (minimize); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/signal.cpp b/src/sat/cadical/signal.cpp new file mode 100644 index 000000000..384ba986f --- /dev/null +++ b/src/sat/cadical/signal.cpp @@ -0,0 +1,136 @@ +#include "signal.hpp" +#include "cadical.hpp" +#include "resources.hpp" + +/*------------------------------------------------------------------------*/ + +#include <cassert> +#include <csignal> + +/*------------------------------------------------------------------------*/ + +extern "C" { +#include <unistd.h> +} + +/*------------------------------------------------------------------------*/ + +// Signal handlers for printing statistics even if solver is interrupted. + +namespace CaDiCaL { + +static volatile bool caught_signal = false; +static Handler *signal_handler; + +#ifndef __WIN32 + +static volatile bool caught_alarm = false; +static volatile bool alarm_set = false; +static int alarm_time = -1; + +void Handler::catch_alarm () { catch_signal (SIGALRM); } + +#endif + +#define SIGNALS \ + SIGNAL (SIGABRT) \ + SIGNAL (SIGINT) \ + SIGNAL (SIGSEGV) \ + SIGNAL (SIGTERM) + +#define SIGNAL(SIG) static void (*SIG##_handler) (int); +SIGNALS +#undef SIGNAL + +#ifndef __WIN32 + +static void (*SIGALRM_handler) (int); + +void Signal::reset_alarm () { + if (!alarm_set) + return; + (void) signal (SIGALRM, SIGALRM_handler); + SIGALRM_handler = 0; + caught_alarm = false; + alarm_set = false; + alarm_time = -1; +} + +#endif + +void Signal::reset () { + signal_handler = 0; +#define SIGNAL(SIG) \ + (void) signal (SIG, SIG##_handler); \ + SIG##_handler = 0; + SIGNALS +#undef SIGNAL +#ifndef __WIN32 + reset_alarm (); +#endif + caught_signal = false; +} + +const char *Signal::name (int sig) { +#define SIGNAL(SIG) \ + if (sig == SIG) \ + return #SIG; + SIGNALS +#undef SIGNAL +#ifndef __WIN32 + if (sig == SIGALRM) + return "SIGALRM"; +#endif + return "UNKNOWN"; +} + +// TODO printing is not reentrant and might lead to deadlock if the signal +// is raised during another print attempt (and locked IO is used). To avoid +// this we have to either run our own low-level printing routine here or in +// 'Message' or just dump those statistics somewhere else were we have +// exclusive access to. All these solutions are painful and not elegant. + +static void catch_signal (int sig) { +#ifndef __WIN32 + if (sig == SIGALRM && absolute_real_time () >= alarm_time) { + if (!caught_alarm) { + caught_alarm = true; + if (signal_handler) + signal_handler->catch_alarm (); + } + Signal::reset_alarm (); + } else +#endif + { + if (!caught_signal) { + caught_signal = true; + if (signal_handler) + signal_handler->catch_signal (sig); + } + Signal::reset (); + ::raise (sig); + } +} + +void Signal::set (Handler *h) { + signal_handler = h; +#define SIGNAL(SIG) SIG##_handler = signal (SIG, catch_signal); + SIGNALS +#undef SIGNAL +} + +#ifndef __WIN32 + +void Signal::alarm (int seconds) { + assert (seconds >= 0); + assert (!alarm_set); + assert (alarm_time < 0); + SIGALRM_handler = signal (SIGALRM, catch_signal); + alarm_set = true; + alarm_time = absolute_real_time () + seconds; + ::alarm (seconds); +} + +#endif + +} // namespace CaDiCaL diff --git a/src/sat/cadical/signal.hpp b/src/sat/cadical/signal.hpp new file mode 100644 index 000000000..5984b66f1 --- /dev/null +++ b/src/sat/cadical/signal.hpp @@ -0,0 +1,33 @@ +#ifndef _signal_hpp_INCLUDED +#define _signal_hpp_INCLUDED + +namespace CaDiCaL { + +// Helper class for handling signals in applications. + +class Handler { +public: + Handler () {} + virtual ~Handler () {} + virtual void catch_signal (int sig) = 0; +#ifndef __WIN32 + virtual void catch_alarm (); +#endif +}; + +class Signal { + +public: + static void set (Handler *); + static void reset (); +#ifndef __WIN32 + static void alarm (int seconds); + static void reset_alarm (); +#endif + + static const char *name (int sig); +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/solution.cpp b/src/sat/cadical/solution.cpp new file mode 100644 index 000000000..8427d14e3 --- /dev/null +++ b/src/sat/cadical/solution.cpp @@ -0,0 +1,50 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +// Sam Buss suggested to debug the case where a solver incorrectly claims +// the formula to be unsatisfiable by checking every learned clause to be +// satisfied by a satisfying assignment. Thus the first inconsistent +// learned clause will be immediately flagged without the need to generate +// proof traces and perform forward proof checking. The incorrectly derived +// clause will raise an abort signal and thus allows to debug the issue with +// a symbolic debugger immediately. + +void External::check_solution_on_learned_clause () { + assert (solution); + for (const auto &lit : internal->clause) + if (sol (internal->externalize (lit)) == lit) + return; + fatal_message_start (); + fputs ("learned clause unsatisfied by solution:\n", stderr); + for (const auto &lit : internal->clause) + fprintf (stderr, "%d ", lit); + fputc ('0', stderr); + fatal_message_end (); +} + +void External::check_solution_on_shrunken_clause (Clause *c) { + assert (solution); + for (const auto &lit : *c) + if (sol (internal->externalize (lit)) == lit) + return; + fatal_message_start (); + for (const auto &lit : *c) + fprintf (stderr, "%d ", lit); + fputc ('0', stderr); + fatal_message_end (); +} + +void External::check_no_solution_after_learning_empty_clause () { + assert (solution); + FATAL ("learned empty clause but got solution"); +} + +void External::check_solution_on_learned_unit_clause (int unit) { + assert (solution); + if (sol (internal->externalize (unit)) == unit) + return; + FATAL ("learned unit %d contradicts solution", unit); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/solver.cpp b/src/sat/cadical/solver.cpp new file mode 100644 index 000000000..f125cc32f --- /dev/null +++ b/src/sat/cadical/solver.cpp @@ -0,0 +1,1758 @@ +#include "internal.hpp" + +/*------------------------------------------------------------------------*/ + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// See corresponding header file 'cadical.hpp' (!) for more information. +// +// Again, to avoid confusion, note that, 'cadical.hpp' is the header file of +// this file 'solver.cpp', since we want to call the application and main +// file 'cadical.cpp', while at the same time using 'cadical.hpp' as the +// main header file of the library (and not 'solver.hpp'). + +/*------------------------------------------------------------------------*/ +#ifdef LOGGING + +// Needs to be kept in sync with the color schemes used in 'logging.cpp'. +// +#define api_code blue_code // API call color +#define log_code magenta_code // standard/default logging color +#define emph_code bright_magenta_code // emphasized logging color + +#endif +/*------------------------------------------------------------------------*/ + +// Log state transitions. + +#define STATE(S) \ + do { \ + assert (is_power_of_two (S)); \ + if (_state == S) \ + break; \ + _state = S; \ + LOG ("API enters state %s" #S "%s", tout.emph_code (), \ + tout.normal_code ()); \ + } while (0) + +void Solver::transition_to_steady_state () { + if (state () == CONFIGURING) { + LOG ("API leaves state %sCONFIGURING%s", tout.emph_code (), + tout.normal_code ()); + if (internal->opts.check && internal->opts.checkproof) { + internal->check (); + } + } else if (state () == SATISFIED) { + LOG ("API leaves state %sSATISFIED%s", tout.emph_code (), + tout.normal_code ()); + external->reset_assumptions (); + external->reset_concluded (); + external->reset_constraint (); + } else if (state () == UNSATISFIED) { + LOG ("API leaves state %sUNSATISFIED%s", tout.emph_code (), + tout.normal_code ()); + external->reset_assumptions (); + external->reset_concluded (); + external->reset_constraint (); + } else if (state() == INCONCLUSIVE) { + external->reset_assumptions (); + external->reset_concluded (); + external->reset_constraint (); + } + if (state () != STEADY) + STATE (STEADY); +} + +/*------------------------------------------------------------------------*/ +#ifdef LOGGING +/*------------------------------------------------------------------------*/ + +// The following logging code is useful for debugging mostly (or trying to +// understand what the solver is actually doing). It needs to be enabled +// during configuration using the '-l' option for './configure', which +// forces 'LOGGING' to be defined during compilation. This includes all the +// logging code, which then still needs to enabled during run-time with the +// '-l' or 'log' option. + +static void log_api_call (Internal *internal, const char *name, + const char *suffix) { + Logger::log (internal, "API call %s'%s ()'%s %s", tout.api_code (), name, + tout.log_code (), suffix); +} + +static void log_api_call (Internal *internal, const char *name, int arg, + const char *suffix) { + Logger::log (internal, "API call %s'%s (%d)'%s %s", tout.api_code (), + name, arg, tout.log_code (), suffix); +} + +static void log_api_call (Internal *internal, const char *name, + const char *arg, const char *suffix) { + Logger::log (internal, "API call %s'%s (\"%s\")'%s %s", tout.api_code (), + name, arg, tout.log_code (), suffix); +} + +static void log_api_call (Internal *internal, const char *name, + const char *a1, int a2, const char *s) { + Logger::log (internal, "API call %s'%s (\"%s\", %d)'%s %s", + tout.api_code (), name, a1, a2, tout.log_code (), s); +} + +/*------------------------------------------------------------------------*/ + +// We factored out API call begin/end logging and use overloaded functions. + +static void log_api_call_begin (Internal *internal, const char *name) { + Logger::log_empty_line (internal); + log_api_call (internal, name, "started"); +} + +static void log_api_call_begin (Internal *internal, const char *name, + int arg) { + Logger::log_empty_line (internal); + log_api_call (internal, name, arg, "started"); +} + +static void log_api_call_begin (Internal *internal, const char *name, + const char *arg) { + Logger::log_empty_line (internal); + log_api_call (internal, name, arg, "started"); +} + +static void log_api_call_begin (Internal *internal, const char *name, + const char *arg1, int arg2) { + Logger::log_empty_line (internal); + log_api_call (internal, name, arg1, arg2, "started"); +} + +/*------------------------------------------------------------------------*/ + +static void log_api_call_end (Internal *internal, const char *name) { + log_api_call (internal, name, "succeeded"); +} + +static void log_api_call_end (Internal *internal, const char *name, + int lit) { + log_api_call (internal, name, lit, "succeeded"); +} + +static void log_api_call_end (Internal *internal, const char *name, + const char *arg) { + Logger::log_empty_line (internal); + log_api_call (internal, name, arg, "succeeded"); +} + +static void log_api_call_end (Internal *internal, const char *name, + const char *arg, bool res) { + log_api_call (internal, name, arg, res ? "succeeded" : "failed"); +} + +static void log_api_call_end (Internal *internal, const char *name, + const char *arg, int val, bool res) { + log_api_call (internal, name, arg, val, res ? "succeeded" : "failed"); +} + +static void log_api_call_returns (Internal *internal, const char *name, + bool res) { + log_api_call (internal, name, res ? "returns 'true'" : "returns 'false'"); +} + +static void log_api_call_returns (Internal *internal, const char *name, + int res) { + char fmt[32]; + snprintf (fmt, sizeof fmt, "returns '%d'", res); + log_api_call (internal, name, fmt); +} + +static void log_api_call_returns (Internal *internal, const char *name, + int64_t res) { + char fmt[32]; + snprintf (fmt, sizeof fmt, "returns '%" PRId64 "'", res); + log_api_call (internal, name, fmt); +} + +static void log_api_call_returns (Internal *internal, const char *name, + int lit, int res) { + char fmt[32]; + snprintf (fmt, sizeof fmt, "returns '%d'", res); + log_api_call (internal, name, lit, fmt); +} + +static void log_api_call_returns (Internal *internal, const char *name, + const char *arg, bool res) { + log_api_call (internal, name, arg, + res ? "returns 'true'" : "returns 'false'"); +} + +static void log_api_call_returns (Internal *internal, const char *name, + int lit, bool res) { + log_api_call (internal, name, lit, + res ? "returns 'true'" : "returns 'false'"); +} + +static void log_api_call_returns (Internal *internal, const char *name, + const char *arg, const char *res) { + Logger::log (internal, "API call %s'%s (\"%s\")'%s returns '%s'", + tout.api_code (), name, arg, tout.log_code (), + res ? res : "<null>"); +} + +static void log_api_call_returns (Internal *internal, const char *name, + const char *arg1, int arg2, + const char *res) { + Logger::log (internal, "API call %s'%s (\"%s\", %d)'%s returns '%s'", + tout.api_code (), name, arg1, arg2, tout.log_code (), + res ? res : "<null>"); +} + +/*------------------------------------------------------------------------*/ + +#define LOG_API_CALL_BEGIN(...) \ + do { \ + if (!internal->opts.log) \ + break; \ + log_api_call_begin (internal, __VA_ARGS__); \ + } while (0) + +#define LOG_API_CALL_END(...) \ + do { \ + if (!internal->opts.log) \ + break; \ + log_api_call_end (internal, __VA_ARGS__); \ + } while (0) + +#define LOG_API_CALL_RETURNS(...) \ + do { \ + if (!internal->opts.log) \ + break; \ + log_api_call_returns (internal, __VA_ARGS__); \ + } while (0) + +/*------------------------------------------------------------------------*/ +#else // end of 'then' part of 'ifdef LOGGING' +/*------------------------------------------------------------------------*/ + +#define LOG_API_CALL_BEGIN(...) \ + do { \ + } while (0) +#define LOG_API_CALL_END(...) \ + do { \ + } while (0) +#define LOG_API_CALL_RETURNS(...) \ + do { \ + } while (0) + +/*------------------------------------------------------------------------*/ +#endif // end of 'else' part of 'ifdef LOGGING' +/*------------------------------------------------------------------------*/ + +/*------------------------------------------------------------------------*/ +#ifndef NTRACING +/*------------------------------------------------------------------------*/ + +#define TRACE(...) \ + do { \ + /*if ((this == 0)) break; */ /* gcc-12 produces warning */ \ + if ((internal == 0)) \ + break; \ + LOG_API_CALL_BEGIN (__VA_ARGS__); \ + if (!trace_api_file) \ + break; \ + trace_api_call (__VA_ARGS__); \ + } while (0) + +void Solver::trace_api_call (const char *s0) const { + assert (trace_api_file); + LOG ("TRACE %s", s0); + fprintf (trace_api_file, "%s\n", s0); + fflush (trace_api_file); +} + +void Solver::trace_api_call (const char *s0, int i1) const { + assert (trace_api_file); + LOG ("TRACE %s %d", s0, i1); + fprintf (trace_api_file, "%s %d\n", s0, i1); + fflush (trace_api_file); +} + +void Solver::trace_api_call (const char *s0, const char *s1) const { + assert (trace_api_file); + LOG ("TRACE %s %s", s0, s1); + fprintf (trace_api_file, "%s %s\n", s0, s1); + fflush (trace_api_file); +} + +void Solver::trace_api_call (const char *s0, const char *s1, int i2) const { + assert (trace_api_file); + LOG ("TRACE %s %s %d", s0, s1, i2); + fprintf (trace_api_file, "%s %s %d\n", s0, s1, i2); + fflush (trace_api_file); +} + +/*------------------------------------------------------------------------*/ + +// The global 'tracing_api_calls_through_environment_variable_method' flag +// is used to ensure that only one solver traces to a file. Otherwise the +// method to use an environment variable to point to the trace file is +// bogus, since those different solver instances would all write to the same +// file producing garbage. A more sophisticated solution would use a +// different mechanism to tell the solver to which file to trace to, but in +// our experience it is quite convenient to get traces out of applications +// which use the solver as library by just setting an environment variable +// without requiring to change any application code. +// +static bool tracing_api_calls_through_environment_variable_method; + +/*------------------------------------------------------------------------*/ +#else // NTRACING +/*------------------------------------------------------------------------*/ + +#define TRACE(...) \ + do { \ + } while (0) + +/*------------------------------------------------------------------------*/ +#endif +/*------------------------------------------------------------------------*/ + +static bool tracing_nb_lidrup_env_var_method = false; + +Solver::Solver () { + +#ifndef NTRACING + const char *path = getenv ("CADICAL_API_TRACE"); + if (!path) + path = getenv ("CADICALAPITRACE"); + if (path) { + if (tracing_api_calls_through_environment_variable_method) + FATAL ("can not trace API calls of two solver instances " + "using environment variable 'CADICAL_API_TRACE'"); + if (!(trace_api_file = fopen (path, "w"))) + FATAL ("failed to open file '%s' to trace API calls " + "using environment variable 'CADICAL_API_TRACE'", + path); + close_trace_api_file = true; + tracing_api_calls_through_environment_variable_method = true; + } else { + tracing_api_calls_through_environment_variable_method = false; + close_trace_api_file = false; + trace_api_file = 0; + } +#endif + + adding_clause = false; + adding_constraint = false; + _state = INITIALIZING; + internal = new Internal (); + DeferDeletePtr<Internal> delete_internal (internal); + TRACE ("init"); + external = new External (internal); + DeferDeletePtr<External> delete_external (external); + STATE (CONFIGURING); +#ifndef NTRACING + if (tracing_api_calls_through_environment_variable_method) + message ("tracing API calls to '%s'", path); +#endif + + const char *lidrup_path = getenv ("CADICAL_LIDRUP_TRACE"); + if (!lidrup_path) + lidrup_path = getenv ("CADICALLIDRUPTRACE"); + if (lidrup_path) { + + // if (tracing_nb_lidrup_env_var_method) + // FATAL ("can not trace LIDRUP of two solver instances " + // "using environment variable 'CADICAL_LIDRUP_TRACE'"); + // Here we use the solver interface to setup non-binary IDRUP tracing to + // the defined file. Options set by the user can and will overwrite + // these settings if neeed be. + set ("lidrup", 1); + set ("binary", 0); + trace_proof (lidrup_path); + tracing_nb_lidrup_env_var_method = true; + } else { + tracing_nb_lidrup_env_var_method = false; + } + + delete_internal.release (); + delete_external.release (); +} + +Solver::~Solver () { + + TRACE ("reset"); + REQUIRE_VALID_OR_SOLVING_STATE (); + STATE (DELETING); + + tracing_nb_lidrup_env_var_method = false; + +#ifdef LOGGING + // + // After deleting 'internal' logging does not work anymore. + // + bool logging = internal->opts.log; + int level = internal->level; + string prefix = internal->prefix; +#endif + + delete internal; + delete external; + +#ifndef NTRACING + if (close_trace_api_file) { + close_trace_api_file = false; + assert (trace_api_file); + assert (tracing_api_calls_through_environment_variable_method); + fclose (trace_api_file); + tracing_api_calls_through_environment_variable_method = false; + } +#endif + +#ifdef LOGGING + // + // Need to log success of this API call manually. + // + if (logging) { + printf ("%s%sLOG %s%d%s API call %s'reset ()'%s succeeded%s\n", + prefix.c_str (), tout.log_code (), tout.emph_code (), level, + tout.log_code (), tout.api_code (), tout.log_code (), + tout.normal_code ()); + fflush (stdout); + } +#endif +} + +/*------------------------------------------------------------------------*/ + +int Solver::vars () { + TRACE ("vars"); + REQUIRE_VALID_OR_SOLVING_STATE (); + int res = external->max_var; + LOG_API_CALL_RETURNS ("vars", res); + return res; +} + +void Solver::reserve (int min_max_var) { + TRACE ("reserve", min_max_var); + REQUIRE_VALID_STATE (); + transition_to_steady_state (); + external->reset_extended (); + external->init (min_max_var); + LOG_API_CALL_END ("reserve", min_max_var); +} + +int Solver::reserve_difference (int number_of_vars) { + TRACE ("reserve_difference", number_of_vars); + REQUIRE_VALID_STATE (); + transition_to_steady_state (); + external->reset_extended (); + int new_max_var = external->max_var + number_of_vars; + external->init (new_max_var); + LOG_API_CALL_END ("reserve_difference", number_of_vars); + return new_max_var; +} + +/*------------------------------------------------------------------------*/ +#ifndef NTRACING + +void Solver::trace_api_calls (FILE *file) { + LOG_API_CALL_BEGIN ("trace_api_calls"); + REQUIRE_VALID_STATE (); + REQUIRE (file != 0, "invalid zero file argument"); + REQUIRE (!tracing_api_calls_through_environment_variable_method, + "already tracing API calls " + "using environment variable 'CADICAL_API_TRACE'"); + REQUIRE (!trace_api_file, "called twice"); + trace_api_file = file; + LOG_API_CALL_END ("trace_api_calls"); + trace_api_call ("init"); +} + +#endif +/*------------------------------------------------------------------------*/ + +bool Solver::is_valid_option (const char *name) { + return Options::has (name); +} + +bool Solver::is_preprocessing_option (const char *name) { + return Options::is_preprocessing_option (name); +} + +bool Solver::is_valid_long_option (const char *arg) { + string name; + int tmp; + return Options::parse_long_option (arg, name, tmp); +} + +int Solver::get (const char *arg) { + REQUIRE_VALID_OR_SOLVING_STATE (); + return internal->opts.get (arg); +} + +bool Solver::set (const char *arg, int val) { + TRACE ("set", arg, val); + REQUIRE_VALID_STATE (); + if (strcmp (arg, "log") && strcmp (arg, "quiet") && + strcmp (arg, "report") && strcmp (arg, "verbose")) { + REQUIRE ( + state () == CONFIGURING, + "can only set option 'set (\"%s\", %d)' right after initialization", + arg, val); + } + bool res = internal->opts.set (arg, val); + LOG_API_CALL_END ("set", arg, val, res); + + return res; +} + +bool Solver::set_long_option (const char *arg) { + LOG_API_CALL_BEGIN ("set", arg); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only set option '%s' right after initialization", arg); + bool res; + if (arg[0] != '-' || arg[1] != '-') + res = false; + else { + int val; + string name; + res = Options::parse_long_option (arg, name, val); + if (res) + set (name.c_str (), val); + } + LOG_API_CALL_END ("set", arg, res); + return res; +} + +void Solver::optimize (int arg) { + LOG_API_CALL_BEGIN ("optimize", arg); + REQUIRE_VALID_STATE (); + internal->opts.optimize (arg); + LOG_API_CALL_END ("optimize", arg); +} + +bool Solver::limit (const char *arg, int val) { + TRACE ("limit", arg, val); + REQUIRE_VALID_STATE (); + bool res = internal->limit (arg, val); + LOG_API_CALL_END ("limit", arg, val, res); + return res; +} + +bool Solver::is_valid_limit (const char *arg) { + return Internal::is_valid_limit (arg); +} + +void Solver::prefix (const char *str) { + LOG_API_CALL_BEGIN ("prefix", str); + REQUIRE_VALID_OR_SOLVING_STATE (); + internal->prefix = str; + LOG_API_CALL_END ("prefix", str); +} + +bool Solver::is_valid_configuration (const char *name) { + return Config::has (name); +} + +bool Solver::configure (const char *name) { + TRACE ("configure", name); + LOG_API_CALL_BEGIN ("configure", name); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only set configuration '%s' right after initialization", + name); + bool res = Config::set (internal->opts, name); + LOG_API_CALL_END ("configure", name, res); + return res; +} + +/*===== IPASIR BEGIN =====================================================*/ + +void Solver::add (int lit) { + TRACE ("add", lit); + REQUIRE_VALID_STATE (); + if (lit) + REQUIRE_VALID_LIT (lit); + transition_to_steady_state (); + external->add (lit); + adding_clause = lit; + if (adding_clause) + STATE (ADDING); + else if (!adding_constraint) + STATE (STEADY); + LOG_API_CALL_END ("add", lit); +} + +void Solver::clause (int a) { + REQUIRE_VALID_LIT (a); + add (a), add (0); +} + +void Solver::clause (int a, int b) { + REQUIRE_VALID_LIT (a); + REQUIRE_VALID_LIT (b); + add (a), add (b), add (0); +} + +void Solver::clause (int a, int b, int c) { + REQUIRE_VALID_LIT (a); + REQUIRE_VALID_LIT (b); + REQUIRE_VALID_LIT (c); + add (a), add (b), add (c), add (0); +} + +void Solver::clause (int a, int b, int c, int d) { + REQUIRE_VALID_LIT (a); + REQUIRE_VALID_LIT (b); + REQUIRE_VALID_LIT (c); + REQUIRE_VALID_LIT (d); + add (a), add (b), add (c), add (d), add (0); +} + +void Solver::clause (int a, int b, int c, int d, int e) { + REQUIRE_VALID_LIT (a); + REQUIRE_VALID_LIT (b); + REQUIRE_VALID_LIT (c); + REQUIRE_VALID_LIT (d); + REQUIRE_VALID_LIT (e); + add (a), add (b), add (c), add (d), add (e), add (0); +} + +void Solver::clause (const int *lits, size_t size) { + REQUIRE (!size || lits, + "first argument 'lits' zero while second argument 'size' not"); + const int *end = lits + size; + for (const int *p = lits; p != end; p++) { + const int lit = *p; + REQUIRE_VALID_LIT (lit); + add (lit); + } + add (0); +} + +void Solver::clause (const std::vector<int> &lits) { + for (auto lit : lits) { + REQUIRE_VALID_LIT (lit); + add (lit); + } + add (0); +} + +bool Solver::inconsistent () { return internal->unsat; } + +void Solver::constrain (int lit) { + TRACE ("constrain", lit); + REQUIRE_VALID_STATE (); + if (lit) + REQUIRE_VALID_LIT (lit); + transition_to_steady_state (); + external->constrain (lit); + adding_constraint = lit; + if (adding_constraint) + STATE (ADDING); + else if (!adding_clause) + STATE (STEADY); + LOG_API_CALL_END ("constrain", lit); +} + +void Solver::assume (int lit) { + TRACE ("assume", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + transition_to_steady_state (); + external->assume (lit); + LOG_API_CALL_END ("assume", lit); +} + +int Solver::lookahead () { + TRACE ("lookahead"); + REQUIRE_VALID_OR_SOLVING_STATE (); + int lit = external->lookahead (); + TRACE ("lookahead"); + return lit; +} + +Solver::CubesWithStatus Solver::generate_cubes (int depth, int min_depth) { + TRACE ("lookahead_cubes"); + REQUIRE_VALID_OR_SOLVING_STATE (); + auto cubes = external->generate_cubes (depth, min_depth); + TRACE ("lookahead_cubes"); + + CubesWithStatus cubes2; + cubes2.status = cubes.status; + cubes2.cubes = cubes.cubes; + return cubes2; +} + +void Solver::reset_assumptions () { + TRACE ("reset_assumptions"); + REQUIRE_VALID_STATE (); + transition_to_steady_state (); + external->reset_assumptions (); + external->reset_concluded (); + LOG_API_CALL_END ("reset_assumptions"); +} + +void Solver::reset_constraint () { + TRACE ("reset_constraint"); + REQUIRE_VALID_STATE (); + transition_to_steady_state (); + external->reset_constraint (); + external->reset_concluded (); + LOG_API_CALL_END ("reset_constraint"); +} + +/*------------------------------------------------------------------------*/ + +int Solver::propagate () { + TRACE ("propagate_assumptions"); + REQUIRE_VALID_STATE (); + transition_to_steady_state (); + const int res = external->propagate_assumptions (); + if (tracing_nb_lidrup_env_var_method) + flush_proof_trace (true); + LOG_API_CALL_RETURNS ("propagate_assumptions", res); + if (res == 10) + STATE (SATISFIED); + else if (res == 20) + STATE (UNSATISFIED); + else + STATE (INCONCLUSIVE); + return res; +} + +void Solver::implied (std::vector<int> &entrailed) { + TRACE ("implied"); + REQUIRE_VALID_STATE (); + REQUIRE (state () == INCONCLUSIVE, + "can only get implied literals only in unknown state"); + external->conclude_unknown (); + external->implied (entrailed); + if (tracing_nb_lidrup_env_var_method) + flush_proof_trace (true); + LOG_API_CALL_RETURNS ("implied", (int) entrailed.size ()); +} + +/*------------------------------------------------------------------------*/ + +int Solver::call_external_solve_and_check_results (bool preprocess_only) { + transition_to_steady_state (); + assert (state () & READY); + STATE (SOLVING); + const int res = external->solve (preprocess_only); + if (res == 10) + STATE (SATISFIED); + else if (res == 20) + STATE (UNSATISFIED); + else + STATE (INCONCLUSIVE); +#if 0 // EXPENSIVE ALTERNATIVE ASSUMPTION CHECKING + // This checks that the set of failed assumptions form a core using the + // external 'copy (...)' function to copy the solver, which can be trusted + // less, since it involves copying the extension stack too. The + // 'External::check_assumptions_failing' is a better alternative and can + // be enabled by options too. We keep this code though to have an + // alternative failed assumption checking available for debugging. + // + if (res == 20 && !external->assumptions.empty ()) { + Solver checker; + // checking restored clauses does not work (because the clauses are not added) + checker.set("checkproof", 1); + checker.set("lrat", 0); + checker.prefix ("checker "); + copy (checker); + checker.set("log", 1); + for (const auto & lit : external->assumptions) + if (failed (lit)) + checker.add (lit), checker.add (0); + if (checker.solve () != 20) + FATAL ("copying assumption checker failed"); + } +#endif + if (!res) { + external->reset_assumptions (); + external->reset_constraint (); + external->reset_concluded (); + } + return res; +} + +int Solver::solve () { + TRACE ("solve"); + REQUIRE_READY_STATE (); + const int res = call_external_solve_and_check_results (false); + LOG_API_CALL_RETURNS ("solve", res); + if (tracing_nb_lidrup_env_var_method) + flush_proof_trace (true); + return res; +} + +int Solver::simplify (int rounds) { + TRACE ("simplify", rounds); + REQUIRE_READY_STATE (); + REQUIRE (rounds >= 0, "negative number of simplification rounds '%d'", + rounds); + internal->limit ("preprocessing", rounds); + const int res = call_external_solve_and_check_results (true); + LOG_API_CALL_RETURNS ("simplify", rounds, res); + return res; +} + +/*------------------------------------------------------------------------*/ + +int Solver::val (int lit) { + TRACE ("val", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + REQUIRE (state () == SATISFIED, "can only get value in satisfied state"); + if (!external->extended) + external->extend (); + external->conclude_sat (); + int res = external->ival (lit); + LOG_API_CALL_RETURNS ("val", lit, res); + assert (state () == SATISFIED); + assert (res == lit || res == -lit); + return res; +} + +bool Solver::flip (int lit) { + TRACE ("flip", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + REQUIRE (state () == SATISFIED, "can only flip value in satisfied state"); + REQUIRE (!external->propagator, + "can only flip when no external propagator is present"); + bool res = external->flip (lit); + LOG_API_CALL_RETURNS ("flip", lit, res); + assert (state () == SATISFIED); + return res; +} + +bool Solver::flippable (int lit) { + TRACE ("flippable", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + REQUIRE (state () == SATISFIED, "can only flip value in satisfied state"); + REQUIRE (!external->propagator, + "can only flip when no external propagator is present"); + bool res = external->flippable (lit); + LOG_API_CALL_RETURNS ("flippable", lit, res); + assert (state () == SATISFIED); + return res; +} + +bool Solver::failed (int lit) { + TRACE ("failed", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + REQUIRE (state () == UNSATISFIED, + "can only get failed assumptions in unsatisfied state"); + bool res = external->failed (lit); + LOG_API_CALL_RETURNS ("failed", lit, res); + assert (state () == UNSATISFIED); + return res; +} + +bool Solver::constraint_failed () { + TRACE ("constraint_failed"); + REQUIRE_VALID_STATE (); + REQUIRE (state () == UNSATISFIED, + "can only determine if constraint failed in unsatisfied state"); + bool res = external->failed_constraint (); + LOG_API_CALL_RETURNS ("constraint_failed", res); + assert (state () == UNSATISFIED); + return res; +} + +int Solver::fixed (int lit) const { + TRACE ("fixed", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + int res = external->fixed (lit); + LOG_API_CALL_RETURNS ("fixed", lit, res); + return res; +} + +void Solver::phase (int lit) { + TRACE ("phase", lit); + REQUIRE_VALID_OR_SOLVING_STATE (); + REQUIRE_VALID_LIT (lit); + external->phase (lit); + LOG_API_CALL_END ("phase", lit); +} + +void Solver::unphase (int lit) { + TRACE ("unphase", lit); + REQUIRE_VALID_OR_SOLVING_STATE (); + REQUIRE_VALID_LIT (lit); + external->unphase (lit); + LOG_API_CALL_END ("unphase", lit); +} + +/*------------------------------------------------------------------------*/ + +void Solver::terminate () { + LOG_API_CALL_BEGIN ("terminate"); + REQUIRE_VALID_OR_SOLVING_STATE (); + external->terminate (); + LOG_API_CALL_END ("terminate"); +} + +void Solver::connect_terminator (Terminator *terminator) { + LOG_API_CALL_BEGIN ("connect_terminator"); + REQUIRE_VALID_STATE (); + REQUIRE (terminator, "can not connect zero terminator"); +#ifdef LOGGING + if (external->terminator) + LOG ("connecting new terminator (disconnecting previous one)"); + else + LOG ("connecting new terminator (no previous one)"); +#endif + external->terminator = terminator; + LOG_API_CALL_END ("connect_terminator"); +} + +void Solver::disconnect_terminator () { + LOG_API_CALL_BEGIN ("disconnect_terminator"); + REQUIRE_VALID_STATE (); +#ifdef LOGGING + if (external->terminator) + LOG ("disconnecting previous terminator"); + else + LOG ("ignoring to disconnect terminator (no previous one)"); +#endif + external->terminator = 0; + LOG_API_CALL_END ("disconnect_terminator"); +} + +/*------------------------------------------------------------------------*/ + +void Solver::connect_learner (Learner *learner) { + LOG_API_CALL_BEGIN ("connect_learner"); + REQUIRE_VALID_STATE (); + REQUIRE (learner, "can not connect zero learner"); +#ifdef LOGGING + if (external->learner) + LOG ("connecting new learner (disconnecting previous one)"); + else + LOG ("connecting new learner (no previous one)"); +#endif + external->learner = learner; + LOG_API_CALL_END ("connect_learner"); +} + +void Solver::disconnect_learner () { + LOG_API_CALL_BEGIN ("disconnect_learner"); + REQUIRE_VALID_STATE (); +#ifdef LOGGING + if (external->learner) + LOG ("disconnecting previous learner"); + else + LOG ("ignoring to disconnect learner (no previous one)"); +#endif + external->learner = 0; + LOG_API_CALL_END ("disconnect_learner"); +} + +/*===== IPASIR END =======================================================*/ + +void Solver::connect_fixed_listener ( + FixedAssignmentListener *fixed_listener) { + LOG_API_CALL_BEGIN ("connect_fixed_listener"); + REQUIRE_VALID_STATE (); + REQUIRE (fixed_listener, "can not connect zero fixed listener"); + +#ifdef LOGGING + if (external->fixed_listener) + LOG ("connecting new listener of fixed assignments (disconnecting " + "previous one)"); + else + LOG ("connecting new listener of fixed assigments (no previous one)"); +#endif + if (external->fixed_listener) + disconnect_fixed_listener (); + external->fixed_listener = fixed_listener; + // Listeners are treated as real-time listeners, thus previously found + // fixed assignments are not sent out (would be rather expensive to + // recover it retrospect, see external_propagate.cpp/get_fixed_literals () + // function). + LOG_API_CALL_END ("connect_fixed_listener"); +} + +void Solver::disconnect_fixed_listener () { + LOG_API_CALL_BEGIN ("disconnect_fixed_listener"); + REQUIRE_VALID_STATE (); +#ifdef LOGGING + if (external->fixed_listener) + LOG ("disconnecting previous listener of fixed assignments"); + else + LOG ("ignoring to disconnect listener of fixed assignments (no " + "previous one)"); +#endif + external->fixed_listener = 0; + LOG_API_CALL_END ("disconnect_fixed_listener"); +} + +/*===== IPASIR-UP BEGIN ==================================================*/ + +void Solver::connect_external_propagator (ExternalPropagator *propagator) { + LOG_API_CALL_BEGIN ("connect_external_propagator"); + REQUIRE_VALID_STATE (); + REQUIRE (propagator, "can not connect zero propagator"); +#ifdef LOGGING + if (external->propagator) + LOG ("connecting new external propagator (disconnecting previous one)"); + else + LOG ("connecting new external propagator (no previous one)"); +#endif + if (external->propagator) + disconnect_external_propagator (); + + external->propagator = propagator; + internal->connect_propagator (); + internal->external_prop = true; + internal->external_prop_is_lazy = propagator->is_lazy; + LOG_API_CALL_END ("connect_external_propagator"); +} + +void Solver::disconnect_external_propagator () { + LOG_API_CALL_BEGIN ("disconnect_external_propagator"); + REQUIRE_VALID_STATE (); + +#ifdef LOGGING + if (external->propagator) + LOG ("disconnecting previous external propagator"); + else + LOG ("ignoring to disconnect external propagator (no previous one)"); +#endif + if (external->propagator) + external->reset_observed_vars (); + + external->propagator = 0; + internal->set_tainted_literal (); + internal->external_prop = false; + internal->external_prop_is_lazy = true; + LOG_API_CALL_END ("disconnect_external_propagator"); +} + +void Solver::add_observed_var (int idx) { + TRACE ("observe", idx); + REQUIRE_VALID_OR_SOLVING_STATE (); + REQUIRE_VALID_LIT (idx); + external->add_observed_var (idx); + LOG_API_CALL_END ("observe", idx); +} + +void Solver::remove_observed_var (int idx) { + TRACE ("unobserve", idx); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (idx); + external->remove_observed_var (idx); + LOG_API_CALL_END ("unobserve", idx); +} + +void Solver::reset_observed_vars () { + TRACE ("reset_observed_vars"); + REQUIRE_VALID_OR_SOLVING_STATE (); + external->reset_observed_vars (); + LOG_API_CALL_END ("reset_observed_vars"); +} + +/*===== IPASIR-UP END ====================================================*/ + +int Solver::active () const { + TRACE ("active"); + REQUIRE_VALID_STATE (); + int res = internal->active (); + LOG_API_CALL_RETURNS ("active", res); + return res; +} + +int64_t Solver::redundant () const { + TRACE ("redundant"); + REQUIRE_VALID_STATE (); + int64_t res = internal->redundant (); + LOG_API_CALL_RETURNS ("redundant", res); + return res; +} + +int64_t Solver::irredundant () const { + TRACE ("irredundant"); + REQUIRE_VALID_STATE (); + int64_t res = internal->irredundant (); + LOG_API_CALL_RETURNS ("irredundant", res); + return res; +} + +/*------------------------------------------------------------------------*/ + +void Solver::freeze (int lit) { + TRACE ("freeze", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + external->freeze (lit); + LOG_API_CALL_END ("freeze", lit); +} + +void Solver::melt (int lit) { + TRACE ("melt", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + REQUIRE (external->frozen (lit), + "can not melt completely melted literal '%d'", lit); + external->melt (lit); + LOG_API_CALL_END ("melt", lit); +} + +bool Solver::frozen (int lit) const { + TRACE ("frozen", lit); + REQUIRE_VALID_STATE (); + REQUIRE_VALID_LIT (lit); + bool res = external->frozen (lit); + LOG_API_CALL_RETURNS ("frozen", lit, res); + return res; +} + +/*------------------------------------------------------------------------*/ + +bool Solver::trace_proof (FILE *external_file, const char *name) { + TRACE ("trace_proof", name); + REQUIRE_VALID_STATE (); + REQUIRE ( + state () == CONFIGURING, + "can only start proof tracing to '%s' right after initialization", + name); + File *internal_file = File::write (internal, external_file, name); + assert (internal_file); + internal->trace (internal_file); + LOG_API_CALL_RETURNS ("trace_proof", name, true); + return true; +} + +bool Solver::trace_proof (const char *path) { + TRACE ("trace_proof", path); + REQUIRE_VALID_STATE (); + REQUIRE ( + state () == CONFIGURING, + "can only start proof tracing to '%s' right after initialization", + path); + File *internal_file = File::write (internal, path); + bool res = (internal_file != 0); + internal->trace (internal_file); + LOG_API_CALL_RETURNS ("trace_proof", path, res); + return res; +} + +void Solver::flush_proof_trace (bool print_statistics_unless_quiet) { + TRACE ("flush_proof_trace"); + REQUIRE_VALID_STATE (); + REQUIRE (!internal->file_tracers.empty (), "proof is not traced"); + REQUIRE (!internal->file_tracers.back ()->closed (), + "proof trace already closed"); + internal->flush_trace (print_statistics_unless_quiet); + LOG_API_CALL_END ("flush_proof_trace"); +} + +void Solver::close_proof_trace (bool print_statistics_unless_quiet) { + TRACE ("close_proof_trace"); + REQUIRE_VALID_STATE (); + REQUIRE (!internal->file_tracers.empty (), "proof is not traced"); + REQUIRE (!internal->file_tracers.back ()->closed (), + "proof trace already closed"); + internal->close_trace (print_statistics_unless_quiet); + LOG_API_CALL_END ("close_proof_trace"); +} + +/*------------------------------------------------------------------------*/ + +void Solver::connect_proof_tracer (Tracer *tracer, bool antecedents, + bool finalize_clauses) { + LOG_API_CALL_BEGIN ("connect proof tracer"); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only start proof tracing to right after initialization"); + REQUIRE (tracer, "can not connect zero tracer"); + internal->connect_proof_tracer (tracer, antecedents, finalize_clauses); + LOG_API_CALL_END ("connect proof tracer"); +} + +void Solver::connect_proof_tracer (InternalTracer *tracer, bool antecedents, + bool finalize_clauses) { + LOG_API_CALL_BEGIN ("connect proof tracer"); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only start proof tracing to right after initialization"); + REQUIRE (tracer, "can not connect zero tracer"); + internal->connect_proof_tracer (tracer, antecedents, finalize_clauses); + LOG_API_CALL_END ("connect proof tracer"); +} + +void Solver::connect_proof_tracer (StatTracer *tracer, bool antecedents, + bool finalize_clauses) { + LOG_API_CALL_BEGIN ("connect proof tracer with stats"); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only start proof tracing to right after initialization"); + REQUIRE (tracer, "can not connect zero tracer"); + internal->connect_proof_tracer (tracer, antecedents, finalize_clauses); + LOG_API_CALL_END ("connect proof tracer with stats"); +} + +void Solver::connect_proof_tracer (FileTracer *tracer, bool antecedents, + bool finalize_clauses) { + LOG_API_CALL_BEGIN ("connect proof tracer with file"); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only start proof tracing right after initialization"); + REQUIRE (tracer, "can not connect zero tracer"); + internal->connect_proof_tracer (tracer, antecedents, finalize_clauses); + LOG_API_CALL_END ("connect proof tracer with file"); +} + +bool Solver::disconnect_proof_tracer (Tracer *tracer) { + LOG_API_CALL_BEGIN ("disconnect proof tracer"); + REQUIRE_VALID_STATE (); + REQUIRE (tracer, "can not disconnect zero tracer"); + bool res = internal->disconnect_proof_tracer (tracer); + LOG_API_CALL_RETURNS ("connect proof tracer", res); + return res; +} + +bool Solver::disconnect_proof_tracer (StatTracer *tracer) { + LOG_API_CALL_BEGIN ("disconnect proof tracer"); + REQUIRE_VALID_STATE (); + REQUIRE (tracer, "can not disconnect zero tracer"); + bool res = internal->disconnect_proof_tracer (tracer); + LOG_API_CALL_RETURNS ("disconnect proof tracer", res); + return res; +} + +bool Solver::disconnect_proof_tracer (FileTracer *tracer) { + LOG_API_CALL_BEGIN ("disconnect proof tracer"); + REQUIRE_VALID_STATE (); + REQUIRE (tracer, "can not disconnect zero tracer"); + bool res = internal->disconnect_proof_tracer (tracer); + LOG_API_CALL_RETURNS ("disconnect proof tracer", res); + return res; +} + +/*------------------------------------------------------------------------*/ + +void Solver::conclude () { + TRACE ("conclude"); + REQUIRE_VALID_STATE (); + REQUIRE (state () == UNSATISFIED || state () == SATISFIED || + state () == INCONCLUSIVE, + "can only conclude in satisfied, unsatisfied or inconclusive state"); + if (state () == UNSATISFIED) + internal->conclude_unsat (); + else if (state () == SATISFIED) + external->conclude_sat (); + else if (state () == INCONCLUSIVE) + external->conclude_unknown (); + assert (state () == UNSATISFIED || state () == SATISFIED || + state () == INCONCLUSIVE); + LOG_API_CALL_END ("conclude"); +} + +/*------------------------------------------------------------------------*/ + +void Solver::build (FILE *file, const char *prefix) { + + assert (file == stdout || file == stderr); + + Terminal *terminal; + + if (file == stdout) + terminal = &tout; + else if (file == stderr) + terminal = &terr; + else + terminal = 0; + + const char *v = CaDiCaL::version (); + const char *i = identifier (); + const char *c = compiler (); + const char *b = date (); + const char *f = flags (); + + assert (v); + + fputs (prefix, file); + if (terminal) + terminal->magenta (); + fputs ("Version ", file); + if (terminal) + terminal->normal (); + fputs (v, file); + if (i) { + if (terminal) + terminal->magenta (); + fputc (' ', file); + fputs (i, file); + if (terminal) + terminal->normal (); + } + fputc ('\n', file); + + if (c) { + fputs (prefix, file); + if (terminal) + terminal->magenta (); + fputs (c, file); + if (f) { + fputc (' ', file); + fputs (f, file); + } + if (terminal) + terminal->normal (); + fputc ('\n', file); + } + + if (b) { + fputs (prefix, file); + if (terminal) + terminal->magenta (); + fputs (b, file); + if (terminal) + terminal->normal (); + fputc ('\n', file); + } + + fflush (file); +} + +const char *Solver::version () { return CaDiCaL::version (); } + +const char *Solver::signature () { return CaDiCaL::signature (); } + +void Solver::options () { + REQUIRE_VALID_STATE (); + internal->opts.print (); +} + +void Solver::usage () { Options::usage (); } + +void Solver::configurations () { Config::usage (); } + +void Solver::statistics () { + if (state () == DELETING) + return; + TRACE ("stats"); + REQUIRE_VALID_OR_SOLVING_STATE (); + internal->print_statistics (); + LOG_API_CALL_END ("stats"); +} + +void Solver::resources () { + if (state () == DELETING) + return; + TRACE ("resources"); + REQUIRE_VALID_OR_SOLVING_STATE (); + internal->print_resource_usage (); + LOG_API_CALL_END ("resources"); +} + +/*------------------------------------------------------------------------*/ + +const char *Solver::read_dimacs (File *file, int &vars, int strict, + bool *incremental, vector<int> *cubes) { + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only read DIMACS file right after initialization"); + Parser *parser = new Parser (this, file, incremental, cubes); + const char *err = parser->parse_dimacs (vars, strict); + delete parser; + return err; +} + +const char *Solver::read_dimacs (FILE *external_file, const char *name, + int &vars, int strict) { + LOG_API_CALL_BEGIN ("read_dimacs", name); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only read DIMACS file right after initialization"); + File *file = File::read (internal, external_file, name); + assert (file); + const char *err = read_dimacs (file, vars, strict); + delete file; + LOG_API_CALL_RETURNS ("read_dimacs", name, err); + return err; +} + +const char *Solver::read_dimacs (const char *path, int &vars, int strict) { + LOG_API_CALL_BEGIN ("read_dimacs", path); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only read DIMACS file right after initialization"); + File *file = File::read (internal, path); + if (!file) + return internal->error_message.init ("failed to read DIMACS file '%s'", + path); + const char *err = read_dimacs (file, vars, strict); + delete file; + LOG_API_CALL_RETURNS ("read_dimacs", path, err); + return err; +} + +const char *Solver::read_dimacs (FILE *external_file, const char *name, + int &vars, int strict, bool &incremental, + vector<int> &cubes) { + LOG_API_CALL_BEGIN ("read_dimacs", name); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only read DIMACS file right after initialization"); + File *file = File::read (internal, external_file, name); + assert (file); + const char *err = read_dimacs (file, vars, strict, &incremental, &cubes); + delete file; + LOG_API_CALL_RETURNS ("read_dimacs", name, err); + return err; +} + +const char *Solver::read_dimacs (const char *path, int &vars, int strict, + bool &incremental, vector<int> &cubes) { + LOG_API_CALL_BEGIN ("read_dimacs", path); + REQUIRE_VALID_STATE (); + REQUIRE (state () == CONFIGURING, + "can only read DIMACS file right after initialization"); + File *file = File::read (internal, path); + if (!file) + return internal->error_message.init ("failed to read DIMACS file '%s'", + path); + const char *err = read_dimacs (file, vars, strict, &incremental, &cubes); + delete file; + LOG_API_CALL_RETURNS ("read_dimacs", path, err); + return err; +} + +const char *Solver::read_solution (const char *path) { + LOG_API_CALL_BEGIN ("solution", path); + REQUIRE_VALID_STATE (); + File *file = File::read (internal, path); + if (!file) + return internal->error_message.init ( + "failed to read solution file '%s'", path); + Parser *parser = new Parser (this, file, 0, 0); + const char *err = parser->parse_solution (); + delete parser; + delete file; + if (!err) + external->check_assignment (&External::sol); + LOG_API_CALL_RETURNS ("read_solution", path, err); + return err; +} + +/*------------------------------------------------------------------------*/ + +void Solver::dump_cnf () { + TRACE ("dump"); + REQUIRE_INITIALIZED (); + internal->dump (); + LOG_API_CALL_END ("dump"); +} + +/*------------------------------------------------------------------------*/ + +ExternalPropagator *Solver::get_propagator () { + return external->propagator; +} + +bool Solver::observed (int lit) { + TRACE ("observed", lit); + REQUIRE_VALID_OR_SOLVING_STATE (); + REQUIRE_VALID_LIT (lit); + bool res = external->observed (lit); + LOG_API_CALL_RETURNS ("observed", lit, res); + return res; +} + +bool Solver::is_witness (int lit) { + TRACE ("is_witness", lit); + REQUIRE_VALID_OR_SOLVING_STATE (); + REQUIRE_VALID_LIT (lit); + bool res = external->is_witness (lit); + LOG_API_CALL_RETURNS ("is_witness", lit, res); + return res; +} + +bool Solver::is_decision (int lit) { + TRACE ("is_decision", lit); + REQUIRE_VALID_OR_SOLVING_STATE (); + REQUIRE_VALID_LIT (lit); + bool res = external->is_decision (lit); + LOG_API_CALL_RETURNS ("is_decision", lit, res); + return res; +} + +void Solver::force_backtrack (size_t new_level) { + TRACE ("force_backtrack", new_level); + REQUIRE_VALID_OR_SOLVING_STATE (); + external->force_backtrack (new_level); +} + +/*------------------------------------------------------------------------*/ + +bool Solver::traverse_clauses (ClauseIterator &it) const { + LOG_API_CALL_BEGIN ("traverse_clauses"); + REQUIRE_VALID_STATE (); + bool res = external->traverse_all_frozen_units_as_clauses (it) && + internal->traverse_clauses (it) && + internal->traverse_constraint (it); + LOG_API_CALL_RETURNS ("traverse_clauses", res); + return res; +} + +bool Solver::traverse_witnesses_backward (WitnessIterator &it) const { + LOG_API_CALL_BEGIN ("traverse_witnesses_backward"); + REQUIRE_VALID_STATE (); + bool res = external->traverse_all_non_frozen_units_as_witnesses (it) && + external->traverse_witnesses_backward (it); + LOG_API_CALL_RETURNS ("traverse_witnesses_backward", res); + return res; +} + +bool Solver::traverse_witnesses_forward (WitnessIterator &it) const { + LOG_API_CALL_BEGIN ("traverse_witnesses_forward"); + REQUIRE_VALID_STATE (); + bool res = external->traverse_witnesses_forward (it) && + external->traverse_all_non_frozen_units_as_witnesses (it); + LOG_API_CALL_RETURNS ("traverse_witnesses_forward", res); + return res; +} + +/*------------------------------------------------------------------------*/ + +class ClauseCounter : public ClauseIterator { +public: + int vars; + int64_t clauses; + ClauseCounter () : vars (0), clauses (0) {} + bool clause (const vector<int> &c) { + for (const auto &lit : c) { + assert (lit != INT_MIN); + int idx = abs (lit); + if (idx > vars) + vars = idx; + } + clauses++; + return true; + } +}; + +class ClauseWriter : public ClauseIterator { + File *file; + +public: + ClauseWriter (File *f) : file (f) {} + bool clause (const vector<int> &c) { + for (const auto &lit : c) { + if (!file->put (lit)) + return false; + if (!file->put (' ')) + return false; + } + return file->put ("0\n"); + } +}; + +const char *Solver::write_dimacs (const char *path, int min_max_var) { + LOG_API_CALL_BEGIN ("write_dimacs", path, min_max_var); + REQUIRE_VALID_STATE (); +#ifndef QUIET + const double start = internal->time (); +#endif + internal->restore_clauses (); + ClauseCounter counter; + (void) traverse_clauses (counter); + LOG ("found maximal variable %d and %" PRId64 " clauses", counter.vars, + counter.clauses); + File *file = File::write (internal, path); + const char *res = 0; + if (file) { + int actual_max_vars = max (min_max_var, counter.vars); + MSG ("writing %s'p cnf %d %" PRId64 "'%s header", tout.green_code (), + actual_max_vars, counter.clauses, tout.normal_code ()); + file->put ("p cnf "); + file->put (actual_max_vars); + file->put (' '); + file->put (counter.clauses); + file->put ('\n'); + ClauseWriter writer (file); + if (!traverse_clauses (writer)) + res = internal->error_message.init ( + "writing to DIMACS file '%s' failed", path); + delete file; + } else + res = internal->error_message.init ( + "failed to open DIMACS file '%s' for writing", path); +#ifndef QUIET + if (!res) { + const double end = internal->time (); + MSG ("wrote %" PRId64 " clauses in %.2f seconds %s time", + counter.clauses, end - start, + internal->opts.realtime ? "real" : "process"); + } +#endif + LOG_API_CALL_RETURNS ("write_dimacs", path, min_max_var, res); + return res; +} + +/*------------------------------------------------------------------------*/ + +struct WitnessWriter : public WitnessIterator { + File *file; + int64_t witnesses; + WitnessWriter (File *f) : file (f), witnesses (0) {} + bool write (const vector<int> &a) { + for (const auto &lit : a) { + if (!file->put (lit)) + return false; + if (!file->put (' ')) + return false; + } + return file->put ('0'); + } + bool witness (const vector<int> &c, const vector<int> &w, int64_t) { + if (!write (c)) + return false; + if (!file->put (' ')) + return false; + if (!write (w)) + return false; + if (!file->put ('\n')) + return false; + witnesses++; + return true; + } +}; + +const char *Solver::write_extension (const char *path) { + LOG_API_CALL_BEGIN ("write_extension", path); + REQUIRE_VALID_STATE (); + const char *res = 0; +#ifndef QUIET + const double start = internal->time (); +#endif + File *file = File::write (internal, path); + WitnessWriter writer (file); + if (file) { + if (!traverse_witnesses_backward (writer)) + res = internal->error_message.init ( + "writing to DIMACS file '%s' failed", path); + delete file; + } else + res = internal->error_message.init ( + "failed to open extension file '%s' for writing", path); +#ifndef QUIET + if (!res) { + const double end = internal->time (); + MSG ("wrote %" PRId64 " witnesses in %.2f seconds %s time", + writer.witnesses, end - start, + internal->opts.realtime ? "real" : "process"); + } +#endif + LOG_API_CALL_RETURNS ("write_extension", path, res); + return res; +} + +/*------------------------------------------------------------------------*/ + +struct ClauseCopier : public ClauseIterator { + Solver &dst; + +public: + ClauseCopier (Solver &d) : dst (d) {} + bool clause (const vector<int> &c) { + for (const auto &lit : c) + dst.add (lit); + dst.add (0); + return true; + } +}; + +struct WitnessCopier : public WitnessIterator { + External *dst; + +public: + WitnessCopier (External *d) : dst (d) {} + bool witness (const vector<int> &c, const vector<int> &w, int64_t id) { + dst->push_external_clause_and_witness_on_extension_stack (c, w, id); + return true; + } +}; + +void Solver::copy (Solver &other) const { + REQUIRE_READY_STATE (); + REQUIRE (other.state () & CONFIGURING, "target solver already modified"); + internal->opts.copy (other.internal->opts); + ClauseCopier clause_copier (other); + traverse_clauses (clause_copier); + WitnessCopier witness_copier (other.external); + traverse_witnesses_forward (witness_copier); + external->copy_flags (*other.external); +} + +/*------------------------------------------------------------------------*/ + +void Solver::section (const char *title) { + if (state () == DELETING) + return; +#ifdef QUIET + (void) title; +#endif + REQUIRE_INITIALIZED (); + SECTION (title); +} + +void Solver::message (const char *fmt, ...) { + if (state () == DELETING) + return; +#ifdef QUIET + (void) fmt; +#else + REQUIRE_INITIALIZED (); + va_list ap; + va_start (ap, fmt); + internal->vmessage (fmt, ap); + va_end (ap); +#endif +} + +void Solver::message () { + if (state () == DELETING) + return; + REQUIRE_INITIALIZED (); +#ifndef QUIET + internal->message (); +#endif +} + +void Solver::verbose (int level, const char *fmt, ...) { + if (state () == DELETING) + return; + REQUIRE_VALID_OR_SOLVING_STATE (); +#ifdef QUIET + (void) level; + (void) fmt; +#else + va_list ap; + va_start (ap, fmt); + internal->vverbose (level, fmt, ap); + va_end (ap); +#endif +} + +void Solver::error (const char *fmt, ...) { + if (state () == DELETING) + return; + REQUIRE_INITIALIZED (); + va_list ap; + va_start (ap, fmt); + internal->verror (fmt, ap); + va_end (ap); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/stable.cpp b/src/sat/cadical/stable.cpp new file mode 100644 index 000000000..44c9d1021 --- /dev/null +++ b/src/sat/cadical/stable.cpp @@ -0,0 +1,31 @@ +#ifdef PROFILE_MODE + +#include "internal.hpp" + +namespace CaDiCaL { + +bool Internal::propagate_stable () { + assert (stable); + START (propstable); + bool res = propagate (); + STOP (propstable); + return res; +} + +void Internal::analyze_stable () { + assert (stable); + START (analyzestable); + analyze (); + STOP (analyzestable); +} + +int Internal::decide_stable () { + assert (stable); + return decide (); +} + +}; // namespace CaDiCaL + +#else +int stable_if_not_profile_mode_dummy; +#endif diff --git a/src/sat/cadical/stack.h b/src/sat/cadical/stack.h new file mode 100644 index 000000000..21082e525 --- /dev/null +++ b/src/sat/cadical/stack.h @@ -0,0 +1,110 @@ +#ifndef _stack_h_INCLUDED +#define _stack_h_INCLUDED + +#include <stdlib.h> + +#define STACK(TYPE) \ + struct { \ + TYPE *begin; \ + TYPE *end; \ + TYPE *allocated; \ + } + +#define FULL_STACK(S) ((S).end == (S).allocated) +#define EMPTY_STACK(S) ((S).begin == (S).end) +#define SIZE_STACK(S) ((size_t) ((S).end - (S).begin)) +#define CAPACITY_STACK(S) ((size_t) ((S).allocated - (S).begin)) + +#define INIT_STACK(S) \ + do { \ + (S).begin = (S).end = (S).allocated = 0; \ + } while (0) + +#define TOP_STACK(S) (END_STACK (S)[assert (!EMPTY_STACK (S)), -1]) + +#define PEEK_STACK(S, N) \ + (BEGIN_STACK (S)[assert ((N) < SIZE_STACK (S)), (N)]) + +#define POKE_STACK(S, N, E) \ + do { \ + PEEK_STACK (S, N) = (E); \ + } while (0) + +#define POP_STACK(S) (assert (!EMPTY_STACK (S)), *--(S).end) + +#define PUSH_STACK(S, E) \ + do { \ + if (FULL_STACK (S)) \ + ENLARGE_STACK (S); \ + *(S).end++ = (E); \ + } while (0) + +#define BEGIN_STACK(S) (S).begin + +#define END_STACK(S) (S).end + +#define CLEAR_STACK(S) \ + do { \ + (S).end = (S).begin; \ + } while (0) + +#define RESIZE_STACK(S, NEW_SIZE) \ + do { \ + const size_t TMP_NEW_SIZE = (NEW_SIZE); \ + assert (TMP_NEW_SIZE <= SIZE_STACK (S)); \ + (S).end = (S).begin + TMP_NEW_SIZE; \ + } while (0) + +#define SET_END_OF_STACK(S, P) \ + do { \ + assert (BEGIN_STACK (S) <= (P)); \ + assert ((P) <= END_STACK (S)); \ + if ((P) == END_STACK (S)) \ + break; \ + (S).end = (P); \ + } while (0) + +#define RELEASE_STACK(S) \ + do { \ + DEALLOC ((S).begin, CAPACITY_STACK (S)); \ + INIT_STACK (S); \ + } while (0) + +#define REMOVE_STACK(T, S, E) \ + do { \ + assert (!EMPTY_STACK (S)); \ + T *END_REMOVE_STACK = END_STACK (S); \ + T *P_REMOVE_STACK = BEGIN_STACK (S); \ + while (*P_REMOVE_STACK != (E)) { \ + P_REMOVE_STACK++; \ + assert (P_REMOVE_STACK != END_REMOVE_STACK); \ + } \ + P_REMOVE_STACK++; \ + while (P_REMOVE_STACK != END_REMOVE_STACK) { \ + P_REMOVE_STACK[-1] = *P_REMOVE_STACK; \ + P_REMOVE_STACK++; \ + } \ + (S).end--; \ + } while (0) + +#define all_stack(T, E, S) \ + T E, *E##_PTR = BEGIN_STACK (S), *const E##_END = END_STACK (S); \ + E##_PTR != E##_END && (E = *E##_PTR, true); \ + ++E##_PTR + +#define all_pointers(T, E, S) \ + T *E, *const *E##_PTR = BEGIN_STACK (S), \ + *const *const E##_END = END_STACK (S); \ + E##_PTR != E##_END && (E = *E##_PTR, true); \ + ++E##_PTR + +// clang-format off + +typedef STACK (char) chars; +typedef STACK (int) ints; +typedef STACK (size_t) sizes; +typedef STACK (unsigned) unsigneds; + +// clang-format on + +#endif diff --git a/src/sat/cadical/stats.cpp b/src/sat/cadical/stats.cpp new file mode 100644 index 000000000..e390e1d40 --- /dev/null +++ b/src/sat/cadical/stats.cpp @@ -0,0 +1,820 @@ +// vim: set tw=300: set VIM text width to 300 characters for this file. + +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +Stats::Stats () { + time.real = absolute_real_time (); + time.process = absolute_process_time (); + walk.minimum = LONG_MAX; + used.resize (2); + used[0].resize (127); + used[1].resize (127); +} + +/*------------------------------------------------------------------------*/ + +#define PRT(FMT, ...) \ + do { \ + if (FMT[0] == ' ' && !all) \ + break; \ + MSG (FMT, __VA_ARGS__); \ + } while (0) + +/*------------------------------------------------------------------------*/ + +void Stats::print (Internal *internal) { + +#ifdef QUIET + (void) internal; +#else + + Stats &stats = internal->stats; + + int all = internal->opts.verbose > 0 || internal->opts.stats; +#ifdef LOGGING + if (internal->opts.log) + all = true; +#endif // ifdef LOGGING + + if (internal->opts.profile) + internal->print_profile (); + + double t = internal->solve_time (); + + int64_t propagations = 0; + propagations += stats.propagations.cover; + propagations += stats.propagations.probe; + propagations += stats.propagations.search; + propagations += stats.propagations.transred; + propagations += stats.propagations.vivify; + propagations += stats.propagations.walk; + + int64_t vivified = stats.vivifysubs + stats.vivifystrs; + int64_t searchticks = stats.ticks.search[0] + stats.ticks.search[1]; + int64_t inprobeticks = stats.ticks.vivify + stats.ticks.probe + + stats.ticks.factor + stats.ticks.ternary + + stats.ticks.sweep; + int64_t totalticks = searchticks + inprobeticks; + + size_t extendbytes = internal->external->extension.size (); + extendbytes *= sizeof (int); + + SECTION ("statistics"); + + if (all || stats.blocked) { + PRT ("blocked: %15" PRId64 + " %10.2f %% of irredundant clauses", + stats.blocked, percent (stats.blocked, stats.added.irredundant)); + PRT (" blockings: %15" PRId64 " %10.2f internal", + stats.blockings, relative (stats.conflicts, stats.blockings)); + PRT (" candidates: %15" PRId64 " %10.2f per blocking ", + stats.blockcands, relative (stats.blockcands, stats.blockings)); + PRT (" blockres: %15" PRId64 " %10.2f per candidate", + stats.blockres, relative (stats.blockres, stats.blockcands)); + PRT (" pure: %15" PRId64 " %10.2f %% of all variables", + stats.all.pure, percent (stats.all.pure, stats.vars)); + PRT (" pureclauses: %15" PRId64 " %10.2f per pure literal", + stats.blockpured, relative (stats.blockpured, stats.all.pure)); + } + if (all || stats.chrono) + PRT ("chronological: %15" PRId64 " %10.2f %% of conflicts", + stats.chrono, percent (stats.chrono, stats.conflicts)); + if (all) + PRT ("compacts: %15" PRId64 " %10.2f interval", + stats.compacts, relative (stats.conflicts, stats.compacts)); + if (all || stats.conflicts) { + PRT ("conflicts: %15" PRId64 " %10.2f per second", + stats.conflicts, relative (stats.conflicts, t)); + PRT (" backtracked: %15" PRId64 " %10.2f %% of conflicts", + stats.backtracks, percent (stats.backtracks, stats.conflicts)); + } + if (all || stats.conditioned) { + PRT ("conditioned: %15" PRId64 + " %10.2f %% of irredundant clauses", + stats.conditioned, + percent (stats.conditioned, stats.added.irredundant)); + PRT (" conditionings: %15" PRId64 " %10.2f interval", + stats.conditionings, + relative (stats.conflicts, stats.conditionings)); + PRT (" condcands: %15" PRId64 " %10.2f candidate clauses", + stats.condcands, relative (stats.condcands, stats.conditionings)); + PRT (" condassinit: %17.1f %9.2f %% initial assigned", + relative (stats.condassinit, stats.conditionings), + percent (stats.condassinit, stats.condassvars)); + PRT (" condcondinit: %17.1f %9.2f %% initial condition", + relative (stats.condcondinit, stats.conditionings), + percent (stats.condcondinit, stats.condassinit)); + PRT (" condautinit: %17.1f %9.2f %% initial autarky", + relative (stats.condautinit, stats.conditionings), + percent (stats.condautinit, stats.condassinit)); + PRT (" condassrem: %17.1f %9.2f %% final assigned", + relative (stats.condassrem, stats.conditioned), + percent (stats.condassrem, stats.condassirem)); + PRT (" condcondrem: %19.3f %7.2f %% final conditional", + relative (stats.condcondrem, stats.conditioned), + percent (stats.condcondrem, stats.condassrem)); + PRT (" condautrem: %19.3f %7.2f %% final autarky", + relative (stats.condautrem, stats.conditioned), + percent (stats.condautrem, stats.condassrem)); + PRT (" condprops: %15" PRId64 " %10.2f per candidate", + stats.condprops, relative (stats.condprops, stats.condcands)); + } + if (all || stats.cover.total) { + PRT ("covered: %15" PRId64 + " %10.2f %% of irredundant clauses", + stats.cover.total, + percent (stats.cover.total, stats.added.irredundant)); + PRT (" coverings: %15" PRId64 " %10.2f interval", + stats.cover.count, relative (stats.conflicts, stats.cover.count)); + PRT (" asymmetric: %15" PRId64 " %10.2f %% of covered clauses", + stats.cover.asymmetric, + percent (stats.cover.asymmetric, stats.cover.total)); + PRT (" blocked: %15" PRId64 " %10.2f %% of covered clauses", + stats.cover.blocked, + percent (stats.cover.blocked, stats.cover.total)); + } + if (all || stats.decisions) { + PRT ("decisions: %15" PRId64 " %10.2f per second", + stats.decisions, relative (stats.decisions, t)); + PRT (" searched: %15" PRId64 " %10.2f per decision", + stats.searched, relative (stats.searched, stats.decisions)); + } + if (all || stats.all.eliminated) { + PRT ("eliminated: %15" PRId64 " %10.2f %% of all variables", + stats.all.eliminated, percent (stats.all.eliminated, stats.vars)); + PRT (" fastelim: %15" PRId64 " %10.2f %% of eliminated", + stats.all.fasteliminated, + percent (stats.all.fasteliminated, stats.all.eliminated)); + PRT (" elimphases: %15" PRId64 " %10.2f interval", + stats.elimphases, relative (stats.conflicts, stats.elimphases)); + PRT (" elimrounds: %15" PRId64 " %10.2f per phase", + stats.elimrounds, relative (stats.elimrounds, stats.elimphases)); + PRT (" elimtried: %15" PRId64 " %10.2f %% eliminated", + stats.elimtried, percent (stats.all.eliminated, stats.elimtried)); + PRT (" elimgates: %15" PRId64 " %10.2f %% gates per tried", + stats.elimgates, percent (stats.elimgates, stats.elimtried)); + PRT (" elimequivs: %15" PRId64 " %10.2f %% equivalence gates", + stats.elimequivs, percent (stats.elimequivs, stats.elimgates)); + PRT (" elimands: %15" PRId64 " %10.2f %% and gates", + stats.elimands, percent (stats.elimands, stats.elimgates)); + PRT (" elimites: %15" PRId64 " %10.2f %% if-then-else gates", + stats.elimites, percent (stats.elimites, stats.elimgates)); + PRT (" elimxors: %15" PRId64 " %10.2f %% xor gates", + stats.elimxors, percent (stats.elimxors, stats.elimgates)); + PRT (" elimdefs: %15" PRId64 " %10.2f %% definitions", + stats.definitions_extracted, + percent (stats.definitions_extracted, stats.elimgates)); + PRT (" elimsubst: %15" PRId64 " %10.2f %% substituted", + stats.elimsubst, percent (stats.elimsubst, stats.all.eliminated)); + PRT (" elimsubstequi: %15" PRId64 " %10.2f %% equivalence gates", + stats.eliminated_equi, + percent (stats.eliminated_equi, stats.elimsubst)); + PRT (" elimsubstands: %15" PRId64 " %10.2f %% and gates", + stats.eliminated_and, + percent (stats.eliminated_and, stats.elimsubst)); + PRT (" elimsubstites: %15" PRId64 " %10.2f %% if-then-else gates", + stats.eliminated_ite, + percent (stats.eliminated_ite, stats.elimsubst)); + PRT (" elimsubstxors: %15" PRId64 " %10.2f %% xor gates", + stats.eliminated_xor, + percent (stats.eliminated_xor, stats.elimsubst)); + PRT (" elimsubstdefs: %15" PRId64 " %10.2f %% definitions", + stats.eliminated_def, + percent (stats.eliminated_def, stats.elimsubst)); + PRT (" elimres: %15" PRId64 " %10.2f per eliminated", + stats.elimres, relative (stats.elimres, stats.all.eliminated)); + PRT (" elimrestried: %15" PRId64 " %10.2f %% per resolution", + stats.elimrestried, percent (stats.elimrestried, stats.elimres)); + PRT (" def checked: %15" PRId64 " %10.2f per phase", + stats.definitions_checked, + relative (stats.definitions_checked, stats.elimphases)); + PRT (" def extracted: %15" PRId64 " %10.2f %% per checked", + stats.definitions_extracted, + percent (stats.definitions_extracted, stats.definitions_checked)); + PRT (" def units: %15" PRId64 " %10.2f %% per checked", + stats.definition_units, + percent (stats.definition_units, stats.definitions_checked)); + } + if (all || stats.ext_prop.ext_cb) { + PRT ("ext.prop. calls: %15" PRId64 " %10.2f %% of queries", + stats.ext_prop.eprop_call, + percent (stats.ext_prop.eprop_call, stats.ext_prop.ext_cb)); + PRT (" propagating: %15" PRId64 " %10.2f %% per eprop-call", + stats.ext_prop.eprop_prop, + percent (stats.ext_prop.eprop_prop, stats.ext_prop.eprop_call)); + PRT (" explained: %15" PRId64 " %10.2f %% per eprop-call", + stats.ext_prop.eprop_expl, + percent (stats.ext_prop.eprop_expl, stats.ext_prop.eprop_call)); + PRT (" falsified: %15" PRId64 " %10.2f %% per eprop-call", + stats.ext_prop.eprop_conf, + percent (stats.ext_prop.eprop_conf, stats.ext_prop.eprop_call)); + PRT ("ext.clause calls:%15" PRId64 " %10.2f %% of queries", + stats.ext_prop.elearn_call, + percent (stats.ext_prop.elearn_call, stats.ext_prop.ext_cb)); + PRT (" learned: %15" PRId64 " %10.2f %% per called", + stats.ext_prop.elearned, + percent (stats.ext_prop.elearned, stats.ext_prop.elearn_call)); + PRT (" conflicting: %15" PRId64 " %10.2f %% per learned", + stats.ext_prop.elearn_conf, + percent (stats.ext_prop.elearn_conf, stats.ext_prop.elearned)); + PRT (" propagating: %15" PRId64 " %10.2f %% per learned", + stats.ext_prop.elearn_prop, + percent (stats.ext_prop.elearn_prop, stats.ext_prop.elearned)); + PRT ("ext.final check: %15" PRId64 " %10.2f %% of queries", + stats.ext_prop.echeck_call, + percent (stats.ext_prop.echeck_call, stats.ext_prop.ext_cb)); + } + if (all || stats.factored) { + PRT ("factored: %15" PRId64 " %10.2f %% of variables", + stats.factored, percent (stats.factored, internal->max_var)); + PRT (" factor: %15" PRId64 " %10.2f conflict interval", + stats.factor, relative (stats.conflicts, stats.factor)); + PRT (" cls factored: %15" PRId64 " %10.2f per factored", + stats.factor_added, relative (stats.factor_added, factored)); + PRT (" lits factored: %15" PRId64 " %10.2f per factored", + stats.literals_factored, + relative (stats.literals_factored, factored)); + PRT (" cls unfactored:%15" PRId64 " %10.2f per factored", + stats.clauses_unfactored, + relative (stats.clauses_unfactored, factored)); + PRT (" lits unfactored:%14" PRId64 " %10.2f per factored", + stats.literals_unfactored, + relative (stats.literals_unfactored, factored)); + } + if (all || stats.all.fixed) { + PRT ("fixed: %15" PRId64 " %10.2f %% of all variables", + stats.all.fixed, percent (stats.all.fixed, stats.vars)); + PRT (" failed: %15" PRId64 " %10.2f %% of all variables", + stats.failed, percent (stats.failed, stats.vars)); + PRT (" probefailed: %15" PRId64 " %10.2f %% per failed", + stats.probefailed, percent (stats.probefailed, stats.failed)); + PRT (" transredunits: %15" PRId64 " %10.2f %% per failed", + stats.transredunits, percent (stats.transredunits, stats.failed)); + PRT (" inprobephases: %15" PRId64 " %10.2f interval", + stats.inprobingphases, + relative (stats.conflicts, stats.inprobingphases)); + PRT (" inprobesuccess:%15" PRId64 " %10.2f %% phases", + stats.inprobesuccess, + percent (stats.inprobesuccess, stats.inprobingphases)); + PRT (" probingrounds: %15" PRId64 " %10.2f per phase", + stats.probingrounds, + relative (stats.probingrounds, stats.inprobingphases)); + PRT (" probed: %15" PRId64 " %10.2f per failed", + stats.probed, relative (stats.probed, stats.failed)); + PRT (" hbrs: %15" PRId64 " %10.2f per probed", + stats.hbrs, relative (stats.hbrs, stats.probed)); + PRT (" hbrsizes: %15" PRId64 " %10.2f per hbr", + stats.hbrsizes, relative (stats.hbrsizes, stats.hbrs)); + PRT (" hbreds: %15" PRId64 " %10.2f %% per hbr", + stats.hbreds, percent (stats.hbreds, stats.hbrs)); + PRT (" hbrsubs: %15" PRId64 " %10.2f %% per hbr", + stats.hbrsubs, percent (stats.hbrsubs, stats.hbrs)); + } + PRT (" units: %15" PRId64 " %10.2f interval", stats.units, + relative (stats.conflicts, stats.units)); + PRT (" binaries: %15" PRId64 " %10.2f interval", + stats.binaries, relative (stats.conflicts, stats.binaries)); + if (all || stats.flush.learned) { + PRT ("flushed: %15" PRId64 " %10.2f %% per conflict", + stats.flush.learned, + percent (stats.flush.learned, stats.conflicts)); + PRT (" hyper: %15" PRId64 " %10.2f %% per conflict", + stats.flush.hyper, relative (stats.flush.hyper, stats.conflicts)); + PRT (" flushings: %15" PRId64 " %10.2f interval", + stats.flush.count, relative (stats.conflicts, stats.flush.count)); + } + if (all || stats.instantiated) { + PRT ("instantiated: %15" PRId64 " %10.2f %% of tried", + stats.instantiated, percent (stats.instantiated, stats.instried)); + PRT (" instrounds: %15" PRId64 " %10.2f %% of elimrounds", + stats.instrounds, percent (stats.instrounds, stats.elimrounds)); + } + if (all || stats.conflicts) { + PRT ("learned: %15" PRId64 " %10.2f %% per conflict", + stats.learned.clauses, + percent (stats.learned.clauses, stats.conflicts)); + PRT ("@ bumped: %15" PRId64 " %10.2f per learned", + stats.bumped, relative (stats.bumped, stats.learned.clauses)); + PRT (" recomputed: %15" PRId64 " %10.2f %% per learned", + stats.recomputed, + percent (stats.recomputed, stats.learned.clauses)); + PRT (" promoted1: %15" PRId64 " %10.2f %% per learned", + stats.promoted1, percent (stats.promoted1, stats.learned.clauses)); + PRT (" promoted2: %15" PRId64 " %10.2f %% per learned", + stats.promoted2, percent (stats.promoted2, stats.learned.clauses)); + PRT (" improvedglue: %15" PRId64 " %10.2f %% per learned", + stats.improvedglue, + percent (stats.improvedglue, stats.learned.clauses)); + } + if (all || stats.lucky.succeeded) { + PRT ("lucky: %15" PRId64 " %10.2f %% of tried", + stats.lucky.succeeded, + percent (stats.lucky.succeeded, stats.lucky.tried)); + PRT (" constantzero %15" PRId64 " %10.2f %% of tried", + stats.lucky.constant.zero, + percent (stats.lucky.constant.zero, stats.lucky.tried)); + PRT (" constantone %15" PRId64 " %10.2f %% of tried", + stats.lucky.constant.one, + percent (stats.lucky.constant.one, stats.lucky.tried)); + PRT (" backwardone %15" PRId64 " %10.2f %% of tried", + stats.lucky.backward.one, + percent (stats.lucky.backward.one, stats.lucky.tried)); + PRT (" backwardzero %15" PRId64 " %10.2f %% of tried", + stats.lucky.backward.zero, + percent (stats.lucky.backward.zero, stats.lucky.tried)); + PRT (" forwardone %15" PRId64 " %10.2f %% of tried", + stats.lucky.forward.one, + percent (stats.lucky.forward.one, stats.lucky.tried)); + PRT (" forwardzero %15" PRId64 " %10.2f %% of tried", + stats.lucky.forward.zero, + percent (stats.lucky.forward.zero, stats.lucky.tried)); + PRT (" positivehorn %15" PRId64 " %10.2f %% of tried", + stats.lucky.horn.positive, + percent (stats.lucky.horn.positive, stats.lucky.tried)); + PRT (" negativehorn %15" PRId64 " %10.2f %% of tried", + stats.lucky.horn.negative, + percent (stats.lucky.horn.negative, stats.lucky.tried)); + } + PRT (" extendbytes: %15zd %10.2f bytes and MB", extendbytes, + extendbytes / (double) (1l << 20)); + if (all || stats.learned.clauses) + PRT ("learned_lits: %15" PRId64 " %10.2f %% learned literals", + stats.learned.literals, + percent (stats.learned.literals, stats.learned.literals)); + PRT ("minimized: %15" PRId64 " %10.2f %% learned literals", + stats.minimized, percent (stats.minimized, stats.learned.literals)); + PRT ("shrunken: %15" PRId64 " %10.2f %% learned literals", + stats.shrunken, percent (stats.shrunken, stats.learned.literals)); + PRT ("minishrunken: %15" PRId64 " %10.2f %% learned literals", + stats.minishrunken, + percent (stats.minishrunken, stats.learned.literals)); + + if (all || stats.conflicts) { + PRT ("otfs: %15" PRId64 " %10.2f %% of conflict", + stats.otfs.subsumed + stats.otfs.strengthened, + percent (stats.otfs.subsumed + stats.otfs.strengthened, + stats.conflicts)); + PRT (" subsumed %15" PRId64 " %10.2f %% of conflict", + stats.otfs.subsumed, + percent (stats.otfs.subsumed, stats.conflicts)); + PRT (" strengthened %15" PRId64 " %10.2f %% of conflict", + stats.otfs.strengthened, + percent (stats.otfs.strengthened, stats.conflicts)); + } + + PRT ("propagations: %15" PRId64 " %10.2f M per second", + propagations, relative (propagations / 1e6, t)); + PRT (" coverprops: %15" PRId64 " %10.2f %% of propagations", + stats.propagations.cover, + percent (stats.propagations.cover, propagations)); + PRT (" probeprops: %15" PRId64 " %10.2f %% of propagations", + stats.propagations.probe, + percent (stats.propagations.probe, propagations)); + PRT (" searchprops: %15" PRId64 " %10.2f %% of propagations", + stats.propagations.search, + percent (stats.propagations.search, propagations)); + PRT (" transredprops: %15" PRId64 " %10.2f %% of propagations", + stats.propagations.transred, + percent (stats.propagations.transred, propagations)); + PRT (" vivifyprops: %15" PRId64 " %10.2f %% of propagations", + stats.propagations.vivify, + percent (stats.propagations.vivify, propagations)); + PRT (" walkprops: %15" PRId64 " %10.2f %% of propagations", + stats.propagations.walk, + percent (stats.propagations.walk, propagations)); + if (all || stats.reactivated) { + PRT ("reactivated: %15" PRId64 " %10.2f %% of all variables", + stats.reactivated, percent (stats.reactivated, stats.vars)); + } + if (all || stats.reduced) { + PRT ("reduced: %15" PRId64 " %10.2f %% per conflict", + stats.reduced, percent (stats.reduced, stats.conflicts)); + PRT (" reductions: %15" PRId64 " %10.2f interval", + stats.reductions, relative (stats.conflicts, stats.reductions)); + PRT (" sqrt scheme: %15" PRId64 " %10.2f %% reductions", + stats.reduced_sqrt, + relative (stats.reduced_sqrt, stats.reductions)); + PRT (" prct scheme: %15" PRId64 " %10.2f %% reductions", + stats.reduced_prct, + relative (stats.reduced_prct, stats.reductions)); + PRT (" collections: %15" PRId64 " %10.2f interval", + stats.collections, relative (stats.conflicts, stats.collections)); + } + if (all || stats.rephased.total) { + PRT ("rephased: %15" PRId64 " %10.2f interval", + stats.rephased.total, + relative (stats.conflicts, stats.rephased.total)); + PRT (" rephasedbest: %15" PRId64 " %10.2f %% rephased best", + stats.rephased.best, + percent (stats.rephased.best, stats.rephased.total)); + PRT (" rephasedflip: %15" PRId64 " %10.2f %% rephased flipping", + stats.rephased.flipped, + percent (stats.rephased.flipped, stats.rephased.total)); + PRT (" rephasedinv: %15" PRId64 " %10.2f %% rephased inverted", + stats.rephased.inverted, + percent (stats.rephased.inverted, stats.rephased.total)); + PRT (" rephasedorig: %15" PRId64 " %10.2f %% rephased original", + stats.rephased.original, + percent (stats.rephased.original, stats.rephased.total)); + PRT (" rephasedrand: %15" PRId64 " %10.2f %% rephased random", + stats.rephased.random, + percent (stats.rephased.random, stats.rephased.total)); + PRT (" rephasedwalk: %15" PRId64 " %10.2f %% rephased walk", + stats.rephased.walk, + percent (stats.rephased.walk, stats.rephased.total)); + } + if (all) + PRT ("rescored: %15" PRId64 " %10.2f interval", + stats.rescored, relative (stats.conflicts, stats.rescored)); + if (all || stats.restarts) { + PRT ("restarts: %15" PRId64 " %10.2f interval", + stats.restarts, relative (stats.conflicts, stats.restarts)); + PRT (" reused: %15" PRId64 " %10.2f %% per restart", + stats.reused, percent (stats.reused, stats.restarts)); + PRT (" reusedlevels: %15" PRId64 " %10.2f %% per restart levels", + stats.reusedlevels, + percent (stats.reusedlevels, stats.restartlevels)); + } + if (all || stats.restored) { + PRT ("restored: %15" PRId64 " %10.2f %% per weakened", + stats.restored, percent (stats.restored, stats.weakened)); + PRT (" restorations: %15" PRId64 " %10.2f %% per extension", + stats.restorations, + percent (stats.restorations, stats.extensions)); + PRT (" literals: %15" PRId64 " %10.2f per restored clause", + stats.restoredlits, relative (stats.restoredlits, stats.restored)); + } + if (all || stats.stabphases) { + PRT ("stabilizing: %15" PRId64 " %10.2f %% of conflicts", + stats.stabphases, percent (stats.stabconflicts, stats.conflicts)); + PRT (" restartstab: %15" PRId64 " %10.2f %% of all restarts", + stats.restartstable, + percent (stats.restartstable, stats.restarts)); + PRT (" reusedstab: %15" PRId64 " %10.2f %% per stable restarts", + stats.reusedstable, + percent (stats.reusedstable, stats.restartstable)); + } + if (all || stats.all.substituted) { + PRT ("substituted: %15" PRId64 " %10.2f %% of all variables", + stats.all.substituted, + percent (stats.all.substituted, stats.vars)); + PRT (" decompositions:%15" PRId64 " %10.2f per phase", + stats.decompositions, + relative (stats.decompositions, stats.inprobingphases)); + } + if (all || stats.sweep_equivalences) { + PRT ("sweep equivs: %15" PRId64 " %10.2f %% of swept variables", + stats.sweep_equivalences, + percent (stats.sweep_equivalences, stats.sweep_variables)); + PRT (" sweepings: %15" PRId64 " %10.2f vars per sweeping", + stats.sweep, relative (stats.sweep_variables, stats.sweep)); + PRT (" swept vars: %15" PRId64 " %10.2f %% of all variables", + stats.sweep_variables, + percent (stats.sweep_variables, stats.vars)); + PRT (" sweep units: %15" PRId64 " %10.2f %% of all variables", + stats.sweep_units, percent (stats.sweep_units, stats.vars)); + PRT (" solved: %15" PRId64 " %10.2f per swept variable", + stats.sweep_solved, + relative (stats.sweep_solved, stats.sweep_variables)); + PRT (" sat: %15" PRId64 " %10.2f %% solved", + stats.sweep_sat, percent (stats.sweep_sat, stats.sweep_solved)); + PRT (" unsat: %15" PRId64 " %10.2f %% solved", + stats.sweep_unsat, + percent (stats.sweep_unsat, stats.sweep_solved)); + PRT (" backbone solved:%14" PRId64 " %10.2f %% solved", + stats.sweep_solved_backbone, + percent (stats.sweep_solved_backbone, stats.sweep_solved)); + PRT (" sat: %15" PRId64 " %10.2f %% backbone solved", + stats.sweep_sat_backbone, + percent (stats.sweep_sat_backbone, stats.sweep_solved_backbone)); + PRT (" unsat: %15" PRId64 " %10.2f %% backbone solved", + stats.sweep_unsat_backbone, + percent (stats.sweep_unsat_backbone, stats.sweep_solved_backbone)); + PRT (" unknown: %15" PRId64 " %10.2f %% backbone solved", + stats.sweep_unknown_backbone, + percent (stats.sweep_unknown_backbone, + stats.sweep_solved_backbone)); + PRT (" fixed: %15" PRId64 " %10.2f per swept variable", + stats.sweep_fixed_backbone, + relative (stats.sweep_fixed_backbone, stats.sweep_variables)); + PRT (" flip: %15" PRId64 " %10.2f per swept variable", + stats.sweep_flip_backbone, + relative (stats.sweep_flip_backbone, stats.sweep_variables)); + PRT (" flipped: %15" PRId64 " %10.2f %% of backbone flip", + stats.sweep_flipped_backbone, + percent (stats.sweep_flipped_backbone, stats.sweep_flip_backbone)); + PRT (" equiv solved: %15" PRId64 " %10.2f %% solved", + stats.sweep_solved_equivalences, + percent (stats.sweep_solved_equivalences, stats.sweep_solved)); + PRT (" sat: %15" PRId64 " %10.2f %% equiv solved", + stats.sweep_sat_equivalences, + percent (stats.sweep_sat_equivalences, + stats.sweep_solved_equivalences)); + PRT (" unsat: %15" PRId64 " %10.2f %% equiv solved", + stats.sweep_unsat_equivalences, + percent (stats.sweep_unsat_equivalences, + stats.sweep_solved_equivalences)); + PRT (" unknown: %15" PRId64 " %10.2f %% equiv solved", + stats.sweep_unknown_equivalences, + percent (stats.sweep_unknown_equivalences, + stats.sweep_solved_equivalences)); + PRT (" flip: %15" PRId64 " %10.2f per swept variable", + stats.sweep_flip_equivalences, + relative (stats.sweep_flip_equivalences, stats.sweep_variables)); + PRT (" flipped: %15" PRId64 " %10.2f %% of equiv flip", + stats.sweep_flipped_equivalences, + percent (stats.sweep_flipped_equivalences, + stats.sweep_flip_equivalences)); + PRT (" depth: %15" PRId64 " %10.2f per swept variable", + stats.sweep_depth, + relative (stats.sweep_depth, stats.sweep_variables)); + PRT (" environment: %15" PRId64 " %10.2f per swept variable", + stats.sweep_environment, + relative (stats.sweep_environment, stats.sweep_variables)); + PRT (" clauses: %15" PRId64 " %10.2f per swept variable", + stats.sweep_clauses, + relative (stats.sweep_clauses, stats.sweep_variables)); + PRT (" completed: %15" PRId64 " %10.2f sweeps to complete", + stats.sweep_completed, + relative (stats.sweep, stats.sweep_completed)); + } + if (all || stats.subsumed) { + PRT ("subsumed: %15" PRId64 " %10.2f %% of all clauses", + stats.subsumed, percent (stats.subsumed, stats.added.total)); + PRT (" subsumephases: %15" PRId64 " %10.2f interval", + stats.subsumephases, + relative (stats.conflicts, stats.subsumephases)); + PRT (" subsumerounds: %15" PRId64 " %10.2f per phase", + stats.subsumerounds, + relative (stats.subsumerounds, stats.subsumephases)); + PRT (" deduplicated: %15" PRId64 " %10.2f %% per subsumed", + stats.deduplicated, percent (stats.deduplicated, stats.subsumed)); + PRT (" transreds: %15" PRId64 " %10.2f interval", + stats.transreds, relative (stats.conflicts, stats.transreds)); + PRT (" transitive: %15" PRId64 " %10.2f %% per subsumed", + stats.transitive, percent (stats.transitive, stats.subsumed)); + PRT (" subirr: %15" PRId64 " %10.2f %% of subsumed", + stats.subirr, percent (stats.subirr, stats.subsumed)); + PRT (" subred: %15" PRId64 " %10.2f %% of subsumed", + stats.subred, percent (stats.subred, stats.subsumed)); + PRT (" subtried: %15" PRId64 " %10.2f tried per subsumed", + stats.subtried, relative (stats.subtried, stats.subsumed)); + PRT (" subchecks: %15" PRId64 " %10.2f per tried", + stats.subchecks, relative (stats.subchecks, stats.subtried)); + PRT (" subchecks2: %15" PRId64 " %10.2f %% per subcheck", + stats.subchecks2, percent (stats.subchecks2, stats.subchecks)); + PRT (" elimotfsub: %15" PRId64 " %10.2f %% of subsumed", + stats.elimotfsub, percent (stats.elimotfsub, stats.subsumed)); + PRT (" elimbwsub: %15" PRId64 " %10.2f %% of subsumed", + stats.elimbwsub, percent (stats.elimbwsub, stats.subsumed)); + PRT (" eagersub: %15" PRId64 " %10.2f %% of subsumed", + stats.eagersub, percent (stats.eagersub, stats.subsumed)); + PRT (" eagertried: %15" PRId64 " %10.2f tried per eagersub", + stats.eagertried, relative (stats.eagertried, stats.eagersub)); + } + if (all || stats.strengthened) { + PRT ("strengthened: %15" PRId64 " %10.2f %% of all clauses", + stats.strengthened, + percent (stats.strengthened, stats.added.total)); + PRT (" elimotfstr: %15" PRId64 " %10.2f %% of strengthened", + stats.elimotfstr, percent (stats.elimotfstr, stats.strengthened)); + PRT (" elimbwstr: %15" PRId64 " %10.2f %% of strengthened", + stats.elimbwstr, percent (stats.elimbwstr, stats.strengthened)); + } + if (all || stats.htrs) { + PRT ("ternary: %15" PRId64 " %10.2f %% of resolved", + stats.htrs, percent (stats.htrs, stats.ternres)); + PRT (" phases: %15" PRId64 " %10.2f interval", + stats.ternary, relative (stats.conflicts, stats.ternary)); + PRT (" htr3: %15" PRId64 + " %10.2f %% ternary hyper ternres", + stats.htrs3, percent (stats.htrs3, stats.htrs)); + PRT (" htr2: %15" PRId64 " %10.2f %% binary hyper ternres", + stats.htrs2, percent (stats.htrs2, stats.htrs)); + } + PRT ("ticks: %15" PRId64 " %10.2f propagation", totalticks, + relative (totalticks, stats.propagations.search)); + PRT (" searchticks: %15" PRId64 " %10.2f %% totalticks", + searchticks, percent (searchticks, totalticks)); + PRT (" stableticks: %15" PRId64 " %10.2f %% searchticks", + stats.ticks.search[1], percent (stats.ticks.search[1], searchticks)); + PRT (" unstableticks:%15" PRId64 " %10.2f %% searchticks", + stats.ticks.search[0], percent (stats.ticks.search[0], searchticks)); + PRT (" inprobeticks: %15" PRId64 " %10.2f %% totalticks", + inprobeticks, percent (inprobeticks, totalticks)); + PRT (" factorticks: %15" PRId64 " %10.2f %% searchticks", + stats.ticks.factor, percent (stats.ticks.factor, searchticks)); + PRT (" probeticks: %15" PRId64 " %10.2f %% searchticks", + stats.ticks.probe, percent (stats.ticks.probe, searchticks)); + PRT (" sweepticks: %15" PRId64 " %10.2f %% searchticks", + stats.ticks.sweep, percent (stats.ticks.sweep, searchticks)); + PRT (" ternaryticks: %15" PRId64 " %10.2f %% searchticks", + stats.ticks.ternary, percent (stats.ticks.ternary, searchticks)); + PRT (" vivifyticks: %15" PRId64 " %10.2f %% searchticks", + stats.ticks.vivify, percent (stats.ticks.vivify, searchticks)); + if (all) { + PRT ("tier recomputed: %15" PRId64 " %10.2f interval", + stats.tierecomputed, + relative (stats.conflicts, stats.tierecomputed)); + } + if (all || stats.ilbtriggers) { + PRT ("trail reuses: %15" PRId64 " %10.2f %% of incremental calls", + stats.ilbsuccess, percent (stats.ilbsuccess, stats.ilbtriggers)); + PRT (" levels: %15" PRId64 " %10.2f per reuse", + stats.levelsreused, + relative (stats.levelsreused, stats.ilbsuccess)); + PRT (" literals: %15" PRId64 " %10.2f per reuse", + stats.literalsreused, + relative (stats.literalsreused, stats.ilbsuccess)); + PRT (" assumptions: %15" PRId64 " %10.2f per reuse", + stats.assumptionsreused, + relative (stats.assumptionsreused, stats.ilbsuccess)); + } + if (all || vivified) { + PRT ("vivified: %15" PRId64 " %10.2f %% of all clauses", + vivified, percent (vivified, stats.added.total)); + PRT (" vivifications: %15" PRId64 " %10.2f interval", + stats.vivifications, + relative (stats.conflicts, stats.vivifications)); + PRT (" vivifychecks: %15" PRId64 " %10.2f %% per conflict", + stats.vivifychecks, percent (stats.vivifychecks, stats.conflicts)); + PRT (" vivifysched: %15" PRId64 " %10.2f %% checks per scheduled", + stats.vivifysched, + percent (stats.vivifychecks, stats.vivifysched)); + PRT (" vivifyunits: %15" PRId64 " %10.2f %% per vivify check", + stats.vivifyunits, + percent (stats.vivifyunits, stats.vivifychecks)); + PRT (" vivifyinst: %15" PRId64 " %10.2f %% per vivify check", + stats.vivifyinst, percent (stats.vivifyinst, stats.vivifychecks)); + PRT (" vivifysubs: %15" PRId64 " %10.2f %% per subsumed", + stats.vivifysubs, percent (stats.vivifysubs, stats.subsumed)); + PRT (" vivifysubred: %15" PRId64 " %10.2f %% per subs", + stats.vivifysubred, + percent (stats.vivifysubred, stats.vivifysubs)); + PRT (" vivifysubirr: %15" PRId64 " %10.2f %% per subs", + stats.vivifysubirr, + percent (stats.vivifysubirr, stats.vivifysubs)); + PRT (" vivifystrs: %15" PRId64 " %10.2f %% per strengthened", + stats.vivifystrs, percent (stats.vivifystrs, stats.strengthened)); + PRT (" vivifystrirr: %15" PRId64 " %10.2f %% per vivifystrs", + stats.vivifystrirr, + percent (stats.vivifystrirr, stats.vivifystrs)); + PRT (" vivifystred1: %15" PRId64 " %10.2f %% per vivifystrs", + stats.vivifystred1, + percent (stats.vivifystred1, stats.vivifystrs)); + PRT (" vivifystred2: %15" PRId64 " %10.2f %% per viviyfstrs", + stats.vivifystred2, + percent (stats.vivifystred2, stats.vivifystrs)); + PRT (" vivifystred3: %15" PRId64 " %10.2f %% per vivifystrs", + stats.vivifystred3, + percent (stats.vivifystred3, stats.vivifystrs)); + PRT (" vivifydemote: %15" PRId64 " %10.2f %% per vivifystrs", + stats.vivifydemote, + percent (stats.vivifydemote, stats.vivifystrs)); + PRT (" vivifydecs: %15" PRId64 " %10.2f per checks", + stats.vivifydecs, relative (stats.vivifydecs, stats.vivifychecks)); + PRT (" vivifyreused: %15" PRId64 " %10.2f %% per decision", + stats.vivifyreused, + percent (stats.vivifyreused, stats.vivifydecs)); + } + if (all || stats.walk.count) { + PRT ("walked: %15" PRId64 " %10.2f interval", + stats.walk.count, relative (stats.conflicts, stats.walk.count)); +#ifndef QUIET + if (internal->profiles.walk.value > 0) + PRT (" flips: %15" PRId64 " %10.2f M per second", + stats.walk.flips, + relative (1e-6 * stats.walk.flips, + internal->profiles.walk.value)); + else +#endif + PRT (" flips: %15" PRId64 " %10.2f per walk", + stats.walk.flips, relative (stats.walk.flips, stats.walk.count)); + if (stats.walk.minimum < LONG_MAX) + PRT (" minimum: %15" PRId64 " %10.2f %% clauses", + stats.walk.minimum, + percent (stats.walk.minimum, stats.added.irredundant)); + PRT (" broken: %15" PRId64 " %10.2f per flip", + stats.walk.broken, relative (stats.walk.broken, stats.walk.flips)); + } + if (all || stats.weakened) { + PRT ("weakened: %15" PRId64 " %10.2f average size", + stats.weakened, relative (stats.weakenedlen, stats.weakened)); + PRT (" extensions: %15" PRId64 " %10.2f interval", + stats.extensions, relative (stats.conflicts, stats.extensions)); + PRT (" flipped: %15" PRId64 " %10.2f per weakened", + stats.extended, relative (stats.extended, stats.weakened)); + } + + if (all || stats.congruence.gates) { + PRT ("congruence: %15" PRId64 " %10.2f interval", + stats.congruence.rounds, + relative (stats.conflicts, stats.congruence.rounds)); + PRT (" units: %15" PRId64 " %10.2f per congruent", + stats.congruence.units, + relative (stats.congruence.units, stats.congruence.congruent)); + PRT (" cong-and: %15" PRId64 " %10.2f per found gates", + stats.congruence.ands, + relative (stats.congruence.ands, stats.congruence.gates)); + PRT (" cong-ite: %15" PRId64 " %10.2f per found gates", + stats.congruence.ites, + relative (stats.congruence.ites, stats.congruence.gates)); + PRT (" cong-xor: %15" PRId64 " %10.2f per found gates", + stats.congruence.xors, + relative (stats.congruence.xors, stats.congruence.gates)); + PRT (" congruent: %15" PRId64 " %10.2f per round", + stats.congruence.congruent, + relative (stats.congruence.rounds, stats.congruence.congruent)); + PRT (" unaries: %15" PRId64 " %10.2f per round", + stats.congruence.unaries, + relative (stats.congruence.rounds, stats.congruence.unaries)); + PRT (" rewri.-ands: %15" PRId64 " %10.2f per round", + stats.congruence.rewritten_ands, + relative (stats.congruence.rounds, + stats.congruence.rewritten_ands)); + PRT (" subsumed: %15" PRId64 " %10.2f per round", + stats.congruence.subsumed, + relative (stats.congruence.rounds, stats.congruence.subsumed)); + } + + LINE (); + MSG ("%sseconds are measured in %s time for solving%s", + tout.magenta_code (), internal->opts.realtime ? "real" : "process", + tout.normal_code ()); + +#endif // ifndef QUIET +} + +void Internal::print_resource_usage () { +#ifndef QUIET + SECTION ("resources"); + uint64_t m = maximum_resident_set_size (); + MSG ("total process time since initialization: %12.2f seconds", + internal->process_time ()); + MSG ("total real time since initialization: %12.2f seconds", + internal->real_time ()); + MSG ("maximum resident set size of process: %12.2f MB", + m / (double) (1l << 20)); +#endif +} + +/*------------------------------------------------------------------------*/ + +void Checker::print_stats () { + + if (!stats.added && !stats.deleted) + return; + + SECTION ("checker statistics"); + + MSG ("checks: %15" PRId64 "", stats.checks); + MSG ("assumptions: %15" PRId64 " %10.2f per check", + stats.assumptions, relative (stats.assumptions, stats.checks)); + MSG ("propagations: %15" PRId64 " %10.2f per check", + stats.propagations, relative (stats.propagations, stats.checks)); + MSG ("original: %15" PRId64 " %10.2f %% of all clauses", + stats.original, percent (stats.original, stats.added)); + MSG ("derived: %15" PRId64 " %10.2f %% of all clauses", + stats.derived, percent (stats.derived, stats.added)); + MSG ("deleted: %15" PRId64 " %10.2f %% of all clauses", + stats.deleted, percent (stats.deleted, stats.added)); + MSG ("insertions: %15" PRId64 " %10.2f %% of all clauses", + stats.insertions, percent (stats.insertions, stats.added)); + MSG ("collections: %15" PRId64 " %10.2f deleted per collection", + stats.collections, relative (stats.collections, stats.deleted)); + MSG ("collisions: %15" PRId64 " %10.2f per search", + stats.collisions, relative (stats.collisions, stats.searches)); + MSG ("searches: %15" PRId64 "", stats.searches); + MSG ("units: %15" PRId64 "", stats.units); +} + +void LratChecker::print_stats () { + + if (!stats.added && !stats.deleted) + return; + + SECTION ("lrat checker statistics"); + + MSG ("checks: %15" PRId64 "", stats.checks); + MSG ("insertions: %15" PRId64 " %10.2f %% of all clauses", + stats.insertions, percent (stats.insertions, stats.added)); + MSG ("original: %15" PRId64 " %10.2f %% of all clauses", + stats.original, percent (stats.original, stats.added)); + MSG ("derived: %15" PRId64 " %10.2f %% of all clauses", + stats.derived, percent (stats.derived, stats.added)); + MSG ("deleted: %15" PRId64 " %10.2f %% of all clauses", + stats.deleted, percent (stats.deleted, stats.added)); + MSG ("finalized: %15" PRId64 " %10.2f %% of all clauses", + stats.finalized, percent (stats.finalized, stats.added)); + MSG ("collections: %15" PRId64 " %10.2f deleted per collection", + stats.collections, relative (stats.collections, stats.deleted)); + MSG ("collisions: %15" PRId64 " %10.2f per search", + stats.collisions, relative (stats.collisions, stats.searches)); + MSG ("searches: %15" PRId64 "", stats.searches); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/stats.hpp b/src/sat/cadical/stats.hpp new file mode 100644 index 000000000..7eb9e9be5 --- /dev/null +++ b/src/sat/cadical/stats.hpp @@ -0,0 +1,370 @@ +#ifndef _stats_hpp_INCLUDED +#define _stats_hpp_INCLUDED + +#include <cstdint> +#include <cstdlib> +#include <vector> + +namespace CaDiCaL { + +struct Internal; + +struct Stats { + + Internal *internal; + + int64_t vars = 0; // internal initialized variables + + int64_t conflicts = 0; // generated conflicts in 'propagate' + int64_t decisions = 0; // number of decisions in 'decide' + + struct { + int64_t cover = 0; // propagated during covered clause elimination + int64_t instantiate = 0; // propagated during variable instantiation + int64_t probe = 0; // propagated during probing + int64_t search = 0; // propagated literals during search + int64_t transred = 0; // propagated during transitive reduction + int64_t vivify = 0; // propagated during vivification + int64_t walk = 0; // propagated during local search + } propagations; + + struct { + int64_t search[2] = {0}; + int64_t factor = 0; + int64_t probe = 0; + int64_t sweep = 0; + int64_t ternary = 0; + int64_t vivify = 0; + } ticks; + + struct { + int64_t ext_cb = 0; // number of times any external callback was called + int64_t eprop_call = 0; // number of times external_propagate was called + int64_t eprop_prop = 0; // number of times external propagate propagated + int64_t eprop_conf = + 0; // number of times ex-propagate was already falsified + int64_t eprop_expl = + 0; // number of times external propagate was explained + int64_t elearn_call = + 0; // number of times external clause learning was tried + int64_t elearned = + 0; // learned external clauses (incl. eprop explanations) + int64_t elearn_prop = + 0; // number of learned and propagating external clauses + int64_t elearn_conf = + 0; // number of learned and conflicting external clauses + int64_t echeck_call = 0; // number of checking found complete solutions + } ext_prop; + + int64_t condassinit = 0; // initial assigned literals + int64_t condassirem = 0; // initial assigned literals for blocked + int64_t condassrem = 0; // remaining assigned literals for blocked + int64_t condassvars = 0; // sum of active variables at initial assignment + int64_t condautinit = 0; // initial literals in autarky part + int64_t condautrem = 0; // remaining literals in autarky part for blocked + int64_t condcands = 0; // globally blocked candidate clauses + int64_t condcondinit = 0; // initial literals in conditional part + int64_t condcondrem = + 0; // remaining literals in conditional part for blocked + int64_t conditioned = 0; // globally blocked clauses eliminated + int64_t conditionings = 0; // globally blocked clause eliminations + int64_t condprops = 0; // propagated unassigned literals + + struct { + int64_t block = 0; // block marked literals + int64_t elim = 0; // elim marked variables + int64_t subsume = 0; // subsume marked variables + int64_t ternary = 0; // ternary marked variables + int64_t factor = 0; + } mark; + + struct { + int64_t total = 0; + int64_t redundant = 0; + int64_t irredundant = 0; + } current, added; // Clauses. + + struct { + double process = 0, real = 0; + } time; + + struct { + int64_t count = 0; // number of covered clause elimination rounds + int64_t asymmetric = 0; // number of asymmetric tautologies in CCE + int64_t blocked = 0; // number of blocked covered tautologies + int64_t total = 0; // total number of eliminated clauses + } cover; + + struct { + int64_t tried = 0; + int64_t succeeded = 0; + struct { + int64_t one = 0, zero = 0; + } constant, forward, backward; + struct { + int64_t positive = 0, negative = 0; + } horn; + } lucky; + + struct { + int64_t total = 0; // total number of happened rephases + int64_t best = 0; // how often reset to best phases + int64_t flipped = 0; // how often reset phases by flipping + int64_t inverted = 0; // how often reset to inverted phases + int64_t original = 0; // how often reset to original phases + int64_t random = 0; // how often randomly reset phases + int64_t walk = 0; // phases improved through random walked + } rephased; + + struct { + int64_t count = 0; + int64_t broken = 0; + int64_t flips = 0; + int64_t minimum = 0; + } walk; + + struct { + int64_t count = 0; // flushings of learned clauses counter + int64_t learned = 0; // flushed learned clauses + int64_t hyper = 0; // flushed hyper binary/ternary clauses + } flush; + + int64_t compacts = 0; // number of compactifications + int64_t shuffled = 0; // shuffled queues and scores + int64_t restarts = 0; // actual number of happened restarts + int64_t restartlevels = 0; // levels at restart + int64_t restartstable = 0; // actual number of happened restarts + int64_t stabphases = 0; // number of stabilization phases + int64_t stabconflicts = + 0; // number of search conflicts during stabilizing + int64_t rescored = 0; // number of times scores were rescored + int64_t reused = 0; // number of reused trails + int64_t reusedlevels = 0; // reused levels at restart + int64_t reusedstable = 0; // number of reused trails during stabilizing + int64_t sections = 0; // 'section' counter + int64_t chrono = 0; // chronological backtracks + int64_t backtracks = 0; // number of backtracks + int64_t improvedglue = 0; // improved glue during bumping + int64_t promoted1 = 0; // promoted clauses to tier one + int64_t promoted2 = 0; // promoted clauses to tier two + int64_t bumped = 0; // seen and bumped variables in 'analyze' + int64_t recomputed = 0; // recomputed glues 'recompute_glue' + int64_t searched = 0; // searched decisions in 'decide' + int64_t reductions = 0; // 'reduce' counter + int64_t reduced = 0; // number of reduced clauses + int64_t reduced_sqrt = 0; + int64_t reduced_prct = 0; + int64_t collected = 0; // number of collected bytes + int64_t collections = 0; // number of garbage collections + int64_t hbrs = 0; // hyper binary resolvents + int64_t hbrsizes = 0; // sum of hyper resolved base clauses + int64_t hbreds = 0; // redundant hyper binary resolvents + int64_t hbrsubs = 0; // subsuming hyper binary resolvents + int64_t instried = 0; // number of tried instantiations + int64_t instantiated = 0; // number of successful instantiations + int64_t instrounds = 0; // number of instantiation rounds + int64_t subsumed = 0; // number of subsumed clauses + int64_t deduplicated = 0; // number of removed duplicated binary clauses + int64_t deduplications = 0; // number of deduplication phases + int64_t strengthened = 0; // number of strengthened clauses + + int64_t eliminated_equi = + 0; // number of successful equivalence eliminations + int64_t eliminated_and = 0; // number of successful AND gate eliminations + int64_t eliminated_ite = 0; // number of successful ITE gate eliminations + int64_t eliminated_xor = 0; // number of successful XOR gate eliminations + int64_t eliminated_def = + 0; // number of successful definition eliminations + + int64_t definitions_checked = 0; + int64_t definitions_extracted = 0; + int64_t definition_units = 0; + int64_t definition_ticks = 0; + + int64_t factor = 0; + int64_t factored = 0; + int64_t factor_added = 0; + int64_t variables_extension = 0; + int64_t variables_original = 0; + int64_t literals_factored = 0; + int64_t clauses_unfactored = 0; + int64_t literals_unfactored = 0; + + int64_t elimotfstr = + 0; // number of on-the-fly strengthened during elimination + int64_t subirr = 0; // number of subsumed irredundant clauses + int64_t subred = 0; // number of subsumed redundant clauses + int64_t subtried = 0; // number of tried subsumptions + int64_t subchecks = 0; // number of pair-wise subsumption checks + int64_t subchecks2 = 0; // same but restricted to binary clauses + int64_t elimotfsub = + 0; // number of on-the-fly subsumed during elimination + int64_t subsumerounds = 0; // number of subsumption rounds + int64_t subsumephases = 0; // number of scheduled subsumption phases + int64_t eagertried = 0; // number of traversed eager subsumed candidates + int64_t eagersub = + 0; // number of eagerly subsumed recently learned clauses + int64_t elimres = 0; // number of resolved clauses in BVE + int64_t elimrestried = 0; // number of tried resolved clauses in BVE + int64_t elimfastrounds = 0; // number of elimination rounds + int64_t elimrounds = 0; // number of elimination rounds + int64_t elimphases = 0; // number of scheduled elimination phases + int64_t elimfastphases = 0; // number of scheduled elimination phases + int64_t elimcompleted = 0; // number complete elimination procedures + int64_t elimtried = 0; // number of variable elimination attempts + int64_t elimsubst = 0; // number of eliminations through substitutions + int64_t elimgates = 0; // number of gates found during elimination + int64_t elimequivs = 0; // number of equivalences found during elimination + int64_t elimands = 0; // number of AND gates found during elimination + int64_t elimites = 0; // number of ITE gates found during elimination + int64_t elimxors = 0; // number of XOR gates found during elimination + int64_t elimbwsub = 0; // number of eager backward subsumed clauses + int64_t elimbwstr = 0; // number of eager backward strengthened clauses + int64_t ternary = 0; // number of ternary resolution phases + int64_t ternres = 0; // number of ternary resolutions + int64_t htrs = 0; // number of hyper ternary resolvents + int64_t htrs2 = 0; // number of binary hyper ternary resolvents + int64_t htrs3 = 0; // number of ternary hyper ternary resolvents + int64_t decompositions = 0; // number of SCC + ELS + int64_t vivifications = 0; // number of vivifications + int64_t vivifychecks = 0; // checked clauses during vivification + int64_t vivifydecs = 0; // vivification decisions + int64_t vivifyreused = 0; // reused vivification decisions + int64_t vivifysched = 0; // scheduled clauses for vivification + int64_t vivifysubs = 0; // subsumed clauses during vivification + int64_t vivifysubred = 0; // subsumed clauses during vivification + int64_t vivifysubirr = 0; // subsumed clauses during vivification + int64_t vivifystrs = 0; // strengthened clauses during vivification + int64_t vivifystrirr = 0; // strengthened irredundant clause + int64_t vivifystred1 = 0; // strengthened redundant clause (1) + int64_t vivifystred2 = 0; // strengthened redundant clause (2) + int64_t vivifystred3 = 0; // strengthened redundant clause (3) + int64_t vivifyunits = 0; // units during vivification + int64_t vivifyimplied = 0; // implied during vivification + int64_t vivifyinst = 0; // instantiation during vivification + int64_t vivifydemote = 0; // demoting during vivification + int64_t transreds = 0; + int64_t transitive = 0; + struct { + int64_t literals = 0; + int64_t clauses = 0; + } learned; + int64_t minimized = 0; // minimized literals + int64_t shrunken = 0; // shrunken literals + int64_t minishrunken = 0; // shrunken during minimization literals + + int64_t irrlits = 0; // literals in irredundant clauses + struct { + int64_t bytes = 0; + int64_t clauses = 0; + int64_t literals = 0; + } garbage; + + int64_t sweep_units = 0; + int64_t sweep_flip_backbone = 0; + int64_t sweep_fixed_backbone = 0; + int64_t sweep_flipped_backbone = 0; + int64_t sweep_solved_backbone = 0; + int64_t sweep_sat_backbone = 0; + int64_t sweep_unsat_backbone = 0; + int64_t sweep_unknown_backbone = 0; + int64_t sweep_flip_equivalences = 0; + int64_t sweep_flipped_equivalences = 0; + int64_t sweep_sat_equivalences = 0; + int64_t sweep_unsat_equivalences = 0; + int64_t sweep_unknown_equivalences = 0; + int64_t sweep_solved_equivalences = 0; + int64_t sweep_equivalences = 0; + int64_t sweep_variables = 0; + int64_t sweep_completed = 0; + int64_t sweep_solved = 0; + int64_t sweep_sat = 0; + int64_t sweep_unsat = 0; + int64_t sweep_depth = 0; + int64_t sweep_environment = 0; + int64_t sweep_clauses = 0; + int64_t sweep = 0; + + int64_t units = 0; // learned unit clauses + int64_t binaries = 0; // learned binary clauses + int64_t inprobingphases = 0; // number of scheduled probing phases + int64_t probingrounds = 0; // number of probing rounds + int64_t inprobesuccess = 0; // number successful probing phases + int64_t probed = 0; // number of probed literals + int64_t failed = 0; // number of failed literals + int64_t hyperunary = 0; // hyper unary resolved unit clauses + int64_t probefailed = 0; // failed literals from probing + int64_t transredunits = 0; // units derived in transitive reduction + int64_t blockings = 0; // number of blocked clause eliminations + int64_t blocked = 0; // number of actually blocked clauses + int64_t blockres = 0; // number of resolutions during blocking + int64_t blockcands = 0; // number of clause / pivot pairs tried + int64_t blockpured = 0; // number of clauses blocked through pure literals + int64_t blockpurelits = 0; // number of pure literals + int64_t extensions = 0; // number of extended witnesses + int64_t extended = 0; // number of flipped literals during extension + int64_t weakened = 0; // number of clauses pushed to extension stack + int64_t weakenedlen = 0; // lengths of weakened clauses + int64_t restorations = 0; // number of restore calls + int64_t restored = 0; // number of restored clauses + int64_t reactivated = 0; // number of reactivated clauses + int64_t restoredlits = 0; // number of restored literals + + int64_t preprocessings = 0; + + int64_t ilbtriggers = 0; + int64_t ilbsuccess = 0; + int64_t levelsreused = 0; + int64_t literalsreused = 0; + int64_t assumptionsreused = 0; + int64_t tierecomputed = 0; // number of tier recomputation; + + struct { + int64_t fixed = 0; // number of top level assigned variables + int64_t eliminated = 0; // number of eliminated variables + int64_t fasteliminated = 0; // number of fast eliminated variables only + int64_t substituted = 0; // number of substituted variables + int64_t pure = 0; // number of pure literals + } all, now; + + struct { + int64_t strengthened = 0; // number of clauses strengthened during OTFS + int64_t subsumed = 0; // number of clauses subsumed by OTFS + } otfs; + + int64_t unused = 0; // number of unused variables + int64_t active = 0; // number of active variables + int64_t inactive = 0; // number of inactive variables + std::vector<uint64_t> bump_used = {0, 0}; + std::vector<std::vector<uint64_t>> used; // used clauses in focused mode + + struct { + int64_t gates = 0; + int64_t ands = 0; + int64_t ites = 0; + int64_t xors = 0; + int64_t units = 0; + int64_t congruent = 0; + int64_t rounds = 0; + int64_t unary_and = 0; + int64_t unaries = 0; + int64_t rewritten_ands = 0; + int64_t simplified = 0; + int64_t simplified_ands = 0; + int64_t simplified_xors = 0; + int64_t simplified_ites = 0; + int64_t subsumed = 0; + int64_t trivial_ite = 0; + int64_t unary_ites = 0; + } congruence; + + Stats (); + + void print (Internal *); +}; + +/*------------------------------------------------------------------------*/ + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/subsume.cpp b/src/sat/cadical/subsume.cpp new file mode 100644 index 000000000..a125a10f4 --- /dev/null +++ b/src/sat/cadical/subsume.cpp @@ -0,0 +1,646 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// This file implements a global forward subsumption algorithm, which is run +// frequently during search. It works both on original (irredundant) +// clauses and on 'sticky' learned clauses which are likely to be kept. +// This is abstracted away in the 'likely_to_be_kept_clause' function, which +// implicitly relies on 'opts.reducetier1glue' (glucose level of clauses +// which are not reduced) as well as dynamically determined size and glucose +// level ('lim.keptglue' and 'lim.keptsize') of clauses kept in 'reduce'. +// +// Note, that 'forward' means that the clause from which the subsumption +// check is started is checked for being subsumed by other (smaller or equal +// size) clauses. Since 'vivification' is an extended version of subsume, +// more powerful, but also slower, we schedule 'vivify' right after +// 'subsume', which in contrast to 'subsume' is not run until to completion. +// +// This implementation is inspired by Bayardo's SDM'11 analysis of our +// subsumption algorithm in our SATeLite preprocessor in the context of +// finding extremal sets in data mining and his suggested improvements. + +// Our original subsumption algorithm in 'Quantor' and 'SATeLite' (and in +// MiniSAT and descendants) is based on backward subsumption. It uses the +// observation that only the occurrence list of one literal of a clause has +// to be traversed in order to find all potential clauses which are subsumed +// by the candidate. Thus the literal with the smallest number of +// occurrences is used. However, that scheme requires to connect all +// literals of surviving clauses, while forward algorithms only need to +// connect one literal. On the other hand forward checking requires to +// traverse the occurrence lists of all literals of the candidate clause to +// find subsuming clauses. During connecting the single literal (similar to +// the one-watch scheme by Lintao Zhang) one can connect a literal with a +// minimal number of occurrence so far, which implicitly also reduces future +// occurrence list traversal time. + +// Also the actual subsumption check is cheaper since during backward +// checking the short subsuming candidate clause is marked and all the +// literals in the larger subsume candidate clauses have to be traversed, +// while for our forward approach the long subsumed candidate clause is only +// marked once, while the literals of the shorter subsuming clauses have to +// be checked. We also use a fixed special more cache friendly data +// structure for binary clauses, to avoid traversing them directly. + +// In our forward scheme it is still possible to skip occurrence lists of +// literals which were not added since the last subsumption round, since +// only those can contain subsuming candidates. Actually, clauses which +// contain at least one literal, which was not added since the last +// subsumption round do not have to be connected at all, even though they +// might still be subsumed them self. + +// Bayardo suggests to sort the literals in clauses and perform some kind of +// partial merge-sort, while we mark literals, but do sort literals during +// connecting a clause w.r.t. the number of occurrences, in order to find +// literals which do not occur in the subsumed candidate fast with high +// probability (less occurring literals have a higher chance). + +// This is the actual subsumption and strengthening check. We assume that +// all the literals of the candidate clause to be subsumed or strengthened +// are marked, so we only have to check that all the literals of the +// argument clause 'subsuming', which is checked for subsuming the candidate +// clause 'subsumed', has all its literals marked (in the correct phase). +// If exactly one is in the opposite phase we can still strengthen the +// candidate clause by this single literal which occurs in opposite phase. +// +// The result is INT_MIN if all literals are marked and thus the candidate +// clause can be subsumed. It is zero if neither subsumption nor +// strengthening is possible. Otherwise the candidate clause can be +// strengthened and as a result the negation of the literal which can be +// removed is returned. + +inline int Internal::subsume_check (Clause *subsuming, Clause *subsumed) { +#ifdef NDEBUG + (void) subsumed; +#endif + // Only use 'subsumed' for these following assertion checks. Otherwise we + // only require that 'subsumed' has all its literals marked. + // + assert (!subsumed->garbage); + assert (!subsuming->garbage); + assert (subsuming != subsumed); + assert (subsuming->size <= subsumed->size); + + stats.subchecks++; + if (subsuming->size == 2) + stats.subchecks2++; + + int flipped = 0, prev = 0; + bool failed = false; + const auto eoc = subsuming->end (); + for (auto i = subsuming->begin (); !failed && i != eoc; i++) { + int lit = *i; + *i = prev; + prev = lit; + const int tmp = marked (lit); + if (!tmp) + failed = true; + else if (tmp > 0) + continue; + else if (flipped) + failed = true; + else + flipped = lit; + } + assert (prev); + assert (!subsuming->literals[0]); + subsuming->literals[0] = prev; + if (failed) + return 0; + + if (!flipped) + return INT_MIN; // subsumed!! + else if (!opts.subsumestr) + return 0; + else + return flipped; // strengthen!! +} + +/*------------------------------------------------------------------------*/ + +// Candidate clause 'subsumed' is subsumed by 'subsuming'. + +inline void Internal::subsume_clause (Clause *subsuming, Clause *subsumed) { + stats.subsumed++; + assert (subsuming->size <= subsumed->size); + LOG (subsumed, "subsumed"); + if (subsumed->redundant) + stats.subred++; + else + stats.subirr++; + if (subsumed->redundant || !subsuming->redundant) { + mark_garbage (subsumed); + return; + } + LOG ("turning redundant subsuming clause into irredundant clause"); + subsuming->redundant = false; + if (proof) + proof->strengthen (subsuming->id); + mark_garbage (subsumed); + stats.current.irredundant++; + stats.added.irredundant++; + stats.irrlits += subsuming->size; + assert (stats.current.redundant > 0); + stats.current.redundant--; + assert (stats.added.redundant > 0); + stats.added.redundant--; + // ... and keep 'stats.added.total'. +} + +/*------------------------------------------------------------------------*/ + +// Candidate clause 'c' is strengthened by removing 'lit'. + +void Internal::strengthen_clause (Clause *c, int lit) { + if (opts.check && is_external_forgettable (c->id)) + mark_garbage_external_forgettable (c->id); + stats.strengthened++; + assert (c->size > 2); + LOG (c, "removing %d in", lit); + if (proof) { + LOG (lrat_chain, "strengthening clause with chain"); + proof->strengthen_clause (c, lit, lrat_chain); + } + if (!c->redundant) + mark_removed (lit); + auto new_end = remove (c->begin (), c->end (), lit); + assert (new_end + 1 == c->end ()), (void) new_end; + (void) shrink_clause (c, c->size - 1); + // bump_clause2 (c); + LOG (c, "strengthened"); + external->check_shrunken_clause (c); +} + +/*------------------------------------------------------------------------*/ + +// Find clauses connected in the occurrence lists 'occs' which subsume the +// candidate clause 'c' given as first argument. If this is the case the +// clause is subsumed and the result is positive. If the clause was +// strengthened the result is negative. Otherwise the candidate clause +// can not be subsumed nor strengthened and zero is returned. + +inline int Internal::try_to_subsume_clause (Clause *c, + vector<Clause *> &shrunken) { + + stats.subtried++; + assert (!level); + LOG (c, "trying to subsume"); + + mark (c); // signed! + + Clause *d = 0; + int flipped = 0; + + for (const auto &lit : *c) { + + // Only clauses which have a variable which has recently been added are + // checked for being subsumed. The idea is that all these newly added + // clauses are candidates for subsuming the clause. Then we also only + // need to check occurrences of these variables. The occurrence lists + // of other literal do not have to be checked. + // + if (!flags (lit).subsume) + continue; + + for (int sign = -1; !d && sign <= 1; sign += 2) { + + // First we check against all binary clauses. The other literals of + // all binary clauses of 'sign*lit' are stored in one consecutive + // array, which is way faster than storing clause pointers and + // dereferencing them. Since this binary clause array is also not + // shrunken, we also can bail out earlier if subsumption or + // strengthening is determined. + + // In both cases the (self-)subsuming clause is stored in 'd', which + // makes it nonzero and forces aborting both the outer and inner loop. + // If the binary clause can strengthen the candidate clause 'c' + // (through self-subsuming resolution), then 'filled' is set to the + // literal which can be removed in 'c', otherwise to 'INT_MIN' which + // is a non-valid literal. + + for (const auto &bin : bins (sign * lit)) { + const auto &other = bin.lit; + const int tmp = marked (other); + if (!tmp) + continue; + if (tmp < 0 && sign < 0) + continue; + if (tmp < 0) { + if (sign < 0) + continue; // tautological resolvent + dummy_binary->literals[0] = lit; + dummy_binary->literals[1] = other; + flipped = other; + } else { + dummy_binary->literals[0] = sign * lit; + dummy_binary->literals[1] = other; + flipped = (sign < 0) ? -lit : INT_MIN; + } + + // This dummy binary clauses is initialized in 'Internal::Internal' + // and only changes it literals in the lines above. By using such + // a faked binary clause we can simply reuse 'subsume_clause' as + // well as the code around 'strengthen_clause' uniform for both real + // clauses and this special case for binary clauses + + dummy_binary->id = bin.id; + d = dummy_binary; + + break; + } + + if (d) + break; + + // In this second loop we check for larger than binary clauses to + // subsume or strengthen the candidate clause. This is more costly, + // and needs a call to 'subsume_check'. Otherwise the same contract + // as above for communicating 'subsumption' or 'strengthening' to the + // code after the loop is used. + // + const Occs &os = occs (sign * lit); + for (const auto &e : os) { + assert (!e->garbage); // sanity check + if (e->garbage) + continue; // defensive: not needed + flipped = subsume_check (e, c); + if (!flipped) + continue; + d = e; // leave also outer loop + break; + } + } + + if (d) + break; + } + + unmark (c); + + if (flipped == INT_MIN) { + LOG (d, "subsuming"); + subsume_clause (d, c); + return 1; + } + + if (flipped) { + LOG (d, "strengthening"); + if (lrat) { + assert (lrat_chain.empty ()); + lrat_chain.push_back (c->id); + lrat_chain.push_back (d->id); + } + if (d->used > c->used) + c->used = d->used; + strengthen_clause (c, -flipped); + lrat_chain.clear (); + assert (likely_to_be_kept_clause (c)); + shrunken.push_back (c); + return -1; + } + + return 0; +} + + +struct subsume_less_noccs { + Internal *internal; + subsume_less_noccs (Internal *i) : internal (i) {} + bool operator() (int a, int b) { + const signed char u = internal->val (a), v = internal->val (b); + if (!u && v) + return true; + if (u && !v) + return false; + const int64_t m = internal->noccs (a), n = internal->noccs (b); + if (m < n) + return true; + if (m > n) + return false; + return abs (a) < abs (b); + } +}; + +/*------------------------------------------------------------------------*/ + +// Usually called from 'subsume' below if 'subsuming' triggered it. Then +// the idea is to subsume both redundant and irredundant clauses. It is also +// called in the elimination loop in 'elim' in which case we focus on +// irredundant clauses only to help bounded variable elimination. The +// function returns true of an irredundant clause was removed or +// strengthened, which might then in the second usage scenario trigger new +// variable eliminations. + +bool Internal::subsume_round () { + + if (!opts.subsume) + return false; + if (unsat) + return false; + if (terminated_asynchronously ()) + return false; + if (!stats.current.redundant && !stats.current.irredundant) + return false; + + START_SIMPLIFIER (subsume, SUBSUME); + stats.subsumerounds++; + + int64_t check_limit; + if (opts.subsumelimited) { + int64_t delta = stats.propagations.search; + delta *= 1e-3 * opts.subsumeeffort; + if (delta < opts.subsumemineff) + delta = opts.subsumemineff; + if (delta > opts.subsumemaxeff) + delta = opts.subsumemaxeff; + delta = max (delta, (int64_t) 2l * active ()); + + PHASE ("subsume-round", stats.subsumerounds, + "limit of %" PRId64 " subsumption checks", delta); + + check_limit = stats.subchecks + delta; + } else { + PHASE ("subsume-round", stats.subsumerounds, + "unlimited subsumption checks"); + check_limit = LONG_MAX; + } + + int old_marked_candidate_variables_for_elimination = stats.mark.elim; + + assert (!level); + + // Allocate schedule and occurrence lists. + // + vector<ClauseSize> schedule; + init_noccs (); + + // Determine candidate clauses and sort them by size. + // + int64_t left_over_from_last_subsumption_round = 0; + + for (auto c : clauses) { + + if (c->garbage) + continue; + if (c->size > opts.subsumeclslim) + continue; + if (!likely_to_be_kept_clause (c)) + continue; + + bool fixed = false; + int subsume = 0; + for (const auto &lit : *c) + if (val (lit)) + fixed = true; + else if (flags (lit).subsume) + subsume++; + + // If the clause contains a root level assigned (fixed) literal we will + // not work on it. This simplifies the code substantially since we do + // not have to care about assignments at all. Strengthening becomes + // much simpler too. + // + if (fixed) { + LOG (c, "skipping (fixed literal)"); + continue; + } + + // Further, if less than two variables in the clause were added since + // the last subsumption round, the clause is ignored too. + // + if (subsume < 2) { + LOG (c, "skipping (only %d added literals)", subsume); + continue; + } + + if (c->subsume) + left_over_from_last_subsumption_round++; + schedule.push_back (ClauseSize (c->size, c)); + for (const auto &lit : *c) + noccs (lit)++; + } + shrink_vector (schedule); + + // Smaller clauses are checked and connected first. + // + rsort (schedule.begin (), schedule.end (), smaller_clause_size_rank ()); + + if (!left_over_from_last_subsumption_round) + for (auto cs : schedule) + if (cs.clause->size > 2) + cs.clause->subsume = true; + +#ifndef QUIET + int64_t scheduled = schedule.size (); + int64_t total = stats.current.irredundant + stats.current.redundant; + PHASE ("subsume-round", stats.subsumerounds, + "scheduled %" PRId64 " clauses %.0f%% out of %" PRId64 " clauses", + scheduled, percent (scheduled, total), total); +#endif + + // Now go over the scheduled clauses in the order of increasing size and + // try to forward subsume and strengthen them. Forward subsumption tries + // to find smaller or same size clauses which subsume or might strengthen + // the candidate. After the candidate has been processed connect one + // of its literals (with smallest number of occurrences at this point) in + // a one-watched scheme. + + int64_t subsumed = 0, strengthened = 0, checked = 0; + + vector<Clause *> shrunken; + init_occs (); + init_bins (); + + for (const auto &s : schedule) { + + if (terminated_asynchronously ()) + break; + if (stats.subchecks >= check_limit) + break; + + Clause *c = s.clause; + assert (!c->garbage); + + checked++; + + // First try to subsume or strengthen this candidate clause. For binary + // clauses this could be done much faster by hashing and is costly due + // to a usually large number of binary clauses. There is further the + // issue, that strengthening binary clauses (through double + // self-subsuming resolution) would produce units, which needs much more + // care. In the same (lazy) spirit we also ignore clauses with fixed + // literals (false or true). + // + if (c->size > 2 && c->subsume) { + c->subsume = false; + const int tmp = try_to_subsume_clause (c, shrunken); + if (tmp > 0) { + subsumed++; + continue; + } + if (tmp < 0) + strengthened++; + } + + // If not subsumed connect smallest occurring literal, where occurring + // means the number of times it was used to connect (as a one-watch) a + // previous smaller or equal sized clause. This minimizes the length of + // the occurrence lists traversed during 'try_to_subsume_clause'. Also + // note that this number is usually way smaller than the number of + // occurrences computed before and stored in 'noccs'. + // + int minlit = 0; + int64_t minoccs = 0; + size_t minsize = 0; + bool subsume = true; + bool binary = (c->size == 2 && !c->redundant); + + for (const auto &lit : *c) { + + if (!flags (lit).subsume) + subsume = false; + const size_t size = binary ? bins (lit).size () : occs (lit).size (); + if (minlit && minsize <= size) + continue; + const int64_t tmp = noccs (lit); + if (minlit && minsize == size && tmp <= minoccs) + continue; + minlit = lit, minsize = size, minoccs = tmp; + } + + // If there is a variable in a clause different from is not 'subsume' + // (has been added since the last subsumption round), then this clause + // can not serve to strengthen or subsume another clause, since all + // shrunken or added clauses mark all their variables as 'subsume'. + // + if (!subsume) + continue; + + if (!binary) { + + // If smallest occurring literal occurs too often do not connect. + // + if (minsize > (size_t) opts.subsumeocclim) + continue; + + LOG (c, + "watching %d with %zd current and total %" PRId64 " occurrences", + minlit, minsize, minoccs); + + occs (minlit).push_back (c); + + // This sorting should give faster failures for assumption checks + // since the less occurring variables are put first in a clause and + // thus will make it more likely to be found as witness for a clause + // not to be subsuming. One could in principle (see also the + // discussion on 'subsumption' in our 'Splatz' solver) replace marking + // by a kind of merge sort, as also suggested by Bayardo. It would + // avoid 'marked' calls and thus might be slightly faster but could + // not take benefit of this sorting optimization. + // + sort (c->begin (), c->end (), subsume_less_noccs (this)); + + } else { + + // If smallest occurring literal occurs too often do not connect. + // + if (minsize > (size_t) opts.subsumebinlim) + continue; + + LOG (c, + "watching %d with %zd current binary and total %" PRId64 + " occurrences", + minlit, minsize, minoccs); + + const int minlit_pos = (c->literals[1] == minlit); + const int other = c->literals[!minlit_pos]; + bins (minlit).push_back (Bin{other, c->id}); + } + } + + PHASE ("subsume-round", stats.subsumerounds, + "subsumed %" PRId64 " and strengthened %" PRId64 " out of %" PRId64 + " clauses %.0f%%", + subsumed, strengthened, scheduled, + percent (subsumed + strengthened, scheduled)); + + const int64_t remain = schedule.size () - checked; + const bool completed = !remain; + + if (completed) + PHASE ("subsume-round", stats.subsumerounds, + "checked all %" PRId64 " scheduled clauses", checked); + else + PHASE ("subsume-round", stats.subsumerounds, + "checked %" PRId64 " clauses %.0f%% of scheduled (%" PRId64 + " remain)", + checked, percent (checked, scheduled), remain); + + // Release occurrence lists and schedule. + // + erase_vector (schedule); + reset_noccs (); + reset_occs (); + reset_bins (); + + // Reset all old 'added' flags and mark variables in shrunken + // clauses as 'added' for the next subsumption round. + // + if (completed) + reset_subsume_bits (); + + for (const auto &c : shrunken) + mark_added (c); + erase_vector (shrunken); + + report ('s', !opts.reportall && !(subsumed + strengthened)); + + STOP_SIMPLIFIER (subsume, SUBSUME); + + return old_marked_candidate_variables_for_elimination < stats.mark.elim; +} + +/*------------------------------------------------------------------------*/ + +void Internal::subsume () { + + if (!stats.current.redundant && !stats.current.irredundant) + return; + + if (unsat) + return; + + backtrack (); + if (!propagate ()) { + learn_empty_clause (); + return; + } + + stats.subsumephases++; + + if (external_prop) { + assert (!level); + private_steps = true; + } + + if (opts.subsume) { + reset_watches (); + subsume_round (); + init_watches (); + connect_watches (); + if (!unsat && !propagate ()) { + LOG ("propagation after subsume rounds results in inconsistency"); + learn_empty_clause (); + } + } + + transred (); + if (external_prop) { + assert (!level); + private_steps = false; + } +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/sweep.cpp b/src/sat/cadical/sweep.cpp new file mode 100644 index 000000000..508315736 --- /dev/null +++ b/src/sat/cadical/sweep.cpp @@ -0,0 +1,1953 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +Sweeper::Sweeper (Internal *i) + : internal (i), random (internal->opts.seed) { + random += internal->stats.sweep; // different seed every time + internal->init_sweeper (*this); +} + +Sweeper::~Sweeper () { + // this is already called actively + // internal->release_sweeper (this); + return; +} + +#define INVALID64 INT64_MAX +#define INVALID UINT_MAX + +int Internal::sweep_solve () { + START (sweepsolve); + kitten_randomize_phases (citten); + stats.sweep_solved++; + int res = kitten_solve (citten); + if (res == 10) + stats.sweep_sat++; + if (res == 20) + stats.sweep_unsat++; + STOP (sweepsolve); + return res; +} + +bool Internal::sweep_flip (int lit) { + START (sweepflip); + bool res = kitten_flip_signed_literal (citten, lit); + STOP (sweepflip); + return res; +} + +int Internal::sweep_flip_and_implicant (int lit) { + START (sweepimplicant); + int res = kitten_flip_and_implicant_for_signed_literal (citten, lit); + STOP (sweepimplicant); + return res; +} + +void Internal::sweep_set_kitten_ticks_limit (Sweeper &sweeper) { + uint64_t remaining = 0; + const uint64_t current = sweeper.current_ticks; + if (current < sweeper.limit.ticks) + remaining = sweeper.limit.ticks - current; + LOG ("'kitten_ticks' remaining %" PRIu64, remaining); + kitten_set_ticks_limit (citten, remaining); +} + +void Internal::sweep_update_noccs (Clause *c) { + if (c->redundant) + return; + for (const auto &lit : *c) { + noccs (lit)--; + } +} + +bool Internal::can_sweep_clause (Clause *c) { + if (c->garbage) + return false; + if (!c->redundant) + return true; + return c->size == 2; // && !c->hyper; // could ignore hyper +} + +// essentially do full occurence list as in elim.cpp +void Internal::sweep_dense_mode_and_watch_irredundant () { + reset_watches (); + + init_noccs (); + + // mark satisfied irredundant clauses as garbage + for (const auto &c : clauses) { + if (!can_sweep_clause (c)) + continue; + bool satisfied = false; + for (const auto &lit : *c) { + const signed char tmp = val (lit); + if (tmp <= 0) + continue; + if (tmp > 0) { + satisfied = true; + break; + } + } + if (satisfied) + mark_garbage (c); // forces more precise counts + else { + for (const auto &lit : *c) + noccs (lit)++; + } + } + + init_occs (); + + // Connect irredundant clauses. + // + for (const auto &c : clauses) { + if (!c->garbage) { + for (const auto &lit : *c) + if (active (lit)) + occs (lit).push_back (c); + } else if (c->size == 2) { + if (!c->flushed) { + if (proof) { + c->flushed = true; + proof->delete_clause (c); + } + } + } + } +} + +// go back to two watch scheme +void Internal::sweep_sparse_mode () { + reset_occs (); + reset_noccs (); + init_watches (); + connect_watches (); +} + +// propagate without watches but full occurence list +void Internal::sweep_dense_propagate (Sweeper &sweeper) { + vector<int> &work = sweeper.propagate; + size_t i = 0; + uint64_t &ticks = sweeper.current_ticks; + while (i < work.size ()) { + int lit = work[i++]; + LOG ("sweeping propagation of %d", lit); + assert (val (lit) > 0); + ticks += 1 + cache_lines (occs (-lit).size (), sizeof (Clause *)); + const Occs &ns = occs (-lit); + for (const auto &c : ns) { + ticks++; + if (!can_sweep_clause (c)) + continue; + int unit = 0, satisfied = 0; + for (const auto &other : *c) { + const signed char tmp = val (other); + if (tmp < 0) + continue; + if (tmp > 0) { + satisfied = other; + break; + } + if (unit) + unit = INT_MIN; + else + unit = other; + } + if (satisfied) { + LOG (c, "sweeping propagation of %d finds %d satisfied", lit, + satisfied); + mark_garbage (c); + sweep_update_noccs (c); + } else if (!unit) { + LOG ("empty clause during sweeping propagation of %d", lit); + // need to set conflict = c for lrat + conflict = c; + learn_empty_clause (); + conflict = 0; + break; + } else if (unit != INT_MIN) { + LOG ("new unit %d during sweeping propagation of %d", unit, lit); + build_chain_for_units (unit, c, 0); + assign_unit (unit); + work.push_back (unit); + ticks++; + } + } + if (unsat) + break; + + // not necessary but should help + ticks += 1 + cache_lines (occs (lit).size (), sizeof (Clause *)); + const Occs &ps = occs (lit); + for (const auto &c : ps) { + ticks++; + if (c->garbage) + continue; + // if (c->redundant) // TODO I assume it does not hurt to mark + // everything here continue; + LOG (c, "sweeping propagation of %d produces satisfied", lit); + mark_garbage (c); + sweep_update_noccs (c); + } + } + work.clear (); +} + +bool Internal::kitten_ticks_limit_hit (Sweeper &sweeper, const char *when) { + const uint64_t current = + kitten_current_ticks (citten) + sweeper.current_ticks; + if (current >= sweeper.limit.ticks) { + LOG ("'kitten_ticks' limit of %" PRIu64 " ticks hit after %" PRIu64 + " ticks during %s", + sweeper.limit.ticks, current, when); + return true; + } +#ifndef LOGGING + (void) when; +#endif + return false; +} + +void Internal::init_sweeper (Sweeper &sweeper) { + sweeper.encoded = 0; + enlarge_zero (sweeper.depths, max_var + 1); + sweeper.reprs = new int[2 * max_var + 1]; + sweeper.reprs += max_var; + enlarge_zero (sweeper.prev, max_var + 1); + enlarge_zero (sweeper.next, max_var + 1); + for (const auto &lit : lits) + sweeper.reprs[lit] = lit; + sweeper.first = sweeper.last = 0; + sweeper.current_ticks = + 2 * + clauses + .size (); // initialize with the cost of building full occ list. + sweeper.current_ticks += + 2 + 2 * cache_lines (clauses.size (), sizeof (Clause *)); + assert (!citten); + citten = kitten_init (); + citten_clear_track_log_terminate (); + + sweep_dense_mode_and_watch_irredundant (); // full occurence list + + if (lrat) { + sweeper.prev_units.push_back (0); + for (const auto &idx : vars) { + sweeper.prev_units.push_back (val (idx) != 0); + } + } + + unsigned completed = stats.sweep_completed; + const unsigned max_completed = 32; + if (completed > max_completed) + completed = max_completed; + + uint64_t vars_limit = opts.sweepvars; + vars_limit <<= completed; + const unsigned max_vars_limit = opts.sweepmaxvars; + if (vars_limit > max_vars_limit) + vars_limit = max_vars_limit; + sweeper.limit.vars = vars_limit; + VERBOSE (3, "sweeper variable limit %u", sweeper.limit.vars); + + uint64_t depth_limit = stats.sweep_completed; + depth_limit += opts.sweepdepth; + const unsigned max_depth = opts.sweepmaxdepth; + if (depth_limit > max_depth) + depth_limit = max_depth; + sweeper.limit.depth = depth_limit; + VERBOSE (3, "sweeper depth limit %u", sweeper.limit.depth); + + uint64_t clause_limit = opts.sweepclauses; + clause_limit <<= completed; + const unsigned max_clause_limit = opts.sweepmaxclauses; + if (clause_limit > max_clause_limit) + clause_limit = max_clause_limit; + sweeper.limit.clauses = clause_limit; + VERBOSE (3, "sweeper clause limit %u", sweeper.limit.clauses); +} + +void Internal::release_sweeper (Sweeper &sweeper) { + + sweeper.reprs -= max_var; + delete[] sweeper.reprs; + + erase_vector (sweeper.depths); + erase_vector (sweeper.prev); + erase_vector (sweeper.next); + erase_vector (sweeper.vars); + erase_vector (sweeper.clause); + erase_vector (sweeper.backbone); + erase_vector (sweeper.partition); + erase_vector (sweeper.prev_units); + for (unsigned i = 0; i < 2; i++) + erase_vector (sweeper.core[i]); + + kitten_release (citten); + citten = 0; + stats.ticks.sweep += sweeper.current_ticks; + sweep_sparse_mode (); + return; +} + +void Internal::clear_sweeper (Sweeper &sweeper) { + LOG ("clearing sweeping environment"); + sweeper.current_ticks += kitten_current_ticks (citten); + + citten_clear_track_log_terminate (); + for (auto &idx : sweeper.vars) { + assert (sweeper.depths[idx]); + sweeper.depths[idx] = 0; + } + sweeper.vars.clear (); + for (auto c : sweeper.clauses) { + assert (c->swept); + c->swept = false; + } + sweeper.clauses.clear (); + sweeper.backbone.clear (); + sweeper.partition.clear (); + sweeper.encoded = 0; + sweep_set_kitten_ticks_limit (sweeper); +} + +int Internal::sweep_repr (Sweeper &sweeper, int lit) { + int res; + { + int prev = lit; + while ((res = sweeper.reprs[prev]) != prev) + prev = res; + } + if (res == lit) + return res; + LOG ("sweeping repr[%d] = %d", lit, res); + { + const int not_res = -res; + int next, prev = lit; + while ((next = sweeper.reprs[prev]) != res) { + const int not_prev = -prev; + sweeper.reprs[not_prev] = not_res; + sweeper.reprs[prev] = res; + prev = next; + } + assert (sweeper.reprs[-prev] == not_res); + } + return res; +} + +void Internal::add_literal_to_environment (Sweeper &sweeper, unsigned depth, + int lit) { + const int repr = sweep_repr (sweeper, lit); + if (repr != lit) + return; + const int idx = abs (lit); + if (sweeper.depths[idx]) + return; + assert (depth < UINT_MAX); + sweeper.depths[idx] = depth + 1; + assert (idx); + sweeper.vars.push_back (idx); + LOG ("sweeping[%u] adding literal %d", depth, lit); +} + +void Internal::sweep_add_clause (Sweeper &sweeper, unsigned depth) { + // TODO: assertion fails, check if this an issue or can be avoided + // assert (sweeper.clause.size () > 1); + for (const auto &lit : sweeper.clause) + add_literal_to_environment (sweeper, depth, lit); + citten_clause_with_id (citten, sweeper.clauses.size (), + sweeper.clause.size (), sweeper.clause.data ()); + sweeper.clause.clear (); + if (opts.sweepcountbinary || sweeper.clause.size () > 2) + sweeper.encoded++; +} + +void Internal::sweep_clause (Sweeper &sweeper, unsigned depth, Clause *c) { + if (c->swept) + return; + assert (can_sweep_clause (c)); + LOG (c, "sweeping[%u]", depth); + assert (sweeper.clause.empty ()); + for (const auto &lit : *c) { + const signed char tmp = val (lit); + if (tmp > 0) { + mark_garbage (c); + sweep_update_noccs (c); + sweeper.clause.clear (); + return; + } + if (tmp < 0) { + if (lrat) + sweeper.prev_units[abs (lit)] = true; + continue; + } + sweeper.clause.push_back (lit); + } + c->swept = true; + sweep_add_clause (sweeper, depth); + sweeper.clauses.push_back (c); +} + +extern "C" { +static void save_core_clause (void *state, unsigned id, bool learned, + size_t size, const unsigned *lits) { + Sweeper *sweeper = (Sweeper *) state; + Internal *internal = sweeper->internal; + if (internal->unsat) + return; + vector<sweep_proof_clause> &core = sweeper->core[sweeper->save]; + sweep_proof_clause pc; + if (learned) { + pc.sweep_id = INVALID; // necessary + pc.cad_id = INVALID64; // delay giving ids + } else { + pc.sweep_id = id; // necessary + assert (id < sweeper->clauses.size ()); + pc.cad_id = sweeper->clauses[id]->id; + } + pc.kit_id = 0; + pc.learned = learned; + const unsigned *end = lits + size; + for (const unsigned *p = lits; p != end; p++) { + pc.literals.push_back (internal->citten2lit (*p)); // conversion + } +#ifdef LOGGING + LOG (pc.literals, "traced %s", + pc.learned == true ? "learned" : "original"); +#endif + core.push_back (pc); +} + +static void save_core_clause_with_lrat (void *state, unsigned cid, + unsigned id, bool learned, + size_t size, const unsigned *lits, + size_t chain_size, + const unsigned *chain) { + Sweeper *sweeper = (Sweeper *) state; + Internal *internal = sweeper->internal; + if (internal->unsat) + return; + vector<sweep_proof_clause> &core = sweeper->core[sweeper->save]; + vector<Clause *> &clauses = sweeper->clauses; + sweep_proof_clause pc; + pc.kit_id = cid; + pc.learned = learned; + pc.sweep_id = INVALID; + pc.cad_id = INVALID64; + if (!learned) { + assert (size); + assert (!chain_size); + assert (id < clauses.size ()); + pc.sweep_id = id; + assert (id < clauses.size ()); + pc.cad_id = clauses[id]->id; + for (const auto &lit : *clauses[id]) { + pc.literals.push_back (lit); + } + } else { + assert (chain_size); + pc.sweep_id = INVALID; + pc.cad_id = INVALID64; // delay giving ids + const unsigned *end = lits + size; + for (const unsigned *p = lits; p != end; p++) { + pc.literals.push_back (internal->citten2lit (*p)); // conversion + } + for (const unsigned *p = chain + chain_size; p != chain; p--) { + pc.chain.push_back (*(p - 1)); + } + } +#ifdef LOGGING + if (learned) + LOG (pc.literals, "traced %s", + pc.learned == true ? "learned" : "original"); + else { + assert (id < clauses.size ()); + LOG (clauses[id], "traced"); + } +#endif + core.push_back (pc); +} + +static int citten_terminate (void *data) { + return ((Internal *) data)->terminated_asynchronously (); +} + +} // end extern C + +void Internal::citten_clear_track_log_terminate () { + assert (citten); + kitten_clear (citten); + kitten_track_antecedents (citten); + if (external->terminator) + kitten_set_terminator (citten, internal, citten_terminate); +#ifdef LOGGING + if (opts.log) + kitten_set_logging (citten); +#endif +} + +void Internal::add_core (Sweeper &sweeper, unsigned core_idx) { + if (unsat) + return; + LOG ("check and add extracted core[%u] lemmas to proof", core_idx); + assert (core_idx == 0 || core_idx == 1); + vector<sweep_proof_clause> &core = sweeper.core[core_idx]; + + assert (!lrat || proof); + + unsigned unsat_size = 0; + for (auto &pc : core) { + unsat_size++; + + if (!pc.learned) { + LOG (pc.literals, + "not adding already present core[%u] kitten[%u] clause", + core_idx, pc.kit_id); + continue; + } + + LOG (pc.literals, "adding extracted core[%u] kitten[%u] lemma", + core_idx, pc.kit_id); + + const unsigned new_size = pc.literals.size (); + + if (lrat) { + assert (pc.cad_id == INVALID64); + for (auto &cid : pc.chain) { + int64_t id = 0; + for (const auto &cpc : core) { + if (cpc.kit_id == cid) { + if (cpc.learned) + id = cpc.cad_id; + else { + id = cpc.cad_id; + assert (cpc.cad_id == sweeper.clauses[cpc.sweep_id]->id); + assert (!sweeper.clauses[cpc.sweep_id]->garbage); + // avoid duplicate ids of units with seen flags + for (const auto &lit : cpc.literals) { + if (val (lit) >= 0) + continue; + if (flags (lit).seen) + continue; + const int idx = abs (lit); + if (sweeper.prev_units[idx]) { + int64_t uid = unit_id (-lit); + lrat_chain.push_back (uid); + analyzed.push_back (lit); + flags (lit).seen = true; + } + } + } + break; + } + } + assert (id); + if (id != INVALID64) + lrat_chain.push_back (id); + } + clear_analyzed_literals (); + } + + if (!new_size) { + LOG ("sweeping produced empty clause"); + learn_empty_clause (); + core.resize (unsat_size); + return; + } + + if (new_size == 1) { + int unit = pc.literals[0]; + if (val (unit) > 0) { + LOG ("already assigned sweeping unit %d", unit); + lrat_chain.clear (); + } else if (val (unit) < 0) { + LOG ("falsified sweeping unit %d leads to empty clause", unit); + if (lrat) { + int64_t id = unit_id (-unit); + lrat_chain.push_back (id); + } + learn_empty_clause (); + core.resize (unsat_size); + return; + } else { + LOG ("sweeping produced unit %d", unit); + assign_unit (unit); + stats.sweep_units++; + sweeper.propagate.push_back (unit); + } + if (proof && lrat) + pc.cad_id = unit_id (unit); + continue; + } + + assert (new_size > 1); + assert (pc.learned); + + if (proof) { + pc.cad_id = ++clause_id; + proof->add_derived_clause (pc.cad_id, true, pc.literals, lrat_chain); + lrat_chain.clear (); + } + } +} + +void Internal::save_core (Sweeper &sweeper, unsigned core) { + LOG ("saving extracted core[%u] lemmas", core); + assert (core == 0 || core == 1); + assert (sweeper.core[core].empty ()); + sweeper.save = core; + kitten_compute_clausal_core (citten, 0); + if (lrat) + kitten_trace_core (citten, &sweeper, save_core_clause_with_lrat); + else + kitten_traverse_core_clauses_with_id (citten, &sweeper, + save_core_clause); +} + +void Internal::clear_core (Sweeper &sweeper, unsigned core_idx) { + assert (core_idx == 0 || core_idx == 1); + LOG ("clearing core[%u] lemmas", core_idx); + vector<sweep_proof_clause> &core = sweeper.core[core_idx]; + if (proof) { + LOG ("deleting sub-solver core clauses"); + for (auto &pc : core) { + if (pc.learned && pc.literals.size () > 1) + proof->delete_clause (pc.cad_id, true, pc.literals); + } + } + core.clear (); +} + +void Internal::save_add_clear_core (Sweeper &sweeper) { + save_core (sweeper, 0); + add_core (sweeper, 0); + clear_core (sweeper, 0); +} + +void Internal::init_backbone_and_partition (Sweeper &sweeper) { + LOG ("initializing backbone and equivalent literals candidates"); + sweeper.backbone.clear (); + sweeper.partition.clear (); + for (const auto &idx : sweeper.vars) { + if (!active (idx)) + continue; + assert (idx > 0); + const int lit = idx; + const int not_lit = -lit; + const signed char tmp = kitten_signed_value (citten, lit); + const int candidate = (tmp < 0) ? not_lit : lit; + LOG ("sweeping candidate %d", candidate); + sweeper.backbone.push_back (candidate); + sweeper.partition.push_back (candidate); + } + sweeper.partition.push_back (0); + + LOG (sweeper.backbone, "initialized backbone candidates"); + LOG (sweeper.partition, "initialized equivalence candidates"); +} + +void Internal::sweep_empty_clause (Sweeper &sweeper) { + assert (!unsat); + save_add_clear_core (sweeper); + assert (unsat); +} + +void Internal::sweep_refine_partition (Sweeper &sweeper) { + LOG ("refining partition"); + vector<int> &old_partition = sweeper.partition; + vector<int> new_partition; + auto old_begin = old_partition.begin (); + const auto old_end = old_partition.end (); +#ifdef LOGGING + unsigned old_classes = 0; + unsigned new_classes = 0; +#endif + for (auto p = old_begin, q = p; p != old_end; p = q + 1) { + unsigned assigned_true = 0; + int other; + for (q = p; (other = *q) != 0; q++) { + if (sweep_repr (sweeper, other) != other) + continue; + if (val (other)) + continue; + signed char value = kitten_signed_value (citten, other); + if (!value) + LOG ("dropping sub-solver unassigned %d", other); + else if (value > 0) { + new_partition.push_back (other); + assigned_true++; + } + } +#ifdef LOGGING + LOG ("refining class %u of size %zu", old_classes, (size_t) (q - p)); + old_classes++; +#endif + if (assigned_true == 0) + LOG ("no positive literal in class"); + else if (assigned_true == 1) { +#ifdef LOGGING + other = +#else + (void) +#endif + new_partition.back (); + new_partition.pop_back (); + LOG ("dropping singleton class %d", other); + } else { + LOG ("%u positive literal in class", assigned_true); + new_partition.push_back (0); +#ifdef LOGGING + new_classes++; +#endif + } + + unsigned assigned_false = 0; + for (q = p; (other = *q) != 0; q++) { + if (sweep_repr (sweeper, other) != other) + continue; + if (val (other)) + continue; + signed char value = kitten_signed_value (citten, other); + if (value < 0) { + new_partition.push_back (other); + assigned_false++; + } + } + + if (assigned_false == 0) + LOG ("no negative literal in class"); + else if (assigned_false == 1) { +#ifdef LOGGING + other = +#else + (void) +#endif + new_partition.back (); + new_partition.pop_back (); + LOG ("dropping singleton class %d", other); + } else { + LOG ("%u negative literal in class", assigned_false); + new_partition.push_back (0); +#ifdef LOGGING + new_classes++; +#endif + } + } + old_partition.swap (new_partition); + LOG ("refined %u classes into %u", old_classes, new_classes); +} + +void Internal::sweep_refine_backbone (Sweeper &sweeper) { + LOG ("refining backbone candidates"); + const auto end = sweeper.backbone.end (); + auto q = sweeper.backbone.begin (); + for (auto p = q; p != end; p++) { + const int lit = *p; + if (val (lit)) + continue; + signed char value = kitten_signed_value (citten, lit); + if (!value) + LOG ("dropping sub-solver unassigned %d", lit); + else if (value > 0) + *q++ = lit; + } + sweeper.backbone.resize (q - sweeper.backbone.begin ()); +} + +void Internal::sweep_refine (Sweeper &sweeper) { + assert (kitten_status (citten) == 10); + if (sweeper.backbone.empty ()) + LOG ("no need to refine empty backbone candidates"); + else + sweep_refine_backbone (sweeper); + if (sweeper.partition.empty ()) + LOG ("no need to refine empty partition candidates"); + else + sweep_refine_partition (sweeper); +} + +void Internal::flip_backbone_literals (Sweeper &sweeper) { + const unsigned max_rounds = opts.sweepfliprounds; + if (!max_rounds) + return; + assert (sweeper.backbone.size ()); + if (kitten_status (citten) != 10) + return; +#ifdef LOGGING + unsigned total_flipped = 0; +#endif + unsigned flipped, round = 0; + do { + round++; + flipped = 0; + bool refine = false; + auto begin = sweeper.backbone.begin (), q = begin, p = q; + const auto end = sweeper.backbone.end (); + bool limit_hit = false; + while (p != end) { + const int lit = *p++; + stats.sweep_flip_backbone++; + if (limit_hit || terminated_asynchronously ()) { + break; + } else if (sweep_flip (lit)) { + LOG ("flipping backbone candidate %d succeeded", lit); +#ifdef LOGGING + total_flipped++; +#endif + stats.sweep_flipped_backbone++; + flipped++; + } else { + LOG ("flipping backbone candidate %d failed", lit); + *q++ = lit; + } + } + while (p != end) + *q++ = *p++; + sweeper.backbone.resize (q - sweeper.backbone.begin ()); + LOG ("flipped %u backbone candidates in round %u", flipped, round); + + if (limit_hit) + break; + if (terminated_asynchronously ()) + break; + if (kitten_ticks_limit_hit (sweeper, "backbone flipping")) + break; + if (refine) + sweep_refine (sweeper); + } while (flipped && round < max_rounds); + LOG ("flipped %u backbone candidates in total in %u rounds", + total_flipped, round); +} + +bool Internal::sweep_extract_fixed (Sweeper &sweeper, int lit) { + const int not_lit = -lit; + stats.sweep_solved_backbone++; + kitten_assume_signed (citten, not_lit); + int res = sweep_solve (); + if (!res) { + stats.sweep_unknown_backbone++; + return false; + } + assert (res == 20); + LOG ("sweep unit %d", lit); + save_add_clear_core (sweeper); + stats.sweep_unsat_backbone++; + return true; +} + +bool Internal::sweep_backbone_candidate (Sweeper &sweeper, int lit) { + LOG ("trying backbone candidate %d", lit); + signed char value = kitten_fixed_signed (citten, lit); + if (value) { + stats.sweep_fixed_backbone++; + assert (value > 0); + if (val (lit) <= 0) { + return sweep_extract_fixed (sweeper, lit); + } else + LOG ("literal %d already fixed", lit); + return false; + } + + int res = kitten_status (citten); + if (res != 10) { + LOG ("not flipping due to status %d != 10", res); + } + stats.sweep_flip_backbone++; + if (res == 10 && sweep_flip (lit)) { + stats.sweep_flipped_backbone++; + LOG ("flipping %d succeeded", lit); + // LOGBACKBONE ("refined backbone candidates"); + return false; + } + + LOG ("flipping %d failed", lit); + const int not_lit = -lit; + stats.sweep_solved_backbone++; + kitten_assume_signed (citten, not_lit); + res = sweep_solve (); + if (res == 10) { + LOG ("sweeping backbone candidate %d failed", lit); + sweep_refine (sweeper); + stats.sweep_sat_backbone++; + return false; + } + + if (res == 20) { + LOG ("sweep unit %d", lit); + save_add_clear_core (sweeper); + assert (val (lit)); + stats.sweep_unsat_backbone++; + return true; + } + + stats.sweep_unknown_backbone++; + + LOG ("sweeping backbone candidate %d failed", lit); + return false; +} + +// at this point the binary (lit or other) is already present +// in the proof via 'add_core'. +// We just copy it as an irredundant clause, call weaken minus +// and push it on the extension stack. +// +int64_t Internal::add_sweep_binary (sweep_proof_clause pc, int lit, + int other) { + assert (!unsat); + if (unsat) + return 0; // sanity check, should be fuzzed + + assert (!val (lit) && !val (other)); + if (val (lit) || val (other)) + return 0; + + if (lrat) { + for (const auto &plit : pc.literals) { + if (val (plit)) { + int64_t id = unit_id (-plit); + lrat_chain.push_back (id); + } + } + lrat_chain.push_back (pc.cad_id); + } + clause.push_back (lit); + clause.push_back (other); + const int64_t id = ++clause_id; + if (proof) { + proof->add_derived_clause (id, false, clause, lrat_chain); + proof->weaken_minus (id, clause); + } + external->push_binary_clause_on_extension_stack (id, lit, other); + clause.clear (); + lrat_chain.clear (); + return id; +} + +void Internal::delete_sweep_binary (const sweep_binary &sb) { + if (unsat) + return; + if (!proof) + return; + vector<int> bin; + bin.push_back (sb.lit); + bin.push_back (sb.other); + proof->delete_clause (sb.id, false, bin); +} + +bool Internal::scheduled_variable (Sweeper &sweeper, int idx) { + return sweeper.prev[idx] != 0 || sweeper.first == idx; +} + +void Internal::schedule_inner (Sweeper &sweeper, int idx) { + assert (idx); + if (!active (idx)) + return; + const int next = sweeper.next[idx]; + if (next != 0) { + LOG ("rescheduling inner %d as last", idx); + const unsigned prev = sweeper.prev[idx]; + assert (sweeper.prev[next] == idx); + sweeper.prev[next] = prev; + if (prev == 0) { + assert (sweeper.first == idx); + sweeper.first = next; + } else { + assert (sweeper.next[prev] == idx); + sweeper.next[prev] = next; + } + const unsigned last = sweeper.last; + if (last == 0) { + assert (sweeper.first == 0); + sweeper.first = idx; + } else { + assert (sweeper.next[last] == 0); + sweeper.next[last] = idx; + } + sweeper.prev[idx] = last; + sweeper.next[idx] = 0; + sweeper.last = idx; + } else if (sweeper.last != idx) { + LOG ("scheduling inner %d as last", idx); + const unsigned last = sweeper.last; + if (last == 0) { + assert (sweeper.first == 0); + sweeper.first = idx; + } else { + assert (sweeper.next[last] == 0); + sweeper.next[last] = idx; + } + assert (sweeper.next[idx] == 0); + sweeper.prev[idx] = last; + sweeper.last = idx; + } else + LOG ("keeping inner %d scheduled as last", idx); +} + +void Internal::schedule_outer (Sweeper &sweeper, int idx) { + assert (!scheduled_variable (sweeper, idx)); + assert (active (idx)); + const int first = sweeper.first; + if (first == 0) { + assert (sweeper.last == 0); + sweeper.last = idx; + } else { + assert (sweeper.prev[first] == 0); + sweeper.prev[first] = idx; + } + assert (sweeper.prev[idx] == 0); + sweeper.next[idx] = first; + sweeper.first = idx; + LOG ("scheduling outer %d as first", idx); +} + +int Internal::next_scheduled (Sweeper &sweeper) { + int res = sweeper.last; + if (res == 0) { + LOG ("no more scheduled variables left"); + return 0; + } + assert (res > 0); + LOG ("dequeuing next scheduled %d", res); + const unsigned prev = sweeper.prev[res]; + assert (sweeper.next[res] == 0); + sweeper.prev[res] = 0; + if (prev == 0) { + assert (sweeper.first == res); + sweeper.first = 0; + } else { + assert (sweeper.next[prev] == res); + sweeper.next[prev] = 0; + } + sweeper.last = prev; + return res; +} + +void Internal::sweep_substitute_lrat (Clause *c, int64_t id) { + if (!lrat) + return; + for (const auto &lit : *c) { + assert (val (lit) <= 0); + if (val (lit) < 0) { + int64_t id = unit_id (-lit); + lrat_chain.push_back (id); + } + } + lrat_chain.push_back (id); + lrat_chain.push_back (c->id); +} + +#define all_scheduled(IDX) \ + int IDX = sweeper.first, NEXT_##IDX; \ + IDX != 0 && (NEXT_##IDX = sweeper.next[IDX], true); \ + IDX = NEXT_##IDX + +// Substitute equivalences in clauses (see +// 'sweep_substitute_new_equivalences' for explanation) +void Internal::substitute_connected_clauses (Sweeper &sweeper, int lit, + int repr, int64_t id) { + if (unsat) + return; + if (val (lit)) + return; + if (val (repr)) + return; + LOG ("substituting %d with %d in all irredundant clauses", lit, repr); + + assert (lit != repr); + assert (lit != -repr); + + assert (active (lit)); + assert (active (repr)); + + uint64_t &ticks = sweeper.current_ticks; + + { + ticks += 1 + cache_lines (occs (lit).size (), sizeof (Clause *)); + Occs &ns = occs (lit); + auto const begin = ns.begin (); + const auto end = ns.end (); + auto q = begin; + auto p = q; + while (p != end) { + Clause *c = *q++ = *p++; + ticks++; + if (c->garbage) + continue; + assert (clause.empty ()); + bool satisfied = false; + bool repr_already_watched = false; + const int not_repr = -repr; +#ifndef NDEBUG + bool found = false; +#endif + for (const auto &other : *c) { + if (other == lit) { +#ifndef NDEBUG + assert (!found); + found = true; +#endif + clause.push_back (repr); + continue; + } + assert (other != -lit); + if (other == repr) { + assert (!repr_already_watched); + repr_already_watched = true; + continue; + } + if (other == not_repr) { + satisfied = true; + break; + } + const signed char tmp = val (other); + if (tmp < 0) + continue; + if (tmp > 0) { + satisfied = true; + break; + } + clause.push_back (other); + } + if (satisfied) { + clause.clear (); + mark_garbage (c); + sweep_update_noccs (c); + continue; + } + assert (found); + const unsigned new_size = clause.size (); + sweep_substitute_lrat (c, id); + if (new_size == 0) { + LOG (c, "substituted empty clause"); + assert (!unsat); + learn_empty_clause (); + break; + } + ticks++; + if (new_size == 1) { + LOG (c, "reduces to unit"); + const int unit = clause[0]; + clause.clear (); + assign_unit (unit); + sweeper.propagate.push_back (unit); + mark_garbage (c); + sweep_update_noccs (c); + stats.sweep_units++; + break; + } + assert (c->size >= 2); + if (!c->redundant) + mark_removed (c); + uint64_t new_id = ++clause_id; + if (proof) { + proof->add_derived_clause (new_id, c->redundant, clause, + lrat_chain); + proof->delete_clause (c); + } + c->id = new_id; + lrat_chain.clear (); + size_t l; + int *literals = c->literals; + for (l = 0; l < clause.size (); l++) + literals[l] = clause[l]; + int flushed = c->size - (int) l; + if (flushed) { + LOG ("flushed %d literals", flushed); + (void) shrink_clause (c, l); + } else if (likely_to_be_kept_clause (c)) + mark_added (c); + LOG (c, "substituted"); + if (!repr_already_watched) { + occs (repr).push_back (c); + noccs (repr)++; + } + clause.clear (); + q--; + } + while (p != end) + *q++ = *p++; + ns.resize (q - ns.begin ()); + } +} + +// In contrast to kissat we substitute the equivalences explicitely after +// every successful round of sweeping. This is necessary in order to extract +// valid LRAT proofs for subsequent rounds of sweeping. +void Internal::sweep_substitute_new_equivalences (Sweeper &sweeper) { + if (unsat) + return; + + unsigned count = 0; + assert (lrat_chain.empty ()); + + for (const auto &sb : sweeper.binaries) { + count++; + const auto lit = sb.lit; + const auto other = sb.other; + if (abs (lit) < abs (other)) { + substitute_connected_clauses (sweeper, -other, lit, sb.id); + } else { + substitute_connected_clauses (sweeper, -lit, other, sb.id); + } + assert (lrat_chain.empty ()); + if (val (lit) < 0) { + if (lrat) { + const int64_t lid = unit_id (-lit); + lrat_chain.push_back (lid); + } + if (!val (other)) { + if (lrat) + lrat_chain.push_back (sb.id); + assign_unit (other); + } else if (val (other) < 0) { + if (lrat) { + const int64_t oid = unit_id (-other); + lrat_chain.push_back (oid); + lrat_chain.push_back (sb.id); + } + learn_empty_clause (); + return; + } + } else if (val (other) < 0) { + if (!val (lit)) { + if (lrat) { + const int64_t oid = unit_id (-other); + lrat_chain.push_back (oid); + lrat_chain.push_back (sb.id); + } + assign_unit (lit); + } else + assert (val (lit) > 0); + } + lrat_chain.clear (); + delete_sweep_binary (sb); + if (count == 2) { + if (!val (lit) && !val (other)) { + const auto idx = abs (lit) < abs (other) ? abs (other) : abs (lit); + if (!flags (idx).fixed ()) + mark_substituted (idx); + } + count = 0; + } + assert (lrat_chain.empty ()); + } + sweeper.binaries.clear (); +} + +void Internal::sweep_remove (Sweeper &sweeper, int lit) { + assert (sweeper.reprs[lit] != lit); + vector<int> &partition = sweeper.partition; + const auto begin_partition = partition.begin (); + auto p = begin_partition; + const auto end_partition = partition.end (); + for (; *p != lit; p++) + assert (p + 1 != end_partition); + auto begin_class = p; + while (begin_class != begin_partition && begin_class[-1] != 0) + begin_class--; + auto end_class = p; + while (*end_class != 0) + end_class++; + const unsigned size = end_class - begin_class; + LOG ("removing non-representative %d from equivalence class of size %u", + lit, size); + assert (size > 1); + auto q = begin_class; + if (size == 2) { + LOG ("completely squashing equivalence class of %d", lit); + for (auto r = end_class + 1; r != end_partition; r++) + *q++ = *r; + } else { + for (auto r = begin_class; r != end_partition; r++) + if (r != p) + *q++ = *r; + } + partition.resize (q - partition.begin ()); +} + +void Internal::flip_partition_literals (Sweeper &sweeper) { + const unsigned max_rounds = opts.sweepfliprounds; + if (!max_rounds) + return; + assert (sweeper.partition.size ()); + if (kitten_status (citten) != 10) + return; +#ifdef LOGGING + unsigned total_flipped = 0; +#endif + unsigned flipped, round = 0; + do { + round++; + flipped = 0; + bool refine = false; + bool limit_hit = false; + auto begin = sweeper.partition.begin (), dst = begin, src = dst; + const auto end = sweeper.partition.end (); + while (src != end) { + auto end_src = src; + while (assert (end_src != end), *end_src != 0) + end_src++; + unsigned size = end_src - src; + assert (size > 1); + auto q = dst; + for (auto p = src; p != end_src; p++) { + const int lit = *p; + if (limit_hit) { + *q++ = lit; + continue; + } else if (kitten_ticks_limit_hit (sweeper, "partition flipping")) { + *q++ = lit; + limit_hit = true; + continue; + } else if (sweep_flip (lit)) { + LOG ("flipping equivalence candidate %d succeeded", lit); +#ifdef LOGGING + total_flipped++; +#endif + flipped++; + if (--size < 2) + break; + } else { + LOG ("flipping equivalence candidate %d failed", lit); + *q++ = lit; + } + stats.sweep_flip_equivalences++; + } + if (size > 1) { + *q++ = 0; + dst = q; + } + src = end_src + 1; + } + stats.sweep_flipped_equivalences += flipped; + sweeper.partition.resize (dst - sweeper.partition.begin ()); + LOG ("flipped %u equivalence candidates in round %u", flipped, round); + if (terminated_asynchronously ()) + break; + if (kitten_ticks_limit_hit (sweeper, "partition flipping")) + break; + if (refine) + sweep_refine (sweeper); + } while (flipped && round < max_rounds); + LOG ("flipped %u equivalence candidates in total in %u rounds", + total_flipped, round); +} + +bool Internal::sweep_equivalence_candidates (Sweeper &sweeper, int lit, + int other) { + LOG ("trying equivalence candidates %d = %d", lit, other); + const auto begin = sweeper.partition.begin (); + auto const end = sweeper.partition.end (); + assert (begin + 3 <= end); + assert (end[-3] == lit); + assert (end[-2] == other); + const int third = (end - begin == 3) ? 0 : end[-4]; + int res = kitten_status (citten); + if (res == 10) { + stats.sweep_flip_equivalences++; + if (sweep_flip (lit)) { + stats.sweep_flipped_equivalences++; + LOG ("flipping %d succeeded", lit); + if (third == 0) { + LOG ("squashing equivalence class of %d", lit); + sweeper.partition.resize (sweeper.partition.size () - 3); + } else { + LOG ("removing %d from equivalence class of %d", lit, other); + end[-3] = other; + end[-2] = 0; + sweeper.partition.resize (sweeper.partition.size () - 1); + } + return false; + } + stats.sweep_flip_equivalences++; + if (sweep_flip (other)) { + stats.sweep_flipped_equivalences++; + LOG ("flipping %d succeeded", other); + if (third == 0) { + LOG ("squashing equivalence class of %d", lit); + sweeper.partition.resize (sweeper.partition.size () - 3); + } else { + LOG ("removing %d from equivalence class of %d", other, lit); + end[-2] = 0; + sweeper.partition.resize (sweeper.partition.size () - 1); + } + return false; + } + } + // frozen variables are not allowed to be eliminated. + // It might still be beneficial to learn the binaries, if they + // really are equivalent, but we avoid the issue by not trying + // for equivalence at all if the non-representative is frozen. + // i.e., the higher absolute value + if (abs (lit) > abs (other) && frozen (lit)) { + if (third == 0) { + LOG ("squashing equivalence class of %d", lit); + sweeper.partition.resize (sweeper.partition.size () - 3); + } else { + LOG ("removing %d from equivalence class of %d", lit, other); + end[-3] = other; + end[-2] = 0; + sweeper.partition.resize (sweeper.partition.size () - 1); + } + return false; + } else if (abs (other) > abs (lit) && frozen (other)) { + if (third == 0) { + LOG ("squashing equivalence class of %d", lit); + sweeper.partition.resize (sweeper.partition.size () - 3); + } else { + LOG ("removing %d from equivalence class of %d", lit, other); + end[-2] = 0; + sweeper.partition.resize (sweeper.partition.size () - 1); + } + return false; + } + + const int not_other = -other; + const int not_lit = -lit; + LOG ("flipping %d and %d both failed", lit, other); + kitten_assume_signed (citten, not_lit); + kitten_assume_signed (citten, other); + stats.sweep_solved_equivalences++; + res = sweep_solve (); + if (res == 10) { + stats.sweep_sat_equivalences++; + LOG ("first sweeping implication %d -> %d failed", other, lit); + sweep_refine (sweeper); + } else if (!res) { + stats.sweep_unknown_equivalences++; + LOG ("first sweeping implication %d -> %d hit ticks limit", other, lit); + } + + if (res != 20) + return false; + + stats.sweep_unsat_equivalences++; + LOG ("first sweeping implication %d -> %d succeeded", other, lit); + + save_core (sweeper, 0); + + kitten_assume_signed (citten, lit); + kitten_assume_signed (citten, not_other); + res = sweep_solve (); + stats.sweep_solved_equivalences++; + if (res == 10) { + stats.sweep_sat_equivalences++; + LOG ("second sweeping implication %d <- %d failed", other, lit); + sweep_refine (sweeper); + } else if (!res) { + stats.sweep_unknown_equivalences++; + LOG ("second sweeping implication %d <- %d hit ticks limit", other, + lit); + } + + if (res != 20) { + sweeper.core[0].clear (); + return false; + } + + assert (res == 20); + + stats.sweep_unsat_equivalences++; + LOG ("second sweeping implication %d <- %d succeeded too", other, lit); + + save_core (sweeper, 1); + + LOG ("sweep equivalence %d = %d", lit, other); + + // If kitten behaves as expected, the two binaries of the equivalence + // should be stored at sweeper.core[i].back () for i in {0, 1}. + // We pick the smaller absolute valued literal as representative and + // store the equivalence . + add_core (sweeper, 0); + add_core (sweeper, 1); + if (!val (lit) && !val (other)) { + assert (sweeper.core[0].size ()); + assert (sweeper.core[1].size ()); + stats.sweep_equivalences++; + sweep_binary bin1; + sweep_binary bin2; + if (abs (lit) > abs (other)) { + bin1.lit = lit; + bin1.other = not_other; + bin2.lit = not_lit; + bin2.other = other; + bin1.id = add_sweep_binary (sweeper.core[0].back (), lit, not_other); + bin2.id = add_sweep_binary (sweeper.core[1].back (), not_lit, other); + } else { + bin1.lit = not_other; + bin1.other = lit; + bin2.lit = other; + bin2.other = not_lit; + bin1.id = add_sweep_binary (sweeper.core[0].back (), not_other, lit); + bin2.id = add_sweep_binary (sweeper.core[1].back (), other, not_lit); + } + if (bin1.id && bin2.id) { + sweeper.binaries.push_back (bin1); + sweeper.binaries.push_back (bin2); + } + } + + int repr; + if (abs (lit) < abs (other)) { + repr = sweeper.reprs[other] = lit; + sweeper.reprs[not_other] = not_lit; + sweep_remove (sweeper, other); + } else { + repr = sweeper.reprs[lit] = other; + sweeper.reprs[not_lit] = not_other; + sweep_remove (sweeper, lit); + } + clear_core (sweeper, 0); + clear_core (sweeper, 1); + + const int repr_idx = abs (repr); + schedule_inner (sweeper, repr_idx); + + return true; +} + +const char *Internal::sweep_variable (Sweeper &sweeper, int idx) { + assert (!unsat); + if (!active (idx)) + return "inactive variable"; + const int start = idx; + if (sweeper.reprs[start] != start) + return "non-representative variable"; + assert (sweeper.vars.empty ()); + assert (sweeper.clauses.empty ()); + assert (sweeper.backbone.empty ()); + assert (sweeper.partition.empty ()); + assert (!sweeper.encoded); + + stats.sweep_variables++; + + LOG ("sweeping %d", idx); + assert (!val (start)); + LOG ("starting sweeping[0]"); + add_literal_to_environment (sweeper, 0, start); + LOG ("finished sweeping[0]"); + LOG ("starting sweeping[1]"); + + bool limit_reached = false; + size_t expand = 0, next = 1; + bool success = false; + unsigned depth = 1; + + uint64_t &ticks = sweeper.current_ticks; + while (!limit_reached) { + if (sweeper.encoded >= sweeper.limit.clauses) { + LOG ("environment clause limit reached"); + limit_reached = true; + break; + } + if (expand == next) { + LOG ("finished sweeping[%u]", depth); + if (depth >= sweeper.limit.depth) { + LOG ("environment depth limit reached"); + break; + } + next = sweeper.vars.size (); + if (expand == next) { + LOG ("completely copied all clauses"); + break; + } + depth++; + LOG ("starting sweeping[%u]", depth); + } + const unsigned choices = next - expand; + if (opts.sweeprand && choices > 1) { + const unsigned swaps = sweeper.random.pick_int (0, choices - 1); + if (swaps) { + assert (expand + swaps < sweeper.vars.size ()); + swap (sweeper.vars[expand], sweeper.vars[expand + swaps]); + } + } + const int idx = sweeper.vars[expand]; + LOG ("traversing and adding clauses of %d", idx); + for (unsigned sign = 0; sign < 2; sign++) { + const int lit = sign ? -idx : idx; + ticks += 1 + cache_lines (occs (lit).size (), sizeof (Clause *)); + Occs &ns = occs (lit); + for (auto c : ns) { + ticks++; + if (!can_sweep_clause (c)) + continue; + sweep_clause (sweeper, depth, c); + if (sweeper.vars.size () >= sweeper.limit.vars) { + LOG ("environment variable limit reached"); + limit_reached = true; + break; + } + } + if (limit_reached) + break; + } + expand++; + } + stats.sweep_depth += depth; + stats.sweep_clauses += sweeper.encoded; + stats.sweep_environment += sweeper.vars.size (); + VERBOSE (3, + "sweeping variable %d environment of " + "%zu variables %u clauses depth %u", + externalize (idx), sweeper.vars.size (), sweeper.encoded, depth); + + int res; + if (sweeper.vars.size () == 1) { + LOG ("not sweeping literal %d with environment size 1", idx); + goto DONE; + } + res = sweep_solve (); + LOG ("sub-solver returns '%d'", res); + if (res == 10) { + init_backbone_and_partition (sweeper); +#ifndef QUIET + uint64_t units = stats.sweep_units; + uint64_t solved = stats.sweep_solved; +#endif + START (sweepbackbone); + while (sweeper.backbone.size ()) { + if (unsat || terminated_asynchronously () || + kitten_ticks_limit_hit (sweeper, "backbone refinement")) { + limit_reached = true; + STOP_SWEEP_BACKBONE: + STOP (sweepbackbone); + goto DONE; + } + flip_backbone_literals (sweeper); + if (terminated_asynchronously () || + kitten_ticks_limit_hit (sweeper, "backbone refinement")) { + limit_reached = true; + goto STOP_SWEEP_BACKBONE; + } + if (sweeper.backbone.empty ()) + break; + const int lit = sweeper.backbone.back (); + sweeper.backbone.pop_back (); + if (!active (lit)) + continue; + if (sweep_backbone_candidate (sweeper, lit)) + success = true; + } + STOP (sweepbackbone); +#ifndef QUIET + units = stats.sweep_units - units; + solved = stats.sweep_solved - solved; +#endif + VERBOSE (3, + "complete swept variable %d backbone with %" PRIu64 + " units in %" PRIu64 " solver calls", + externalize (idx), units, solved); + assert (sweeper.backbone.empty ()); +#ifndef QUIET + uint64_t equivalences = stats.sweep_equivalences; + solved = stats.sweep_solved; +#endif + START (sweepequivalences); + while (sweeper.partition.size ()) { + if (unsat || terminated_asynchronously () || + kitten_ticks_limit_hit (sweeper, "partition refinement")) { + limit_reached = true; + STOP_SWEEP_EQUIVALENCES: + STOP (sweepequivalences); + goto DONE; + } + flip_partition_literals (sweeper); + if (terminated_asynchronously () || + kitten_ticks_limit_hit (sweeper, "backbone refinement")) { + limit_reached = true; + goto STOP_SWEEP_EQUIVALENCES; + } + if (sweeper.partition.empty ()) + break; + if (sweeper.partition.size () > 2) { + const auto end = sweeper.partition.end (); + assert (end[-1] == 0); + int lit = end[-3]; + int other = end[-2]; + if (sweep_equivalence_candidates (sweeper, lit, other)) + success = true; + } else + sweeper.partition.clear (); + } + STOP (sweepequivalences); +#ifndef QUIET + equivalences = stats.sweep_equivalences - equivalences; + solved = stats.sweep_solved - solved; + if (equivalences) + VERBOSE (3, + "complete swept variable %d partition with %" PRIu64 + " equivalences in %" PRIu64 " solver calls", + externalize (idx), equivalences, solved); +#endif + } else if (res == 20) + sweep_empty_clause (sweeper); + +DONE: + clear_sweeper (sweeper); + + if (!unsat) + sweep_substitute_new_equivalences (sweeper); + if (!unsat) + sweep_dense_propagate (sweeper); + + if (success && limit_reached) + return "successfully despite reaching limit"; + if (!success && !limit_reached) + return "unsuccessfully without reaching limit"; + else if (success && !limit_reached) + return "successfully without reaching limit"; + assert (!success && limit_reached); + return "unsuccessfully and reached limit"; +} + +struct sweep_candidate { + unsigned rank; + int idx; +}; + +struct rank_sweep_candidate { + bool operator() (sweep_candidate a, sweep_candidate b) const { + assert (a.rank && b.rank); + assert (a.idx > 0 && b.idx > 0); + if (a.rank < b.rank) + return true; + if (b.rank < a.rank) + return false; + return a.idx < b.idx; + } +}; + +bool Internal::scheduable_variable (Sweeper &sweeper, int idx, + size_t *occ_ptr) { + const int lit = idx; + const size_t pos = noccs (lit); + if (!pos) + return false; + const unsigned max_occurrences = sweeper.limit.clauses; + if (pos > max_occurrences) + return false; + const int not_lit = -lit; + const size_t neg = noccs (not_lit); + if (!neg) + return false; + if (neg > max_occurrences) + return false; + *occ_ptr = pos + neg; + return true; +} + +unsigned Internal::schedule_all_other_not_scheduled_yet (Sweeper &sweeper) { + vector<sweep_candidate> fresh; + for (const auto &idx : vars) { + Flags &f = flags (idx); + if (!f.active ()) + continue; + if (sweep_incomplete && !f.sweep) + continue; + if (scheduled_variable (sweeper, idx)) + continue; + size_t occ; + if (!scheduable_variable (sweeper, idx, &occ)) { + f.sweep = false; + continue; + } + sweep_candidate cand; + cand.rank = occ; + cand.idx = idx; + fresh.push_back (cand); + } + const size_t size = fresh.size (); + assert (size <= UINT_MAX); + sort (fresh.begin (), fresh.end (), rank_sweep_candidate ()); + for (auto &cand : fresh) + schedule_outer (sweeper, cand.idx); + return size; +} + +unsigned Internal::reschedule_previously_remaining (Sweeper &sweeper) { + unsigned rescheduled = 0; + for (const auto &idx : sweep_schedule) { + Flags &f = flags (idx); + if (!f.active ()) + continue; + if (scheduled_variable (sweeper, idx)) + continue; + size_t occ; + if (!scheduable_variable (sweeper, idx, &occ)) { + f.sweep = false; + continue; + } + schedule_inner (sweeper, idx); + rescheduled++; + } + sweep_schedule.clear (); + return rescheduled; +} + +unsigned Internal::incomplete_variables () { + unsigned res = 0; + for (const auto &idx : vars) { + Flags &f = flags (idx); + if (!f.active ()) + continue; + if (f.sweep) + res++; + } + return res; +} + +void Internal::mark_incomplete (Sweeper &sweeper) { + unsigned marked = 0; + for (all_scheduled (idx)) { + if (!flags (idx).sweep) { + flags (idx).sweep = true; + marked++; + } + } + sweep_incomplete = true; +#ifndef QUIET + VERBOSE (2, "marked %u scheduled sweeping variables as incomplete", + marked); +#else + (void) marked; +#endif +} + +unsigned Internal::schedule_sweeping (Sweeper &sweeper) { + const unsigned rescheduled = reschedule_previously_remaining (sweeper); + const unsigned fresh = schedule_all_other_not_scheduled_yet (sweeper); + const unsigned scheduled = fresh + rescheduled; + const unsigned incomplete = incomplete_variables (); +#ifndef QUIET + PHASE ("sweep", stats.sweep, + "scheduled %u variables %.0f%% " + "(%u rescheduled %.0f%%, %u incomplete %.0f%%)", + scheduled, percent (scheduled, active ()), rescheduled, + percent (rescheduled, scheduled), incomplete, + percent (incomplete, scheduled)); +#endif + if (incomplete) + assert (sweep_incomplete); + else { + if (sweep_incomplete) + stats.sweep_completed++; + mark_incomplete (sweeper); + } + return scheduled; +} + +void Internal::unschedule_sweeping (Sweeper &sweeper, unsigned swept, + unsigned scheduled) { +#ifdef QUIET + (void) scheduled, (void) swept; +#endif + assert (sweep_schedule.empty ()); + assert (sweep_incomplete); + for (all_scheduled (idx)) + if (active (idx)) { + sweep_schedule.push_back (idx); + LOG ("untried scheduled %d", idx); + } +#ifndef QUIET + const unsigned retained = sweep_schedule.size (); +#endif + VERBOSE (3, "retained %u variables %.0f%% to be swept next time", + retained, percent (retained, active ())); + const unsigned incomplete = incomplete_variables (); + if (incomplete) + VERBOSE (3, "need to sweep %u more variables %.0f%% for completion", + incomplete, percent (incomplete, active ())); + else { + VERBOSE (3, "no more variables needed to complete sweep"); + sweep_incomplete = false; + stats.sweep_completed++; + } + PHASE ("sweep", stats.sweep, "swept %u variables (%u remain %.0f%%)", + swept, incomplete, percent (incomplete, scheduled)); +} + +bool Internal::sweep () { + if (!opts.sweep) + return false; + if (unsat) + return false; + if (terminated_asynchronously ()) + return false; + if (delaying_sweep.bumpreasons.delay ()) { // TODO need to fix Delay + last.sweep.ticks = stats.ticks.search[0] + stats.ticks.search[1]; + return false; + } + delaying_sweep.bumpreasons.bypass_delay (); + SET_EFFORT_LIMIT (tickslimit, sweep, !opts.sweepcomplete); + delaying_sweep.bumpreasons.unbypass_delay (); + + assert (!level); + START_SIMPLIFIER (sweep, SWEEP); + stats.sweep++; + uint64_t equivalences = stats.sweep_equivalences; + uint64_t units = stats.sweep_units; + Sweeper sweeper = Sweeper (this); + if (opts.sweepcomplete) + sweeper.limit.ticks = INT64_MAX; + else + sweeper.limit.ticks = tickslimit - stats.ticks.sweep; + sweep_set_kitten_ticks_limit (sweeper); + const unsigned scheduled = schedule_sweeping (sweeper); + uint64_t swept = 0, limit = 10; + for (;;) { + if (unsat) + break; + if (terminated_asynchronously ()) + break; + if (kitten_ticks_limit_hit (sweeper, "sweeping loop")) + break; + int idx = next_scheduled (sweeper); + if (idx == 0) + break; + flags (idx).sweep = false; +#ifndef QUIET + const char *res = +#endif + sweep_variable (sweeper, idx); + VERBOSE (2, "swept[%" PRIu64 "] external variable %d %s", swept, + externalize (idx), res); + if (++swept == limit) { + VERBOSE (2, + "found %" PRIu64 " equivalences and %" PRIu64 + " units after sweeping %" PRIu64 " variables ", + stats.sweep_equivalences - equivalences, + stats.sweep_units - units, swept); + limit *= 10; + } + } + VERBOSE (2, "swept %" PRIu64 " variables", swept); + equivalences = stats.sweep_equivalences - equivalences, + units = stats.sweep_units - units; + PHASE ("sweep", stats.sweep, + "found %" PRIu64 " equivalences and %" PRIu64 " units", + equivalences, units); + unschedule_sweeping (sweeper, swept, scheduled); + release_sweeper (sweeper); + + if (!unsat) { + propagated = 0; + if (!propagate ()) { + learn_empty_clause (); + } + } + + uint64_t eliminated = equivalences + units; + report ('=', !eliminated); + + if (relative (eliminated, swept) < 0.001) { + delaying_sweep.bumpreasons.bump_delay (); + } else { + delaying_sweep.bumpreasons.reduce_delay (); + } + STOP_SIMPLIFIER (sweep, SWEEP); + return eliminated; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/sweep.hpp b/src/sat/cadical/sweep.hpp new file mode 100644 index 000000000..04d40ead1 --- /dev/null +++ b/src/sat/cadical/sweep.hpp @@ -0,0 +1,61 @@ +#ifndef _sweep_hpp_INCLUDED +#define _sweep_hpp_INCLUDED + +namespace CaDiCaL { + +struct Internal; + +struct sweep_proof_clause { + unsigned sweep_id; // index for sweeper.clauses + int64_t cad_id; // cadical id + unsigned kit_id; // kitten id + bool learned; + vector<int> literals; + vector<unsigned> chain; // lrat +}; + +struct sweep_blocked_clause { + int blit; + int64_t id; + vector<int> literals; +}; + +struct sweep_binary { + int lit; + int other; + int64_t id; +}; + +struct Sweeper { + Sweeper (Internal *internal); + ~Sweeper (); + Internal *internal; + Random random; + vector<unsigned> depths; + int *reprs; + vector<int> next, prev; + int first, last, blit; + unsigned encoded; + unsigned save; + vector<int> vars; + vector<Clause *> clauses; + vector<sweep_blocked_clause> blocked_clauses; + bool flush_blocked_clauses; + vector<int> blockable; + vector<int> clause; + vector<int> propagate; + vector<int> backbone; + vector<int> partition; + vector<bool> prev_units; + vector<sweep_binary> binaries; + vector<sweep_proof_clause> core[2]; + uint64_t current_ticks; + struct { + uint64_t ticks; + unsigned clauses, depth, vars; + } limit; +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/terminal.cpp b/src/sat/cadical/terminal.cpp new file mode 100644 index 000000000..919d81c9a --- /dev/null +++ b/src/sat/cadical/terminal.cpp @@ -0,0 +1,38 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +Terminal::Terminal (FILE *f) : file (f), reset_on_exit (false) { + assert (file); + int fd = fileno (f); + assert (fd == 1 || fd == 2); + use_colors = connected = isatty (fd); +} + +void Terminal::force_colors () { use_colors = connected = true; } +void Terminal::force_no_colors () { use_colors = false; } +void Terminal::force_reset_on_exit () { reset_on_exit = true; } + +void Terminal::reset () { + if (!connected) + return; + erase_until_end_of_line (); + cursor (true); + normal (); + fflush (file); +} + +void Terminal::disable () { + reset (); + connected = use_colors = false; +} + +Terminal::~Terminal () { + if (reset_on_exit) + reset (); +} + +Terminal tout (stdout); +Terminal terr (stderr); + +} // namespace CaDiCaL diff --git a/src/sat/cadical/terminal.hpp b/src/sat/cadical/terminal.hpp new file mode 100644 index 000000000..71090a1d5 --- /dev/null +++ b/src/sat/cadical/terminal.hpp @@ -0,0 +1,98 @@ +#ifndef _terminal_hpp_INCLUDED +#define _terminal_hpp_INCLUDED + +namespace CaDiCaL { + +class Terminal { + + FILE *file; // 'stdout' or 'stderr' + bool connected; // Connected to terminal. + bool use_colors; // Use colors. + bool reset_on_exit; // Reset on exit. + + void escape () { + assert (connected); + fputs ("\033[", file); + } + + void color (int color, bool bright) { + if (!use_colors) + return; + assert (connected); + escape (); + fputc (bright ? '1' : '0', file); + fprintf (file, ";%dm", color); + fflush (file); + } + + void code (const char *str) { + if (!use_colors) + return; + if (!connected) + return; + escape (); + fputs (str, file); + fflush (file); + } + +public: + Terminal (FILE *file); + ~Terminal (); + + void disable (); // Assume disconnected in any case. + void force_colors (); + void force_no_colors (); + void force_reset_on_exit (); + + bool colors () { return use_colors; } + + operator bool () const { return connected; } + + void red (bool bright = false) { color (31, bright); } + void green (bool bright = false) { color (32, bright); } + void yellow (bool bright = false) { color (33, bright); } + void blue (bool bright = false) { color (34, bright); } + void magenta (bool bright = false) { color (35, bright); } + void black (bool bright = false) { color (90, bright); } + void cyan (bool bright = false) { color (96, bright); } + + void bold () { code ("1m"); } + void normal () { code ("0m"); } + void inverse () { code ("7m"); } + void underline () { code ("4m"); } + +#define MODIFY(CODE) (use_colors ? "\033[" CODE "m" : "") + + const char *bright_magenta_code () { return MODIFY ("1;35"); } + const char *magenta_code () { return MODIFY ("0;35"); } + const char *blue_code () { return MODIFY ("0;34"); } + const char *bright_blue_code () { return MODIFY ("1;34"); } + const char *yellow_code () { return MODIFY ("0;33"); } + const char *bright_yellow_code () { return MODIFY ("1;33"); } + const char *green_code () { return MODIFY ("0;32"); } + const char *red_code () { return MODIFY ("0;31"); } + const char *cyan_code () { return MODIFY ("0;96"); } + const char *bright_red_code () { return MODIFY ("1;31"); } + const char *normal_code () { return MODIFY ("0"); } + const char *bold_code () { return MODIFY ("1"); } + + void cursor (bool on) { code (on ? "?25h" : "?25l"); } + + void erase_until_end_of_line () { code ("K"); } + + void erase_line_if_connected_otherwise_new_line () { + if (connected) + code ("1G"); + else + fputc ('\n', file), fflush (file); + } + + void reset (); +}; + +extern Terminal tout; // Terminal of 'stdout' (file descriptor '1') +extern Terminal terr; // Terminal of 'stderr' (file descriptor '2') + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/ternary.cpp b/src/sat/cadical/ternary.cpp new file mode 100644 index 000000000..259404fd8 --- /dev/null +++ b/src/sat/cadical/ternary.cpp @@ -0,0 +1,450 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// This procedure can be used to produce all possible hyper ternary +// resolvents from ternary clauses. In contrast to hyper binary resolution +// we would only try to produce ternary clauses from ternary clauses, i.e., +// do not consider quaternary clauses as antecedents. Of course if a binary +// clause is generated we keep it too. In any case we have to make sure +// though that we do not add clauses which are already in the formula or are +// subsumed by a binary clause in the formula. This procedure simulates +// structural hashing for multiplexer (if-then-else) and binary XOR gates in +// combination with equivalent literal substitution ('decompose') if run +// until to completion (which in the current implementation is too costly +// though and would need to be interleaved more eagerly with equivalent +// literal substitution). For more information see our CPAIOR'13 paper. + +/*------------------------------------------------------------------------*/ + +// Check whether a binary clause consisting of the permutation of the given +// literals already exists. + +bool Internal::ternary_find_binary_clause (int a, int b) { + assert (occurring ()); + assert (active (a)); + assert (active (b)); + size_t s = occs (a).size (); + size_t t = occs (b).size (); + int lit = s < t ? a : b; + if (opts.ternaryocclim < (int) occs (lit).size ()) + return true; + for (const auto &c : occs (lit)) { + if (c->size != 2) + continue; + const int *lits = c->literals; + if (lits[0] == a && lits[1] == b) + return true; + if (lits[0] == b && lits[1] == a) + return true; + } + return false; +} + +/*------------------------------------------------------------------------*/ + +// Check whether a ternary clause consisting of the permutation of the given +// literals already exists or is subsumed by an existing binary clause. + +bool Internal::ternary_find_ternary_clause (int a, int b, int c) { + assert (occurring ()); + assert (active (a)); + assert (active (b)); + assert (active (c)); + size_t r = occs (a).size (); + size_t s = occs (b).size (); + size_t t = occs (c).size (); + int lit; + if (r < s) + lit = (t < r) ? c : a; + else + lit = (t < s) ? c : b; + if (opts.ternaryocclim < (int) occs (lit).size ()) + return true; + for (const auto &d : occs (lit)) { + const int *lits = d->literals; + if (d->size == 2) { + if (lits[0] == a && lits[1] == b) + return true; + if (lits[0] == b && lits[1] == a) + return true; + if (lits[0] == a && lits[1] == c) + return true; + if (lits[0] == c && lits[1] == a) + return true; + if (lits[0] == b && lits[1] == c) + return true; + if (lits[0] == c && lits[1] == b) + return true; + } else { + assert (d->size == 3); + if (lits[0] == a && lits[1] == b && lits[2] == c) + return true; + if (lits[0] == a && lits[1] == c && lits[2] == b) + return true; + if (lits[0] == b && lits[1] == a && lits[2] == c) + return true; + if (lits[0] == b && lits[1] == c && lits[2] == a) + return true; + if (lits[0] == c && lits[1] == a && lits[2] == b) + return true; + if (lits[0] == c && lits[1] == b && lits[2] == a) + return true; + } + } + return false; +} + +/*------------------------------------------------------------------------*/ + +// Try to resolve the two ternary clauses on the given pivot (assumed to +// occur positively in the first clause, negatively in the second). If the +// resolvent has four literals, is tautological, already exists or in the +// case of a ternary resolvent is subsumed by an existing binary clause then +// 'false' is returned. The global 'clause' contains the resolvent and +// needs to be cleared in any case. + +bool Internal::hyper_ternary_resolve (Clause *c, int pivot, Clause *d) { + LOG ("hyper binary resolving on pivot %d", pivot); + LOG (c, "1st antecedent"); + LOG (d, "2nd antecedent"); + stats.ternres++; + assert (c->size == 3); + assert (d->size == 3); + assert (clause.empty ()); + for (const auto &lit : *c) + if (lit != pivot) + clause.push_back (lit); + for (const auto &lit : *d) { + if (lit == -pivot) + continue; + if (lit == clause[0]) + continue; + if (lit == -clause[0]) + return false; + if (lit == clause[1]) + continue; + if (lit == -clause[1]) + return false; + clause.push_back (lit); + } + size_t size = clause.size (); + if (size > 3) + return false; + if (size == 2 && ternary_find_binary_clause (clause[0], clause[1])) + return false; + if (size == 3 && + ternary_find_ternary_clause (clause[0], clause[1], clause[2])) + return false; + return true; +} + +/*------------------------------------------------------------------------*/ + +// Produce all ternary resolvents on literal 'pivot' and increment the +// 'steps' counter by the number of clauses containing 'pivot' which are +// used during this process. The reason for choosing this metric to measure +// the effort spent in 'ternary' is that it should be similar to one +// propagation step during search. + +void Internal::ternary_lit (int pivot, int64_t &steps, int64_t &htrs) { + LOG ("starting hyper ternary resolutions on pivot %d", pivot); + steps -= 1 + cache_lines (occs (pivot).size (), sizeof (Clause *)); + for (const auto &c : occs (pivot)) { + if (steps < 0) + break; + if (htrs < 0) + break; + if (c->garbage) + continue; + if (c->size != 3) { + assert (c->size == 2); + continue; + } + if (--steps < 0) + break; + bool assigned = false; + for (const auto &lit : *c) + if (val (lit)) { + assigned = true; + break; + } + if (assigned) + continue; + steps -= 1 + cache_lines (occs (-pivot).size (), sizeof (Clause *)); + for (const auto &d : occs (-pivot)) { + if (htrs < 0) + break; + if (--steps < 0) + break; + if (d->garbage) + continue; + if (d->size != 3) { + assert (d->size == 2); + continue; + } + for (const auto &lit : *d) + if (val (lit)) { + assigned = true; + break; + } + if (assigned) + continue; + assert (clause.empty ()); + htrs--; + if (hyper_ternary_resolve (c, pivot, d)) { + size_t size = clause.size (); + bool red = (size == 3 || (c->redundant && d->redundant)); + if (lrat) { + assert (lrat_chain.empty ()); + lrat_chain.push_back (c->id); + lrat_chain.push_back (d->id); + } + Clause *r = new_hyper_ternary_resolved_clause (red); + if (red) + r->hyper = true; + lrat_chain.clear (); + clause.clear (); + LOG (r, "hyper ternary resolved"); + stats.htrs++; + for (const auto &lit : *r) + occs (lit).push_back (r); + if (size == 2) { + LOG ("hyper ternary resolvent subsumes both antecedents"); + mark_garbage (c); + mark_garbage (d); + stats.htrs2++; + break; + } else { + assert (r->size == 3); + stats.htrs3++; + } + } else { + LOG (clause, "ignoring size %zd resolvent", clause.size ()); + clause.clear (); + } + } + } +} + +/*------------------------------------------------------------------------*/ + +// Same as 'ternary_lit' but pick the phase of the variable based on the +// number of positive and negative occurrence. + +void Internal::ternary_idx (int idx, int64_t &steps, int64_t &htrs) { + assert (0 < idx); + assert (idx <= max_var); + steps -= 3; + if (!active (idx)) + return; + if (!flags (idx).ternary) + return; + int pos = occs (idx).size (); + int neg = occs (-idx).size (); + if (pos <= opts.ternaryocclim && neg <= opts.ternaryocclim) { + LOG ("index %d has %zd positive and %zd negative occurrences", idx, + occs (idx).size (), occs (-idx).size ()); + int pivot = (neg < pos ? -idx : idx); + ternary_lit (pivot, steps, htrs); + } + flags (idx).ternary = false; +} + +/*------------------------------------------------------------------------*/ + +// One round of ternary resolution over all variables. As in 'block' and +// 'elim' we maintain a persistent global flag 'ternary' for each variable, +// which records, whether a ternary clause containing it was added. Then we +// can focus on those variables for which we have not tried ternary +// resolution before and nothing changed for them since then. This works +// across multiple calls to 'ternary' as well as 'ternary_round' since this +// 'ternary' variable flag is updated during adding (ternary) resolvents. +// This function goes over each variable just once. + +bool Internal::ternary_round (int64_t &steps_limit, int64_t &htrs_limit) { + + assert (!unsat); + +#ifndef QUIET + int64_t bincon = 0; + int64_t terncon = 0; +#endif + + init_occs (); + + steps_limit -= 1 + cache_lines (clauses.size (), sizeof (Clause *)); + for (const auto &c : clauses) { + steps_limit--; + if (c->garbage) + continue; + if (c->size > 3) + continue; + bool assigned = false, marked = false; + for (const auto &lit : *c) { + if (val (lit)) { + assigned = true; + break; + } + if (flags (lit).ternary) + marked = true; + } + if (assigned) + continue; + if (c->size == 2) { +#ifndef QUIET + bincon++; +#endif + } else { + assert (c->size == 3); + if (!marked) + continue; +#ifndef QUIET + terncon++; +#endif + } + + for (const auto &lit : *c) + occs (lit).push_back (c); + } + + PHASE ("ternary", stats.ternary, + "connected %" PRId64 " ternary %.0f%% " + "and %" PRId64 " binary clauses %.0f%%", + terncon, percent (terncon, clauses.size ()), bincon, + percent (bincon, clauses.size ())); + + // Try ternary resolution on all variables once. + // + for (auto idx : vars) { + if (terminated_asynchronously ()) + break; + if (steps_limit < 0) + break; + if (htrs_limit < 0) + break; + ternary_idx (idx, steps_limit, htrs_limit); + } + + // Gather some statistics for the verbose messages below and also + // determine whether new variables have been marked and it would make + // sense to run another round of ternary resolution over those variables. + // + int remain = 0; + for (auto idx : vars) { + if (!active (idx)) + continue; + if (!flags (idx).ternary) + continue; + remain++; + } + if (remain) + PHASE ("ternary", stats.ternary, "%d variables remain %.0f%%", remain, + percent (remain, max_var)); + else + PHASE ("ternary", stats.ternary, "completed hyper ternary resolution"); + + reset_occs (); + assert (!unsat); + + return remain; // Are there variables that should be tried again? +} + +/*------------------------------------------------------------------------*/ + +bool Internal::ternary () { + + if (!opts.ternary) + return false; + if (unsat) + return false; + if (terminated_asynchronously ()) + return false; + + // No new ternary clauses added since last time? + // + if (last.ternary.marked == stats.mark.ternary) + return false; + + SET_EFFORT_LIMIT (limit, ternary, true); + + START_SIMPLIFIER (ternary, TERNARY); + stats.ternary++; + + assert (!level); + + assert (!unsat); + if (watching ()) + reset_watches (); + + // The number of clauses derived through ternary resolution can grow + // substantially, particularly for random formulas. Thus we limit the + // number of added clauses too (actually the number of 'htrs'). + // + int64_t htrs_limit = stats.current.redundant + stats.current.irredundant; + htrs_limit *= opts.ternarymaxadd; + htrs_limit /= 100; + + // approximation of ternary ticks. + // TODO: count with ternary.ticks directly. + int64_t steps_limit = stats.ticks.ternary - limit; + stats.ticks.ternary = limit; + + // With 'stats.ternary' we actually count the number of calls to + // 'ternary_round' and not the number of calls to 'ternary'. But before + // the first round we want to show the limit on the number of steps and + // thus we increase counter for the first round here and skip increasing + // it in the loop below. + // + PHASE ("ternary", stats.ternary, + "will run a maximum of %d rounds " + "limited to %" PRId64 " steps and %" PRId64 " clauses", + opts.ternaryrounds, steps_limit, htrs_limit); + + bool resolved_binary_clause = false; + bool completed = false; + + for (int round = 0; + !terminated_asynchronously () && round < opts.ternaryrounds; + round++) { + if (htrs_limit < 0) + break; + if (steps_limit < 0) + break; + if (round) + stats.ternary++; + int old_htrs2 = stats.htrs2; + int old_htrs3 = stats.htrs3; + completed = ternary_round (steps_limit, htrs_limit); + int delta_htrs2 = stats.htrs2 - old_htrs2; + int delta_htrs3 = stats.htrs3 - old_htrs3; + PHASE ("ternary", stats.ternary, + "derived %d ternary and %d binary resolvents", delta_htrs3, + delta_htrs2); + report ('3', !opts.reportall && !(delta_htrs2 + delta_htrs2)); + if (delta_htrs2) + resolved_binary_clause = true; + if (!delta_htrs3) + break; + } + + assert (!occurring ()); + assert (!unsat); + init_watches (); + connect_watches (); + if (!propagate ()) { + LOG ("propagation after connecting watches results in inconsistency"); + learn_empty_clause (); + } + + if (completed) + last.ternary.marked = stats.mark.ternary; + + STOP_SIMPLIFIER (ternary, TERNARY); + + return resolved_binary_clause; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/testing.hpp b/src/sat/cadical/testing.hpp new file mode 100644 index 000000000..16dabb4b4 --- /dev/null +++ b/src/sat/cadical/testing.hpp @@ -0,0 +1,24 @@ +#ifndef _testing_hpp_INCLUDED +#define _testing_hpp_INCLUDED + +// This class provides access to 'internal' which should only be used for +// internal testing and not for any other purpose as 'internal' is supposed +// to be hidden. + +#include "cadical.hpp" + +namespace CaDiCaL { + +class Testing { + Solver *solver; + +public: + Testing (Solver &s) : solver (&s) {} + Testing (Solver *s) : solver (s) {} + Internal *internal () { return solver->internal; } + External *external () { return solver->external; } +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/tier.cpp b/src/sat/cadical/tier.cpp new file mode 100644 index 000000000..8112d81ac --- /dev/null +++ b/src/sat/cadical/tier.cpp @@ -0,0 +1,53 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +void Internal::recompute_tier () { + if (!opts.recomputetier) + return; + + ++stats.tierecomputed; + const int64_t delta = + stats.tierecomputed >= 16 ? 1u << 16 : (1u << stats.tierecomputed); + lim.recompute_tier = stats.conflicts + delta; + LOG ("rescheduling in %zd at %zd (conflicts at %zd)", delta, + lim.recompute_tier, stats.conflicts); +#ifndef NDEBUG + uint64_t total_used = 0; + for (auto u : stats.used[stable]) + total_used += u; + assert (total_used == stats.bump_used[stable]); +#endif + + if (!stats.bump_used[stable]) { + tier1[stable] = opts.reducetier1glue; + tier2[stable] = opts.reducetier2glue; + LOG ("tier1 limit = %d", tier1[stable]); + LOG ("tier2 limit = %d", tier2[stable]); + return; + } else { + uint64_t accumulated_tier1_limit = + stats.bump_used[stable] * opts.tier1limit / 100; + uint64_t accumulated_tier2_limit = + stats.bump_used[stable] * opts.tier2limit / 100; + uint64_t accumulated_used = 0; + for (size_t glue = 0; glue < stats.used[stable].size (); ++glue) { + const uint64_t u = stats.used[stable][glue]; + accumulated_used += u; + if (accumulated_used <= accumulated_tier1_limit) { + tier1[stable] = glue; + } + if (accumulated_used >= accumulated_tier2_limit) { + tier2[stable] = glue; + break; + } + } + } + + LOG ("tier1 limit = %d in %s mode", tier1[stable], + stable ? "stable" : "focused"); + LOG ("tier2 limit = %d in %s mode", tier2[stable], + stable ? "stable" : "focused"); +} + +} // namespace CaDiCaL \ No newline at end of file diff --git a/src/sat/cadical/tracer.hpp b/src/sat/cadical/tracer.hpp new file mode 100644 index 000000000..6af089ed3 --- /dev/null +++ b/src/sat/cadical/tracer.hpp @@ -0,0 +1,177 @@ +#ifndef _tracer_hpp_INCLUDED +#define _tracer_hpp_INCLUDED + +#include <cstdint> +#include <vector> + +namespace CaDiCaL { + +struct Internal; + +enum ConclusionType { CONFLICT = 1, ASSUMPTIONS = 2, CONSTRAINT = 4 }; + +// Proof tracer class to observer all possible proof events, +// such as added or deleted clauses. +// An implementation can decide on which events to act. +// +class Tracer { + +public: + Tracer () {} + virtual ~Tracer () {} + + /*------------------------------------------------------------------------*/ + /* */ + /* Basic Events */ + /* */ + /*------------------------------------------------------------------------*/ + + // Notify the tracer that a original clause has been added. + // Includes ID and whether the clause is redundant or irredundant + // Arguments: ID, redundant, clause, restored + // + virtual void add_original_clause (int64_t, bool, const std::vector<int> &, + bool = false) {} + + // Notify the observer that a new clause has been derived. + // Includes ID and whether the clause is redundant or irredundant + // If antecedents are derived they will be included here. + // Arguments: ID, redundant, clause, antecedents + // + virtual void add_derived_clause (int64_t, bool, const std::vector<int> &, + const std::vector<int64_t> &) {} + + // Notify the observer that a clause is deleted. + // Includes ID and redundant/irredundant + // Arguments: ID, redundant, clause + // + virtual void delete_clause (int64_t, bool, const std::vector<int> &) {} + + // Notify the observer that a clause is deleted. + // Includes ID and redundant/irredundant + // Arguments: ID, redundant, clause + // + virtual void demote_clause (uint64_t, const std::vector<int> &) {} + + // Notify the observer to remember that the clause might be restored later + // Arguments: ID, clause + // + virtual void weaken_minus (int64_t, const std::vector<int> &) {} + + // Notify the observer that a clause is strengthened + // Arguments: ID + // + virtual void strengthen (int64_t) {} + + // Notify the observer that the solve call ends with status StatusType + // If the status is UNSAT and an empty clause has been derived, the second + // argument will contain its id. + // Note that the empty clause is already added through add_derived_clause + // and finalized with finalize_clause + // Arguments: int, ID + // + virtual void report_status (int, int64_t) {} + + /*------------------------------------------------------------------------*/ + /* */ + /* Specifically non-incremental */ + /* */ + /*------------------------------------------------------------------------*/ + + // Notify the observer that a clause is finalized. + // Arguments: ID, clause + // + virtual void finalize_clause (int64_t, const std::vector<int> &) {} + + // Notify the observer that the proof begins with a set of reserved ids + // for original clauses. Given ID is the first derived clause ID. + // Arguments: ID + // + virtual void begin_proof (int64_t) {} + + /*------------------------------------------------------------------------*/ + /* */ + /* Specifically incremental */ + /* */ + /*------------------------------------------------------------------------*/ + + // Notify the observer that an assumption has been added + // Arguments: assumption_literal + // + virtual void solve_query () {} + + // Notify the observer that an assumption has been added + // Arguments: assumption_literal + // + virtual void add_assumption (int) {} + + // Notify the observer that a constraint has been added + // Arguments: constraint_clause + // + virtual void add_constraint (const std::vector<int> &) {} + + // Notify the observer that assumptions and constraints are reset + // + virtual void reset_assumptions () {} + + // Notify the observer that this clause could be derived, which + // is the negation of a core of failing assumptions/constraints. + // If antecedents are derived they will be included here. + // Arguments: ID, clause, antecedents + // + virtual void add_assumption_clause (int64_t, const std::vector<int> &, + const std::vector<int64_t> &) {} + + // Notify the observer that conclude unsat was requested. + // will give either the id of the empty clause, the id of a failing + // assumption clause or the ids of the failing constrain clauses + // Arguments: conclusion_type, clause_ids + // + virtual void conclude_unsat (ConclusionType, + const std::vector<int64_t> &) {} + + // Notify the observer that conclude sat was requested. + // will give the complete model as a vector. + // + virtual void conclude_sat (const std::vector<int> &) {} + + // Notify the observer that conclude unknown was requested. + // will give the current trail as a vector. + // + virtual void conclude_unknown (const std::vector<int> &) {} +}; + +/*--------------------------------------------------------------------------*/ + +// Following tracers for internal use. + +struct InternalTracer : public Tracer { +public: + InternalTracer () {} + virtual ~InternalTracer () {} + + virtual void connect_internal (Internal *) {} +}; + +class StatTracer : public InternalTracer { +public: + StatTracer () {} + virtual ~StatTracer () {} + + virtual void print_stats () {} +}; + +class FileTracer : public InternalTracer { + +public: + FileTracer () {} + virtual ~FileTracer () {} + + virtual bool closed () = 0; + virtual void close (bool print = false) = 0; + virtual void flush (bool print = false) = 0; +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/transred.cpp b/src/sat/cadical/transred.cpp new file mode 100644 index 000000000..f9dc0cafa --- /dev/null +++ b/src/sat/cadical/transred.cpp @@ -0,0 +1,253 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +// Implement transitive reduction in the binary implication graph. This is +// important for hyper binary resolution, which has the risk to produce too +// many hyper binary resolvents otherwise. This algorithm only works on +// binary clauses and is usually pretty fast. It will also find some failed +// literals (in the binary implication graph). + +void Internal::transred () { + if (!opts.transred) + return; + if (unsat) + return; + if (terminated_asynchronously ()) + return; + if (!stats.current.redundant && !stats.current.irredundant) + return; + + assert (opts.transred); + assert (!level); + + START_SIMPLIFIER (transred, TRANSRED); + stats.transreds++; + + // Transitive reduction can not be run to completion for larger formulas + // with many binary clauses. We bound it in the same way as 'probe_core'. + // + int64_t limit = stats.propagations.search; + limit -= last.transred.propagations; + limit *= 1e-3 * opts.transredeffort; + if (limit < opts.transredmineff) + limit = opts.transredmineff; + if (limit > opts.transredmaxeff) + limit = opts.transredmaxeff; + + PHASE ("transred", stats.transreds, + "transitive reduction limit of %" PRId64 " propagations", limit); + + const auto end = clauses.end (); + auto i = clauses.begin (); + + // Find first clause not checked for being transitive yet. + // + for (; i != end; i++) { + Clause *c = *i; + if (c->garbage) + continue; + if (c->size != 2) + continue; + if (c->redundant && c->hyper) + continue; + if (!c->transred) + break; + } + + // If all candidate clauses have been checked reschedule all. + // + if (i == end) { + + PHASE ("transred", stats.transreds, + "rescheduling all clauses since no clauses to check left"); + for (i = clauses.begin (); i != end; i++) { + Clause *c = *i; + if (c->transred) + c->transred = false; + } + i = clauses.begin (); + } + + // Move watches of binary clauses to the front. Thus we can stop iterating + // watches as soon a long clause is found during watch traversal. + // + sort_watches (); + + // This working stack plays the same role as the 'trail' during standard + // propagation. + // + vector<int> work; + + int64_t propagations = 0, units = 0, removed = 0; + + while (!unsat && i != end && !terminated_asynchronously () && + propagations < limit) { + Clause *c = *i++; + + // A clause is a candidate for being transitive if it is binary, and not + // the result of hyper binary resolution. The reason for excluding + // those, is that they come in large numbers, most of them are reduced + // away anyhow and further are non-transitive at the point they are + // added (see the code in 'hyper_binary_resolve' in 'prope.cpp' and + // also check out our CPAIOR paper on tree-based look ahead). + // + if (c->garbage) + continue; + if (c->size != 2) + continue; + if (c->redundant && c->hyper) + continue; + if (c->transred) + continue; // checked before? + c->transred = true; // marked as checked + + LOG (c, "checking transitive reduction of"); + + // Find a different path from 'src' to 'dst' in the binary implication + // graph, not using 'c'. Since this is the same as checking whether + // there is a path from '-dst' to '-src', we can do the reverse search + // if the number of watches of '-dst' is larger than those of 'src'. + // + int src = -c->literals[0]; + int dst = c->literals[1]; + if (val (src) || val (dst)) + continue; + if (watches (-src).size () < watches (dst).size ()) { + int tmp = dst; + dst = -src; + src = -tmp; + } + + LOG ("searching path from %d to %d", src, dst); + + // If the candidate clause is irredundant then we can not use redundant + // binary clauses in the implication graph. See our inprocessing rules + // paper, why this restriction is required. + // + const bool irredundant = !c->redundant; + + assert (work.empty ()); + mark (src); + work.push_back (src); + LOG ("transred assign %d", src); + + bool transitive = false; // found path from 'src' to 'dst'? + bool failed = false; // 'src' failed literal? + + size_t j = 0; // 'propagated' in BFS + + assert (lrat_chain.empty ()); + assert (mini_chain.empty ()); + vector<int> parents; + + while (!transitive && !failed && j < work.size ()) { + const int lit = work[j++]; + assert (marked (lit) > 0); + LOG ("transred propagating %d", lit); + propagations++; + const Watches &ws = watches (-lit); + const const_watch_iterator eow = ws.end (); + const_watch_iterator k; + for (k = ws.begin (); !transitive && !failed && k != eow; k++) { + const Watch &w = *k; + if (!w.binary ()) + break; // since we sorted watches above + Clause *d = w.clause; + if (d == c) + continue; + if (irredundant && d->redundant) + continue; + if (d->garbage) + continue; + const int other = w.blit; + if (other == dst) + transitive = true; // 'dst' reached + else { + const int tmp = marked (other); + if (tmp > 0) + continue; + else if (tmp < 0) { + if (lrat) { + parents.push_back (lit); + mini_chain.push_back (d->id); + work.push_back (other); + } + LOG ("found both %d and %d reachable", -other, other); + failed = true; + } else { + if (lrat) { + parents.push_back (lit); + mini_chain.push_back (d->id); + } + mark (other); + work.push_back (other); + LOG ("transred assign %d", other); + } + } + } + } + + int failed_lit = work.back (); + int next_pos = 0; + int next_neg = 0; + + // Unassign all assigned literals (same as '[bp]acktrack'). + // + while (!work.empty ()) { + const int lit = work.back (); + work.pop_back (); + if (lrat && failed && !work.empty ()) { + assert (!parents.empty () && !mini_chain.empty ()); + LOG ("transred LRAT current lit %d next pos %d next neg %d", lit, + next_pos, next_neg); + if (lit == failed_lit || lit == next_pos) { + lrat_chain.push_back (mini_chain.back ()); + next_pos = parents.back (); + } else if (lit == -failed_lit || lit == next_neg) { + lrat_chain.push_back (mini_chain.back ()); + next_neg = parents.back (); + } + parents.pop_back (); + mini_chain.pop_back (); + } + unmark (lit); + } + mini_chain.clear (); + assert (mini_chain.empty ()); + if (lrat && failed) { + reverse (lrat_chain.begin (), lrat_chain.end ()); + } + + if (transitive) { + removed++; + stats.transitive++; + LOG (c, "transitive redundant"); + mark_garbage (c); + } else if (failed) { + units++; + LOG ("found failed literal %d during transitive reduction", src); + stats.failed++; + stats.transredunits++; + assign_unit (-src); + if (!propagate ()) { + VERBOSE (1, "propagating new unit results in conflict"); + learn_empty_clause (); + } + } + lrat_chain.clear (); + } + + last.transred.propagations = stats.propagations.search; + stats.propagations.transred += propagations; + erase_vector (work); + + PHASE ("transred", stats.transreds, + "removed %" PRId64 " transitive clauses, found %" PRId64 " units", + removed, units); + + STOP_SIMPLIFIER (transred, TRANSRED); + report ('t', !opts.reportall && !(removed + units)); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/unstable.cpp b/src/sat/cadical/unstable.cpp new file mode 100644 index 000000000..5b23164be --- /dev/null +++ b/src/sat/cadical/unstable.cpp @@ -0,0 +1,29 @@ +#ifdef PROFILE_MODE +#include "internal.hpp" + +namespace CaDiCaL { + +bool Internal::propagate_unstable () { + assert (!stable); + START (propunstable); + bool res = propagate (); + STOP (propunstable); + return res; +} + +void Internal::analyze_unstable () { + assert (!stable); + START (analyzeunstable); + analyze (); + STOP (analyzeunstable); +} + +int Internal::decide_unstable () { + assert (!stable); + return decide (); +} + +}; // namespace CaDiCaL +#else +int unstable_if_no_profile_mode; +#endif diff --git a/src/sat/cadical/util.cpp b/src/sat/cadical/util.cpp new file mode 100644 index 000000000..5b7c52f30 --- /dev/null +++ b/src/sat/cadical/util.cpp @@ -0,0 +1,129 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +bool parse_int_str (const char *val_str, int &val) { + if (!strcmp (val_str, "true")) + val = 1; + else if (!strcmp (val_str, "false")) + val = 0; + else { + const char *p = val_str; + int sign; + + if (*p == '-') + sign = -1, p++; + else + sign = 1; + + int ch; + if (!isdigit ((ch = *p++))) + return false; + + const int64_t bound = -(int64_t) INT_MIN; + int64_t mantissa = ch - '0'; + + while (isdigit (ch = *p++)) { + if (bound / 10 < mantissa) + mantissa = bound; + else + mantissa *= 10; + const int digit = ch - '0'; + if (bound - digit < mantissa) + mantissa = bound; + else + mantissa += digit; + } + + int exponent = 0; + if (ch == 'e') { + while (isdigit ((ch = *p++))) + exponent = exponent ? 10 : ch - '0'; + if (ch) + return false; + } else if (ch) + return false; + + assert (exponent <= 10); + int64_t val64 = mantissa; + for (int i = 0; i < exponent; i++) + val64 *= 10; + + if (sign < 0) { + val64 = -val64; + if (val64 < INT_MIN) + val64 = INT_MIN; + } else { + if (val64 > INT_MAX) + val64 = INT_MAX; + } + + assert (INT_MIN <= val64); + assert (val64 <= INT_MAX); + + val = val64; + } + return true; +} + +/*------------------------------------------------------------------------*/ + +bool has_suffix (const char *str, const char *suffix) { + size_t k = strlen (str), l = strlen (suffix); + return k > l && !strcmp (str + k - l, suffix); +} + +bool has_prefix (const char *str, const char *prefix) { + for (const char *p = str, *q = prefix; *q; q++, p++) + if (*q != *p) + return false; + return true; +} + +/*------------------------------------------------------------------------*/ + +bool is_color_option (const char *arg) { + return !strcmp (arg, "--color") || !strcmp (arg, "--colors") || + !strcmp (arg, "--colour") || !strcmp (arg, "--colours") || + !strcmp (arg, "--color=1") || !strcmp (arg, "--colors=1") || + !strcmp (arg, "--colour=1") || !strcmp (arg, "--colours=1") || + !strcmp (arg, "--color=true") || !strcmp (arg, "--colors=true") || + !strcmp (arg, "--colour=true") || !strcmp (arg, "--colours=true"); +} + +bool is_no_color_option (const char *arg) { + return !strcmp (arg, "--no-color") || !strcmp (arg, "--no-colors") || + !strcmp (arg, "--no-colour") || !strcmp (arg, "--no-colours") || + !strcmp (arg, "--color=0") || !strcmp (arg, "--colors=0") || + !strcmp (arg, "--colour=0") || !strcmp (arg, "--colours=0") || + !strcmp (arg, "--color=false") || + !strcmp (arg, "--colors=false") || + !strcmp (arg, "--colour=false") || + !strcmp (arg, "--colours=false"); +} + +/*------------------------------------------------------------------------*/ + +static uint64_t primes[] = { + 1111111111111111111lu, 2222222222222222249lu, 3333333333333333347lu, + 4444444444444444537lu, 5555555555555555621lu, 6666666666666666677lu, + 7777777777777777793lu, 8888888888888888923lu, 9999999999999999961lu, +}; + +uint64_t hash_string (const char *str) { + const unsigned size = sizeof primes / sizeof *primes; + uint64_t res = 0; + unsigned char ch; + unsigned i = 0; + for (const char *p = str; (ch = *p); p++) { + res += ch; + res *= primes[i++]; + if (i == size) + i = 0; + } + return res; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/util.hpp b/src/sat/cadical/util.hpp new file mode 100644 index 000000000..9a971709c --- /dev/null +++ b/src/sat/cadical/util.hpp @@ -0,0 +1,167 @@ +#ifndef _util_hpp_INCLUDED +#define _util_hpp_INCLUDED + +#include <cassert> +#include <cstdint> +#include <vector> + +namespace CaDiCaL { + +using namespace std; + +// Common simple utility functions independent from 'Internal'. + +/*------------------------------------------------------------------------*/ + +inline double relative (double a, double b) { return b ? a / b : 0; } +inline double percent (double a, double b) { return relative (100 * a, b); } +inline int sign (int lit) { return (lit > 0) - (lit < 0); } +inline unsigned bign (int lit) { return 1 + (lit < 0); } + +/*------------------------------------------------------------------------*/ + +bool has_suffix (const char *str, const char *suffix); +bool has_prefix (const char *str, const char *prefix); + +/*------------------------------------------------------------------------*/ + +// Parse integer string in the form of +// +// true +// false +// [-]<mantissa>[e<exponent>] +// +// and in the latter case '<val>' has to be within [-INT_MAX,INT_MAX]. +// +// The function returns true if parsing is successful and then also sets +// the second argument to the parsed value. + +bool parse_int_str (const char *str, int &); + +/*------------------------------------------------------------------------*/ + +inline bool is_power_of_two (unsigned n) { return n && !(n & (n - 1)); } + +inline bool contained (int64_t c, int64_t l, int64_t u) { + return l <= c && c <= u; +} + +inline bool aligned (size_t n, size_t alignment) { + assert (is_power_of_two (alignment)); + const size_t mask = alignment - 1; + return !(n & mask); +} + +inline size_t align (size_t n, size_t alignment) { + size_t res = n; + assert (is_power_of_two (alignment)); + const size_t mask = alignment - 1; + if (res & mask) + res = (res | mask) + 1; + assert (aligned (res, alignment)); + return res; +} + +/*------------------------------------------------------------------------*/ + +inline bool parity (unsigned a) { + assert (sizeof a == 4); + unsigned tmp = a; + tmp ^= (tmp >> 16); + tmp ^= (tmp >> 8); + tmp ^= (tmp >> 4); + tmp ^= (tmp >> 2); + tmp ^= (tmp >> 1); + return tmp & 1; +} + +/*------------------------------------------------------------------------*/ + +// The standard 'Effective STL' way (though not guaranteed) to clear a +// vector and reduce its capacity to zero, thus deallocating all its +// internal memory. This is quite important for keeping the actual +// allocated size of watched and occurrence lists small particularly during +// bounded variable elimination where many clauses are added and removed. + +template <class T> void erase_vector (std::vector<T> &v) { + if (v.capacity ()) { + std::vector<T> ().swap (v); + } + assert (!v.capacity ()); // not guaranteed though +} + +// The standard 'Effective STL' way (though not guaranteed) to shrink the +// capacity of a vector to its size thus kind of releasing all the internal +// excess memory not needed at the moment any more. + +template <class T> void shrink_vector (std::vector<T> &v) { + if (v.capacity () > v.size ()) { + std::vector<T> (v).swap (v); + } + assert (v.capacity () == v.size ()); // not guaranteed though +} + +template <class T> +static void enlarge_init (vector<T> &v, size_t N, const T &i) { + if (v.size () < N) + v.resize (N, i); +} + +template <class T> static void enlarge_only (vector<T> &v, size_t N) { + if (v.size () < N) + v.resize (N, T ()); +} + +template <class T> static void enlarge_zero (vector<T> &v, size_t N) { + enlarge_init (v, N, (const T &) 0); +} + +// Clean-up class for bad_alloc error safety. + +template <typename T> struct DeferDeleteArray { + T *data; + DeferDeleteArray (T *t) : data (t) {} + ~DeferDeleteArray () { delete[] data; } + void release () { data = nullptr; } + void free () { + delete[] data; + data = nullptr; + } +}; + +template <typename T> struct DeferDeletePtr { + T *data; + DeferDeletePtr (T *t) : data (t) {} + ~DeferDeletePtr () { delete data; } + void release () { data = nullptr; } + void free () { + delete data; + data = nullptr; + } +}; + +/*------------------------------------------------------------------------*/ + +template <class T> inline void clear_n (T *base, size_t n) { + memset (base, 0, sizeof (T) * n); +} + +/*------------------------------------------------------------------------*/ + +// These are options both to 'cadical' and 'mobical'. After wasting some +// on not remembering the spelling (British vs American), nor singular vs +// plural and then wanted to use '--color=false', and '--colours=0' too, I +// just factored this out into these two utility functions. + +bool is_color_option (const char *arg); // --color, --colour, ... +bool is_no_color_option (const char *arg); // --no-color, --no-colour, ... + +/*------------------------------------------------------------------------*/ + +uint64_t hash_string (const char *str); + +/*------------------------------------------------------------------------*/ + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/var.cpp b/src/sat/cadical/var.cpp new file mode 100644 index 000000000..0baa0b291 --- /dev/null +++ b/src/sat/cadical/var.cpp @@ -0,0 +1,39 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +void Internal::reset_subsume_bits () { + LOG ("marking all variables as not subsume"); + for (auto idx : vars) + flags (idx).subsume = false; +} + +void Internal::check_var_stats () { +#ifndef NDEBUG + int64_t fixed = 0, eliminated = 0, substituted = 0, pure = 0, unused = 0; + for (auto idx : vars) { + Flags &f = flags (idx); + if (f.active ()) + continue; + if (f.fixed ()) + fixed++; + if (f.eliminated ()) + eliminated++; + if (f.substituted ()) + substituted++; + if (f.unused ()) + unused++; + if (f.pure ()) + pure++; + } + assert (stats.now.fixed == fixed); + assert (stats.now.eliminated == eliminated); + assert (stats.now.substituted == substituted); + assert (stats.now.pure == pure); + int64_t inactive = unused + fixed + eliminated + substituted + pure; + assert (stats.inactive == inactive); + assert (max_var == stats.active + stats.inactive); +#endif +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/var.hpp b/src/sat/cadical/var.hpp new file mode 100644 index 000000000..38b52c811 --- /dev/null +++ b/src/sat/cadical/var.hpp @@ -0,0 +1,22 @@ +#ifndef _var_hpp_INCLUDED +#define _var_hpp_INCLUDED + +namespace CaDiCaL { + +struct Clause; + +// This structure captures data associated with an assigned variable. + +struct Var { + + // Note that none of these members is valid unless the variable is + // assigned. During unassigning a variable we do not reset it. + + int level; // decision level + int trail; // trail height at assignment + Clause *reason; // implication graph edge during search +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/veripbtracer.cpp b/src/sat/cadical/veripbtracer.cpp new file mode 100644 index 000000000..3a8c817f7 --- /dev/null +++ b/src/sat/cadical/veripbtracer.cpp @@ -0,0 +1,394 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +VeripbTracer::VeripbTracer (Internal *i, File *f, bool b, bool a, bool c) + : internal (i), file (f), with_antecedents (a), checked_deletions (c), + num_clauses (0), size_clauses (0), clauses (0), last_hash (0), + last_id (0), last_clause (0) +#ifndef QUIET + , + added (0), deleted (0) +#endif +{ + (void) internal; + + // Initialize random number table for hash function. + // + Random random (42); + for (unsigned n = 0; n < num_nonces; n++) { + uint64_t nonce = random.next (); + if (!(nonce & 1)) + nonce++; + assert (nonce), assert (nonce & 1); + nonces[n] = nonce; + } +#ifndef NDEBUG + binary = b; +#else + (void) b; +#endif +} + +void VeripbTracer::connect_internal (Internal *i) { + internal = i; + file->connect_internal (internal); + LOG ("VERIPB TRACER connected to internal"); +} + +VeripbTracer::~VeripbTracer () { + LOG ("VERIPB TRACER delete"); + delete file; + for (size_t i = 0; i < size_clauses; i++) + for (HashId *c = clauses[i], *next; c; c = next) + next = c->next, delete_clause (c); + delete[] clauses; +} + +/*------------------------------------------------------------------------*/ + +void VeripbTracer::enlarge_clauses () { + assert (num_clauses == size_clauses); + const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1; + LOG ("VeriPB Tracer enlarging clauses from %" PRIu64 " to %" PRIu64, + (uint64_t) size_clauses, (uint64_t) new_size_clauses); + HashId **new_clauses; + new_clauses = new HashId *[new_size_clauses]; + clear_n (new_clauses, new_size_clauses); + for (uint64_t i = 0; i < size_clauses; i++) { + for (HashId *c = clauses[i], *next; c; c = next) { + next = c->next; + const uint64_t h = reduce_hash (c->hash, new_size_clauses); + c->next = new_clauses[h]; + new_clauses[h] = c; + } + } + delete[] clauses; + clauses = new_clauses; + size_clauses = new_size_clauses; +} + +HashId *VeripbTracer::new_clause () { + HashId *res = new HashId (); + res->next = 0; + res->hash = last_hash; + res->id = last_id; + last_clause = res; + num_clauses++; + return res; +} + +void VeripbTracer::delete_clause (HashId *c) { + assert (c); + num_clauses--; + delete c; +} + +uint64_t VeripbTracer::reduce_hash (uint64_t hash, uint64_t size) { + assert (size > 0); + unsigned shift = 32; + uint64_t res = hash; + while ((((uint64_t) 1) << shift) > size) { + res ^= res >> shift; + shift >>= 1; + } + res &= size - 1; + assert (res < size); + return res; +} + +uint64_t VeripbTracer::compute_hash (const int64_t id) { + assert (id > 0); + unsigned j = id % num_nonces; // Dont know if this is a good + uint64_t tmp = nonces[j] * (uint64_t) id; // hash funktion or even better + return last_hash = tmp; // than just using id. +} + +bool VeripbTracer::find_and_delete (const int64_t id) { + if (!num_clauses) + return false; + /* + if (last_clause && last_clause->id == id) { + const uint64_t h = reduce_hash (last_clause->hash, size_clauses); + clauses[h] = last_clause->next; + delete last_clause; + return true; + } + */ + HashId **res = 0, *c; + const uint64_t hash = compute_hash (id); + const uint64_t h = reduce_hash (hash, size_clauses); + for (res = clauses + h; (c = *res); res = &c->next) { + if (c->hash == hash && c->id == id) { + break; + } + if (!c->next) + return false; + } + if (!c) + return false; + assert (c && res); + *res = c->next; + delete_clause (c); + return true; +} + +void VeripbTracer::insert () { + if (num_clauses == size_clauses) + enlarge_clauses (); + const uint64_t h = reduce_hash (compute_hash (last_id), size_clauses); + HashId *c = new_clause (); + c->next = clauses[h]; + clauses[h] = c; +} + +/*------------------------------------------------------------------------*/ + +inline void VeripbTracer::put_binary_zero () { + assert (binary); + assert (file); + file->put ((unsigned char) 0); +} + +inline void VeripbTracer::put_binary_lit (int lit) { + assert (binary); + assert (file); + assert (lit != INT_MIN); + unsigned x = 2 * abs (lit) + (lit < 0); + unsigned char ch; + while (x & ~0x7f) { + ch = (x & 0x7f) | 0x80; + file->put (ch); + x >>= 7; + } + ch = x; + file->put (ch); +} + +inline void VeripbTracer::put_binary_id (int64_t id, bool can_be_negative) { + assert (binary); + assert (file); + uint64_t x = abs (id); + if (can_be_negative) { + x = 2 * x + (id < 0); + } + unsigned char ch; + while (x & ~0x7f) { + ch = (x & 0x7f) | 0x80; + file->put (ch); + x >>= 7; + } + ch = x; + file->put (ch); +} + +/*------------------------------------------------------------------------*/ + +void VeripbTracer::veripb_add_derived_clause ( + int64_t id, bool redundant, const vector<int> &clause, + const vector<int64_t> &chain) { + file->put ("pol "); + bool first = true; + for (auto p = chain.rbegin (); p != chain.rend (); p++) { + auto cid = *p; + if (first) { + first = false; + file->put (cid); + } else { + file->put (' '); + file->put (cid); + file->put (" + s"); + } + } + file->put ("\n"); + file->put ("e "); + for (const auto &external_lit : clause) { + file->put ("1 "); + if (external_lit < 0) + file->put ('~'); + file->put ('x'); + file->put (abs (external_lit)); + file->put (' '); + } + file->put (">= 1 ; "); + file->put (id); + file->put (" ;\n"); + if (!redundant && checked_deletions) { + file->put ("core id "); + file->put (id); + file->put ("\n"); + } +} + +void VeripbTracer::veripb_add_derived_clause (int64_t id, bool redundant, + const vector<int> &clause) { + file->put ("rup "); + for (const auto &external_lit : clause) { + file->put ("1 "); + if (external_lit < 0) + file->put ('~'); + file->put ('x'); + file->put (abs (external_lit)); + file->put (' '); + } + file->put (">= 1 ;\n"); + if (!redundant && checked_deletions) { + file->put ("core id "); + file->put (id); + file->put ("\n"); + } +} + +void VeripbTracer::veripb_begin_proof (int64_t reserved_ids) { + file->put ("pseudo-Boolean proof version 2.0\n"); + file->put ("f "); + file->put (reserved_ids); + file->put ("\n"); +} + +void VeripbTracer::veripb_delete_clause (int64_t id, bool redundant) { + if (!redundant && checked_deletions && find_and_delete (id)) + return; + if (redundant || !checked_deletions) + file->put ("del id "); + else { + file->put ("delc "); + } + file->put (id); + file->put ("\n"); +} + +void VeripbTracer::veripb_report_status (bool unsat, int64_t conflict_id) { + file->put ("output NONE\n"); + if (unsat) { + file->put ("conclusion UNSAT : "); + file->put (conflict_id); + file->put (" \n"); + } else + file->put ("conclusion NONE\n"); + file->put ("end pseudo-Boolean proof\n"); +} + +void VeripbTracer::veripb_strengthen (int64_t id) { + if (!checked_deletions) + return; + file->put ("core id "); + file->put (id); + file->put ("\n"); +} + +/*------------------------------------------------------------------------*/ + +void VeripbTracer::begin_proof (int64_t id) { + if (file->closed ()) + return; + LOG ("VERIPB TRACER tracing start of proof with %" PRId64 + "original clauses", + id); + veripb_begin_proof (id); +} + +void VeripbTracer::add_derived_clause (int64_t id, bool redundant, + const vector<int> &clause, + const vector<int64_t> &chain) { + if (file->closed ()) + return; + LOG ("VERIPB TRACER tracing addition of derived clause[%" PRId64 "]", id); + if (with_antecedents) + veripb_add_derived_clause (id, redundant, clause, chain); + else + veripb_add_derived_clause (id, redundant, clause); +#ifndef QUIET + added++; +#endif +} + +void VeripbTracer::delete_clause (int64_t id, bool redundant, + const vector<int> &) { + if (file->closed ()) + return; + LOG ("VERIPB TRACER tracing deletion of clause[%" PRId64 "]", id); + veripb_delete_clause (id, redundant); +#ifndef QUIET + deleted++; +#endif +} + +void VeripbTracer::report_status (int status, int64_t conflict_id) { + if (file->closed ()) + return; +#ifdef LOGGING + if (conflict_id) + LOG ("VERIPB TRACER tracing finalization of proof with empty " + "clause[%" PRId64 "]", + conflict_id); +#endif + veripb_report_status (status == UNSATISFIABLE, conflict_id); +} + +void VeripbTracer::weaken_minus (int64_t id, const vector<int> &) { + if (!checked_deletions) + return; + if (file->closed ()) + return; + LOG ("VERIPB TRACER tracing weaken minus of clause[%" PRId64 "]", id); + last_id = id; + insert (); +} + +void VeripbTracer::strengthen (int64_t id) { + if (file->closed ()) + return; + LOG ("VERIPB TRACER tracing strengthen of clause[%" PRId64 "]", id); + veripb_strengthen (id); +} + +/*------------------------------------------------------------------------*/ + +bool VeripbTracer::closed () { return file->closed (); } + +#ifndef QUIET + +void VeripbTracer::print_statistics () { + // TODO complete + uint64_t bytes = file->bytes (); + uint64_t total = added + deleted; + MSG ("VeriPB %" PRId64 " added clauses %.2f%%", added, + percent (added, total)); + MSG ("VeriPB %" PRId64 " deleted clauses %.2f%%", deleted, + percent (deleted, total)); + MSG ("VeriPB %" PRId64 " bytes (%.2f MB)", bytes, + bytes / (double) (1 << 20)); +} + +#endif + +void VeripbTracer::close (bool print) { + assert (!closed ()); + file->close (); +#ifndef QUIET + if (print) { + MSG ("VeriPB proof file '%s' closed", file->name ()); + print_statistics (); + } +#else + (void) print; +#endif +} + +void VeripbTracer::flush (bool print) { + assert (!closed ()); + file->flush (); +#ifndef QUIET + if (print) { + MSG ("VeriPB proof file '%s' flushed", file->name ()); + print_statistics (); + } +#else + (void) print; +#endif +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/veripbtracer.hpp b/src/sat/cadical/veripbtracer.hpp new file mode 100644 index 000000000..d8e3dafdf --- /dev/null +++ b/src/sat/cadical/veripbtracer.hpp @@ -0,0 +1,102 @@ +#ifndef _veripbtracer_h_INCLUDED +#define _veripbtracer_h_INCLUDED + +class FileTracer; + +namespace CaDiCaL { + +struct HashId { + HashId *next; // collision chain link for hash table + uint64_t hash; // previously computed full 64-bit hash + int64_t id; // id of clause +}; + +class VeripbTracer : public FileTracer { + + Internal *internal; + File *file; +#ifndef NDEBUG + bool binary; +#endif + bool with_antecedents; + bool checked_deletions; + + // hash table for checked deletions + // + uint64_t num_clauses; // number of clauses in hash table + uint64_t size_clauses; // size of clause hash table + HashId **clauses; // hash table of clauses + + static const unsigned num_nonces = 4; + + uint64_t nonces[num_nonces]; // random numbers for hashing + uint64_t last_hash; // last computed hash value of clause + int64_t last_id; // id of the last added clause + HashId *last_clause; + uint64_t compute_hash (int64_t); // compute and save hash value of clause + + HashId *new_clause (); + void delete_clause (HashId *); + + // Reduce hash value to the actual size. + // + static uint64_t reduce_hash (uint64_t hash, uint64_t size); + + void enlarge_clauses (); // enlarge hash table for clauses + void insert (); // insert clause in hash table + bool + find_and_delete (const int64_t); // find clause position in hash table + +#ifndef QUIET + int64_t added, deleted; +#endif + vector<int64_t> delete_ids; + + void put_binary_zero (); + void put_binary_lit (int external_lit); + void put_binary_id (int64_t id, bool = false); + + // support veriPB + void veripb_add_derived_clause (int64_t, bool redundant, + const vector<int> &clause, + const vector<int64_t> &chain); + void veripb_add_derived_clause (int64_t, bool redundant, + const vector<int> &clause); + void veripb_begin_proof (int64_t reserved_ids); + void veripb_delete_clause (int64_t id, bool redundant); + void veripb_report_status (bool unsat, int64_t conflict_id); + void veripb_strengthen (int64_t); + +public: + // own and delete 'file' + VeripbTracer (Internal *, File *file, bool, bool, bool); + ~VeripbTracer (); + + void connect_internal (Internal *i) override; + void begin_proof (int64_t) override; + + void add_original_clause (int64_t, bool, const vector<int> &, + bool = false) override {} // skip + + void add_derived_clause (int64_t, bool, const vector<int> &, + const vector<int64_t> &) override; + + void delete_clause (int64_t, bool, const vector<int> &) override; + void finalize_clause (int64_t, const vector<int> &) override {} // skip + + void report_status (int, int64_t) override; + + void weaken_minus (int64_t, const vector<int> &) override; + void strengthen (int64_t) override; + +#ifndef QUIET + void print_statistics (); +#endif + bool closed () override; + void close (bool) override; + void flush (bool) override; +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/version.cpp b/src/sat/cadical/version.cpp new file mode 100644 index 000000000..c96540fb8 --- /dev/null +++ b/src/sat/cadical/version.cpp @@ -0,0 +1,107 @@ +/*------------------------------------------------------------------------*/ + +// To simplify the build process without 'make', you can disable the +// generation of 'build.hpp' through '../scripts/make-build-header.sh' by +// defining '-DNBUILD'. Then we try to guess part of the configuration. + +#ifndef NBUILD +#if __GNUC__ > 4 +#if __has_include(<build.hpp>) +#include "build.hpp" +#endif // __has_include +#else +#include "build.hpp" +#endif // __GNUC > 4 +#endif // NBUILD + +/*------------------------------------------------------------------------*/ + +// We prefer short 3 character version numbers made of digits and lower case +// letters only, which gives 36^3 = 46656 different versions. The following +// macro is used for the non-standard build process and only set from +// the file '../VERSION' with '../scripts/update-version.sh'. The standard +// build process relies on 'VERSION' to be defined in 'build.hpp'. + +#ifdef NBUILD +#ifndef VERSION +#define VERSION "2.2.0-rc1" +#endif // VERSION +#endif // NBUILD + + /*------------------------------------------------------------------------*/ + + // The copyright of the code is here. + + static const char *COPYRIGHT = "Copyright (c) 2016-2024"; +static const char *AUTHORS = + "A. Biere, M. Fleury, N. Froleyks, K. Fazekas, F. Pollitt, T. Faller"; +static const char *AFFILIATIONS = + "JKU Linz, University of Freiburg, TU Wien"; + +/*------------------------------------------------------------------------*/ + +// Again if we do not have 'NBUILD' or if something during configuration is +// broken we still want to be able to compile the solver. In this case we +// then try our best to figure out 'COMPILER' and 'DATE', but for +// 'IDENTIFIER' and 'FLAGS' we can only use the '0' string, which marks +// those as unknown. + +#ifndef COMPILER +#ifdef __clang__ +#ifdef __VERSION__ +#define COMPILER "clang++-" __VERSION__ +#else +#define COMPILER "clang++" +#endif +#elif defined(__GNUC__) +#ifdef __VERSION__ +#define COMPILER "g++-" __VERSION__ +#else +#define COMPILER "g++" +#endif +#else +#define COMPILER 0 +#endif +#endif + +// GIT SHA2 identifier. +// +#ifndef IDENTIFIER +#define IDENTIFIER 0 +#endif +#ifdef SHORTID +#define SHORTIDSTR "-" SHORTID +#else +#define SHORTIDSTR "" +#define SHORTID 0 +#endif + +// Compilation flags. +// +#ifndef FLAGS +#define FLAGS 0 +#endif + +// Build Time and operating system. +// +#ifndef DATE +#define DATE __DATE__ " " __TIME__ +#endif + +/*------------------------------------------------------------------------*/ + +#include "version.hpp" + +namespace CaDiCaL { + +const char *version () { return VERSION; } +const char *copyright () { return COPYRIGHT; } +const char *authors () { return AUTHORS; } +const char *affiliations () { return AFFILIATIONS; } +const char *signature () { return "cadical-" VERSION SHORTIDSTR; } +const char *identifier () { return IDENTIFIER; } +const char *compiler () { return COMPILER; } +const char *date () { return DATE; } +const char *flags () { return FLAGS; } + +} // namespace CaDiCaL diff --git a/src/sat/cadical/version.hpp b/src/sat/cadical/version.hpp new file mode 100644 index 000000000..aaa07788e --- /dev/null +++ b/src/sat/cadical/version.hpp @@ -0,0 +1,13 @@ +namespace CaDiCaL { + +const char *version (); +const char *copyright (); +const char *authors (); +const char *affiliations (); +const char *signature (); +const char *identifier (); +const char *compiler (); +const char *date (); +const char *flags (); + +} // namespace CaDiCaL diff --git a/src/sat/cadical/vivify.cpp b/src/sat/cadical/vivify.cpp new file mode 100644 index 000000000..cf6ec1fb8 --- /dev/null +++ b/src/sat/cadical/vivify.cpp @@ -0,0 +1,1948 @@ +#include "vivify.hpp" +#include "internal.hpp" +#include "util.hpp" +#include <algorithm> +#include <limits> +#include <utility> + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Vivification is a special case of asymmetric tautology elimination (ATE) +// and asymmetric literal elimination (ALE). It strengthens and removes +// clauses proven redundant through unit propagation. +// +// The original algorithm is due to a paper by Piette, Hamadi and Sais +// published at ECAI'08. We have an inprocessing version, e.g., it does not +// necessarily run-to-completion. Our version also performs conflict +// analysis and uses a new heuristic for selecting clauses to vivify. + +// Our idea is to focus on clauses with many occurrences of its literals in +// other clauses first. This both complements nicely our implementation of +// subsume, which is bounded, e.g., subsumption attempts are skipped for +// very long clauses with literals with many occurrences and also is +// stronger in the sense that it enables to remove more clauses due to unit +// propagation (AT checks). + +// While first focusing on irredundant clause we then added a separate phase +// upfront which focuses on strengthening also redundant clauses in spirit +// of the ideas presented in the IJCAI'17 paper by M. Luo, C.-M. Li, F. +// Xiao, F. Manya, and Z. Lu. + +// There is another very similar approach called 'distilliation' published +// by Han and Somenzi in DAC'07, which reorganizes the CNF in a trie data +// structure to reuse decisions and propagations along the trie. We used +// that as an inspiration but instead of building a trie we simple sort +// clauses and literals in such a way that we get the same effect. If a new +// clause is 'distilled' or 'vivified' we first check how many of the +// decisions (which are only lazily undone) can be reused for that clause. +// Reusing can be improved by picking a global literal order and sorting the +// literals in all clauses with respect to that order. We favor literals +// with more occurrences first. Then we sort clauses lexicographically with +// respect to that literal order. + +/*------------------------------------------------------------------------*/ + +// Candidate clause 'subsumed' is subsumed by 'subsuming'. + +inline void Internal::vivify_subsume_clause (Clause *subsuming, + Clause *subsumed) { + stats.subsumed++; + stats.vivifysubs++; +#ifndef NDEBUG + assert (subsuming); + assert (subsumed); + assert (subsuming != subsumed); + assert (!subsumed->garbage); + // size after removeing units; + int real_size_subsuming = 0, real_size_subsumed = 0; + for (auto lit : *subsuming) { + if (!val (lit) || var (lit).level) + ++real_size_subsuming; + else + assert (val (lit) < 0); + } + for (auto lit : *subsumed) { + if (!val (lit) || var (lit).level) + ++real_size_subsumed; + else + assert (val (lit) < 0); + } + assert (real_size_subsuming <= real_size_subsumed); +#endif + LOG (subsumed, "subsumed"); + if (subsumed->redundant) { + stats.subred++; + ++stats.vivifysubred; + } else { + stats.subirr++; + ++stats.vivifysubirr; + } + if (subsuming->garbage) { + assert (subsuming->size == 2); + LOG (subsuming, + "binary subsuming clause was already deleted, so undeleting"); + subsuming->garbage = false; + subsuming->glue = 1; + ++stats.current.total; + if (subsuming->redundant) + stats.current.redundant++; + else + stats.current.irredundant++, stats.irrlits += subsuming->size; + } + if (subsumed->redundant || !subsuming->redundant) { + mark_garbage (subsumed); + return; + } + LOG ("turning redundant subsuming clause into irredundant clause"); + subsuming->redundant = false; + if (proof) + proof->strengthen (subsuming->id); + mark_garbage (subsumed); + mark_added (subsuming); + stats.current.irredundant++; + stats.added.irredundant++; + stats.irrlits += subsuming->size; + assert (stats.current.redundant > 0); + stats.current.redundant--; + assert (stats.added.redundant > 0); + stats.added.redundant--; + // ... and keep 'stats.added.total'. +} + +// demoting a clause (opposite is promote from subsume.cpp) + +inline void Internal::demote_clause (Clause *c) { + stats.subsumed++; + stats.vivifydemote++; + LOG (c, "demoting"); + assert (!c->redundant); + mark_removed (c); + c->redundant = true; + assert (stats.current.irredundant > 0); + stats.current.irredundant--; + assert (stats.added.irredundant > 0); + stats.added.irredundant--; + stats.irrlits -= c->size; + stats.current.redundant++; + stats.added.redundant++; + c->glue = c->size - 1; + // ... and keep 'stats.added.total'. +} + +/*------------------------------------------------------------------------*/ +// For vivification we have a separate dedicated propagation routine, which +// prefers to propagate binary clauses first. It also uses its own +// assignment procedure 'vivify_assign', which does not mess with phase +// saving during search nor the conflict and other statistics and further +// can be inlined separately here. The propagation routine needs to ignore +// (large) clauses which are currently vivified. + +inline void Internal::vivify_assign (int lit, Clause *reason) { + require_mode (VIVIFY); + const int idx = vidx (lit); + assert (!vals[idx]); + assert (!flags (idx).eliminated () || !reason); + Var &v = var (idx); + v.level = level; // required to reuse decisions + v.trail = (int) trail.size (); // used in 'vivify_better_watch' + 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; + assert (val (lit) > 0); + assert (val (-lit) < 0); + trail.push_back (lit); + LOG (reason, "vivify assign %d", lit); +} + +// Assume negated literals in candidate clause. + +void Internal::vivify_assume (int lit) { + require_mode (VIVIFY); + level++; + control.push_back (Level (lit, trail.size ())); + LOG ("vivify decide %d", lit); + assert (level > 0); + assert (propagated == trail.size ()); + vivify_assign (lit, 0); +} + +// Dedicated routine similar to 'propagate' in 'propagate.cpp' and +// 'probe_propagate' with 'probe_propagate2' in 'probe.cpp'. Please refer +// to that code for more explanation on how propagation is implemented. + +bool Internal::vivify_propagate (int64_t &ticks) { + require_mode (VIVIFY); + assert (!unsat); + START (propagate); + int64_t before = propagated2 = propagated; + for (;;) { + if (propagated2 != trail.size ()) { + const int lit = -trail[propagated2++]; + LOG ("vivify propagating %d over binary clauses", -lit); + Watches &ws = watches (lit); + ticks += + 1 + cache_lines (ws.size (), sizeof (const_watch_iterator *)); + for (const auto &w : ws) { + if (!w.binary ()) + continue; + const signed char b = val (w.blit); + if (b > 0) + continue; + if (b < 0) + conflict = w.clause; // but continue + else { + ticks++; + build_chain_for_units (w.blit, w.clause, 0); + vivify_assign (w.blit, w.clause); + lrat_chain.clear (); + } + } + } else if (!conflict && propagated != trail.size ()) { + const int lit = -trail[propagated++]; + LOG ("vivify propagating %d over large clauses", -lit); + Watches &ws = watches (lit); + const const_watch_iterator eow = ws.end (); + const_watch_iterator i = ws.begin (); + ticks += 1 + cache_lines (ws.size (), sizeof (*i)); + watch_iterator j = ws.begin (); + while (i != eow) { + const Watch w = *j++ = *i++; + if (w.binary ()) + continue; + if (val (w.blit) > 0) + continue; + ticks++; + if (w.clause->garbage) { + j--; + continue; + } + literal_iterator lits = w.clause->begin (); + const int other = lits[0] ^ lits[1] ^ lit; + const signed char u = val (other); + if (u > 0) + j[-1].blit = other; + else { + const int size = w.clause->size; + const const_literal_iterator end = lits + size; + const literal_iterator middle = lits + w.clause->pos; + literal_iterator k = middle; + signed char v = -1; + int r = 0; + while (k != end && (v = val (r = *k)) < 0) + k++; + if (v < 0) { + k = lits + 2; + assert (w.clause->pos <= size); + while (k != middle && (v = val (r = *k)) < 0) + k++; + } + w.clause->pos = k - lits; + assert (lits + 2 <= k), assert (k <= w.clause->end ()); + if (v > 0) + j[-1].blit = r; + else if (!v) { + LOG (w.clause, "unwatch %d in", r); + lits[0] = other; + lits[1] = r; + *k = lit; + ticks++; + watch_literal (r, lit, w.clause); + j--; + } else if (!u) { + if (w.clause == ignore) { + LOG ("ignoring propagation due to clause to vivify"); + continue; + } + ticks++; + assert (v < 0); + vivify_chain_for_units (other, w.clause); + vivify_assign (other, w.clause); + lrat_chain.clear (); + } else { + if (w.clause == ignore) { + LOG ("ignoring conflict due to clause to vivify"); + continue; + } + assert (u < 0); + assert (v < 0); + conflict = w.clause; + break; + } + } + } + if (j != i) { + while (i != eow) + *j++ = *i++; + ws.resize (j - ws.begin ()); + } + } else + break; + } + int64_t delta = propagated2 - before; + stats.propagations.vivify += delta; + if (conflict) + LOG (conflict, "conflict"); + STOP (propagate); + return !conflict; +} + +/*------------------------------------------------------------------------*/ + +// Check whether a literal occurs less often. In the implementation below +// (search for 'int64_t score = ...' or '@4') we actually compute a +// weighted occurrence count similar to the Jeroslow Wang heuristic. + +struct vivify_more_noccs { + + Internal *internal; + + vivify_more_noccs (Internal *i) : internal (i) {} + + bool operator() (int a, int b) { + int64_t n = internal->noccs (a); + int64_t m = internal->noccs (b); + if (n > m) + return true; // larger occurrences / score first + if (n < m) + return false; // smaller occurrences / score last + if (a == -b) + return a > 0; // positive literal first + return abs (a) < abs (b); // smaller index first + } +}; + +struct vivify_more_noccs_kissat { + + Internal *internal; + + vivify_more_noccs_kissat (Internal *i) : internal (i) {} + + bool operator() (int a, int b) { + unsigned t = internal->noccs (a); + unsigned s = internal->noccs (b); + return ((t - s) | ((b - a) & ~(s - t))) >> 31; + } +}; + +// Sort candidate clauses by the number of occurrences (actually by their +// score) of their literals, with clauses to be vivified first last. We +// assume that clauses are sorted w.r.t. more occurring (higher score) +// literals first (with respect to 'vivify_more_noccs'). +// +// For example if there are the following (long irredundant) clauses +// +// 1 -3 -4 (A) +// -1 -2 3 4 (B) +// 2 -3 4 (C) +// +// then we have the following literal scores using Jeroslow Wang scores and +// normalizing it with 2^12 (which is the same as 1<<12): +// +// nocc ( 1) = 2^12 * (2^-3 ) = 512 3. +// nocc (-1) = 2^12 * (2^-4 ) = 256 6. +// nocc ( 2) = 2^12 * (2^-3 ) = 512 4. +// nocc (-2) = 2^12 * (2^-4 ) = 256 7. @1 +// nocc ( 3) = 2^12 * (2^-4 ) = 256 8. +// nocc (-3) = 2^12 * (2^-3 + 2^-3) = 1024 1. +// nocc ( 4) = 2^12 * (2^-3 + 2^-4) = 768 2. +// nocc (-4) = 2^12 * (2^-3 ) = 512 5. +// +// which gives the literal order (according to 'vivify_more_noccs') +// +// -3, 4, 1, 2, -4, -1, -2, 3 +// +// Then sorting the literals in each clause gives +// +// -3 1 -4 (A') +// 4 -1 -2 3 (B') @2 +// -3 4 2 (C') +// +// and finally sorting those clauses lexicographically w.r.t. scores is +// +// -3 4 2 (C') +// -3 1 -4 (A') @3 +// 4 -1 -2 3 (B') +// +// This order is defined by 'vivify_clause_later' which returns 'true' if +// the first clause should be vivified later than the second. + +struct vivify_clause_later { + + Internal *internal; + + vivify_clause_later (Internal *i) : internal (i) {} + + bool operator() (Clause *a, Clause *b) const { + + if (a == b) + return false; + + // First focus on clauses scheduled in the last vivify round but not + // checked yet since then. + // + if (!a->vivify && b->vivify) + return true; + if (a->vivify && !b->vivify) + return false; + + // Among redundant clauses (in redundant mode) prefer small glue. + // + if (a->redundant) { + assert (b->redundant); + if (a->glue > b->glue) + return true; + if (a->glue < b->glue) + return false; + } + + // Then prefer shorter size. + // + if (a->size > b->size) + return true; + if (a->size < b->size) + return false; + + // Now compare literals in the clauses lexicographically with respect to + // the literal order 'vivify_more_noccs' assuming literals are sorted + // decreasingly with respect to that order. + // + const auto eoa = a->end (), eob = b->end (); + auto j = b->begin (); + for (auto i = a->begin (); i != eoa && j != eob; i++, j++) + if (*i != *j) + return vivify_more_noccs (internal) (*j, *i); + + return j == eob; // Prefer shorter clauses to be vivified first. + } +}; + +/*------------------------------------------------------------------------*/ + +// Attempting on-the-fly subsumption during sorting when the last line is +// reached in 'vivify_clause_later' above turned out to be trouble some for +// identical clauses. This is the single point where 'vivify_clause_later' +// is not asymmetric and would require 'stable' sorting for determinism. It +// can also not be made 'complete' on-the-fly. Instead of on-the-fly +// subsumption we thus go over the sorted scheduled in a linear scan +// again and remove certain subsumed clauses (the subsuming clause is +// syntactically a prefix of the subsumed clause), which includes +// those troublesome syntactically identical clauses. + +struct vivify_flush_smaller { + + bool operator() (Clause *a, Clause *b) const { + + const auto eoa = a->end (), eob = b->end (); + auto i = a->begin (), j = b->begin (); + for (; i != eoa && j != eob; i++, j++) + if (*i != *j) + return *i < *j; + + return j == eob && i != eoa; + } +}; + +void Internal::flush_vivification_schedule (std::vector<Clause *> &schedule, + int64_t &ticks) { + ticks += 1 + 3 * cache_lines (schedule.size (), sizeof (Clause *)); + stable_sort (schedule.begin (), schedule.end (), vivify_flush_smaller ()); + + const auto end = schedule.end (); + auto j = schedule.begin (), i = j; + + Clause *prev = 0; + int64_t subsumed = 0; + for (; i != end; i++) { + ticks++; + Clause *c = *j++ = *i; + if (!prev || c->size < prev->size) { + prev = c; + continue; + } + const auto eop = prev->end (); + auto k = prev->begin (); + for (auto l = c->begin (); k != eop; k++, l++) + if (*k != *l) + break; + if (k == eop) { + LOG (c, "found subsumed"); + LOG (prev, "subsuming"); + assert (!c->garbage); + assert (!prev->garbage); + assert (c->redundant || !prev->redundant); + mark_garbage (c); + subsumed++; + j--; + } else + prev = c; + } + + if (subsumed) + PHASE ("vivify", stats.vivifications, + "flushed %" PRId64 " subsumed scheduled clauses", subsumed); + + stats.vivifysubs += subsumed; + + if (subsumed) { + schedule.resize (j - schedule.begin ()); + shrink_vector (schedule); + } else + assert (j == end); +} + +/*------------------------------------------------------------------------*/ + +// Depending on whether we try to vivify redundant or irredundant clauses, +// we schedule a clause to be vivified. For redundant clauses we initially +// only try to vivify them if they are likely to survive the next 'reduce' +// operation, but this left the last schedule empty most of the time. + +bool Internal::consider_to_vivify_clause (Clause *c) { + if (c->garbage) + return false; + if (opts.vivifyonce >= 1 && c->redundant && c->vivified) + return false; + if (opts.vivifyonce >= 2 && !c->redundant && c->vivified) + return false; + if (!c->redundant) + return true; + assert (c->redundant); + + // likely_to_be_kept_clause is too aggressive at removing tier-3 clauses + return true; +} + +/*------------------------------------------------------------------------*/ + +// In a strengthened clause the idea is to move non-false literals to the +// front, followed by false literals. Literals are further sorted by +// reverse assignment order. The goal is to use watches which require to +// backtrack as few as possible decision levels. + +struct vivify_better_watch { + + Internal *internal; + + vivify_better_watch (Internal *i) : internal (i) {} + + bool operator() (int a, int b) { + + const signed char av = internal->val (a), bv = internal->val (b); + + if (av >= 0 && bv < 0) + return true; + if (av < 0 && bv >= 0) + return false; + + return internal->var (a).trail > internal->var (b).trail; + } +}; + +// Common code to actually strengthen a candidate clause. The resulting +// strengthened clause is communicated through the global 'clause'. + +void Internal::vivify_strengthen (Clause *c) { + + assert (!clause.empty ()); + + if (clause.size () == 1) { + + backtrack_without_updating_phases (); + const int unit = clause[0]; + LOG (c, "vivification shrunken to unit %d", unit); + assert (!val (unit)); + assign_unit (unit); + // lrat_chain.clear (); done in search_assign + stats.vivifyunits++; + + bool ok = propagate (); + if (!ok) + learn_empty_clause (); + + } else { + + // See explanation before 'vivify_better_watch' above. + // + sort (clause.begin (), clause.end (), vivify_better_watch (this)); + + int new_level = level; + + const int lit0 = clause[0]; + signed char val0 = val (lit0); + if (val0 < 0) { + const int level0 = var (lit0).level; + LOG ("1st watch %d negative at level %d", lit0, level0); + new_level = level0 - 1; + } + + const int lit1 = clause[1]; + const signed char val1 = val (lit1); + if (val1 < 0 && !(val0 > 0 && var (lit0).level <= var (lit1).level)) { + const int level1 = var (lit1).level; + LOG ("2nd watch %d negative at level %d", lit1, level1); + new_level = level1 - 1; + } + + assert (new_level >= 0); + if (new_level < level) + backtrack (new_level); + + assert (val (lit0) >= 0); + assert (val (lit1) >= 0 || (val (lit0) > 0 && val (lit1) < 0 && + var (lit0).level <= var (lit1).level)); + + Clause *d = new_clause_as (c); + LOG (c, "before vivification"); + LOG (d, "after vivification"); + (void) d; + } + clause.clear (); + mark_garbage (c); + lrat_chain.clear (); + ++stats.vivifystrs; +} + +void Internal::vivify_sort_watched (Clause *c) { + + sort (c->begin (), c->end (), vivify_better_watch (this)); + + int new_level = level; + + const int lit0 = c->literals[0]; + signed char val0 = val (lit0); + if (val0 < 0) { + const int level0 = var (lit0).level; + LOG ("1st watch %d negative at level %d", lit0, level0); + new_level = level0 - 1; + } + + const int lit1 = c->literals[1]; + const signed char val1 = val (lit1); + if (val1 < 0 && !(val0 > 0 && var (lit0).level <= var (lit1).level)) { + const int level1 = var (lit1).level; + LOG ("2nd watch %d negative at level %d", lit1, level1); + new_level = level1 - 1; + } + + assert (new_level >= 0); + if (new_level < level) + backtrack (new_level); + + assert (val (lit0) >= 0); + assert (val (lit1) >= 0 || (val (lit0) > 0 && val (lit1) < 0 && + var (lit0).level <= var (lit1).level)); +} +// Conflict analysis from 'start' which learns a decision only clause. +// +// We cannot use the stack-based implementation of Kissat, because we need +// to iterate over the conflict in topological ordering to produce a valid +// LRAT proof + +void Internal::vivify_analyze (Clause *start, bool &subsumes, + Clause **subsuming, + const Clause *const candidate, int implied, + bool &redundant) { + const auto &t = &trail; // normal trail, so next_trail is wrong + int i = t->size (); // Start at end-of-trail. + Clause *reason = start; + assert (reason); + assert (!trail.empty ()); + int uip = trail.back (); + bool mark_implied = (implied); + + while (i >= 0) { + if (reason) { + redundant = (redundant || reason->redundant); + subsumes = (start != reason && reason->size <= start->size); + LOG (reason, "resolving on %d with", uip); + for (auto other : *reason) { + const Var v = var (other); + Flags &f = flags (other); + if (!marked2 (other) && v.level) { + LOG ("not subsuming due to lit %d", other); + subsumes = false; + } + if (!val (other)) { + LOG ("skipping unset lit %d", other); + continue; + } + if (other == uip) { + continue; + } + if (!v.level) { + if (f.seen || !lrat || reason == start) + continue; + LOG ("unit reason for %d", other); + int64_t id = unit_id (-other); + LOG ("adding unit reason %zd for %d", id, other); + unit_chain.push_back (id); + f.seen = true; + analyzed.push_back (other); + continue; + } + if (mark_implied && other != implied) { + LOG ("skipping non-implied literal %d on current level", other); + continue; + } + + assert (val (other)); + if (f.seen) + continue; + LOG ("pushing lit %d", other); + analyzed.push_back (other); + f.seen = true; + } + if (start->redundant) { + const int new_glue = recompute_glue (start); + promote_clause (start, new_glue); + } + if (subsumes) { + assert (reason); + LOG (reason, "clause found subsuming"); + LOG (candidate, "clause found subsumed"); + *subsuming = reason; + return; + } + } else { + LOG ("vivify analyzed decision %d", uip); + clause.push_back (-uip); + } + mark_implied = false; + + uip = 0; + while (!uip && i > 0) { + assert (i > 0); + const int lit = (*t)[--i]; + if (!var (lit).level) + continue; + if (flags (lit).seen) + uip = lit; + } + if (!uip) + break; + LOG ("uip is %d", uip); + Var &w = var (uip); + reason = w.reason; + if (lrat && reason) + lrat_chain.push_back (reason->id); + } + (void) candidate; +} + +void Internal::vivify_deduce (Clause *candidate, Clause *conflict, + int implied, Clause **subsuming, + bool &redundant) { + assert (lrat_chain.empty ()); + bool subsumes; + Clause *reason; + + assert (clause.empty ()); + if (implied) { + reason = candidate; + mark2 (candidate); + const int not_implied = -implied; + assert (var (not_implied).level); + Flags &f = flags (not_implied); + f.seen = true; + LOG ("pushing implied lit %d", not_implied); + analyzed.push_back (not_implied); + clause.push_back (implied); + } else { + reason = (conflict ? conflict : candidate); + assert (reason); + assert (!reason->garbage); + mark2 (candidate); + subsumes = (candidate != reason); + redundant = reason->redundant; + LOG (reason, "resolving with"); + if (lrat) + lrat_chain.push_back (reason->id); + for (auto lit : *reason) { + const Var &v = var (lit); + Flags &f = flags (lit); + assert (val (lit) < 0); + if (!v.level) { + if (!lrat) + continue; + LOG ("adding unit %d", lit); + if (!f.seen) { + // nevertheless we can use var (l) as if l was still assigned + // because var is updated lazily + int64_t id = unit_id (-lit); + LOG ("adding unit reason %zd for %d", id, lit); + unit_chain.push_back (id); + } + f.seen = true; + analyzed.push_back (lit); + continue; + } + assert (v.level); + if (!marked2 (lit)) { + LOG ("lit %d is not marked", lit); + subsumes = false; + } + LOG ("analyzing lit %d", lit); + LOG ("pushing lit %d", lit); + analyzed.push_back (lit); + f.seen = true; + } + if (reason != candidate && reason->redundant) { + const int new_glue = recompute_glue (reason); + promote_clause (reason, new_glue); + } + if (subsumes) { + assert (candidate != reason); +#ifndef NDEBUG + int nonfalse_reason = 0; + for (auto lit : *reason) + if (!fixed (lit)) + ++nonfalse_reason; + + int nonfalse_candidate = 0; + for (auto lit : *candidate) + if (!fixed (lit)) + ++nonfalse_candidate; + + assert (nonfalse_reason <= nonfalse_candidate); +#endif + LOG (candidate, "vivify subsumed 0"); + LOG (reason, "vivify subsuming 0"); + *subsuming = reason; + unmark (candidate); + if (lrat) + lrat_chain.clear (); + return; + } + } + + vivify_analyze (reason, subsumes, subsuming, candidate, implied, + redundant); + unmark (candidate); + if (subsumes) { + assert (*subsuming); + LOG (candidate, "vivify subsumed"); + LOG (*subsuming, "vivify subsuming"); + if (lrat) + lrat_chain.clear (); + } +} +/*------------------------------------------------------------------------*/ + +bool Internal::vivify_shrinkable (const std::vector<int> &sorted, + Clause *conflict) { + + unsigned count_implied = 0; + for (auto lit : sorted) { + const signed char value = val (lit); + if (!value) { + LOG ("vivification unassigned %d", lit); + return true; + } + if (value > 0) { + LOG ("vivification implied satisfied %d", lit); + if (conflict) + return true; + if (count_implied++) { + LOG ("at least one implied literal with conflict thus shrinking"); + return true; + } + } else { + assert (value < 0); + const Var &v = var (lit); + const Flags &f = flags (lit); + if (!v.level) + continue; + if (!f.seen) { + LOG ("vivification non-analyzed %d", lit); + return true; + } + if (v.reason) { + LOG ("vivification implied falsified %d", lit); + return true; + } + } + } + return false; +} +/*------------------------------------------------------------------------*/ + +inline void Internal::vivify_increment_stats (const Vivifier &vivifier) { + switch (vivifier.tier) { + case Vivify_Mode::TIER1: + ++stats.vivifystred1; + break; + case Vivify_Mode::TIER2: + ++stats.vivifystred2; + break; + case Vivify_Mode::TIER3: + ++stats.vivifystred3; + break; + default: + assert (vivifier.tier == Vivify_Mode::IRREDUNDANT); + ++stats.vivifystrirr; + break; + } +} +/*------------------------------------------------------------------------*/ +// instantiate last literal (see the description of the hack track 2023), +// fix the watches and +// backtrack two level back +bool Internal::vivify_instantiate ( + const std::vector<int> &sorted, Clause *c, + std::vector<std::tuple<int, Clause *, bool>> &lrat_stack, + int64_t &ticks) { + LOG ("now trying instantiation"); + conflict = nullptr; + const int lit = sorted.back (); + LOG ("vivify instantiation"); + assert (!var (lit).reason); + assert (var (lit).level); + assert (val (lit)); + backtrack (level - 1); + assert (val (lit) == 0); + stats.vivifydecs++; + vivify_assume (lit); + bool ok = vivify_propagate (ticks); + if (!ok) { + LOG (c, "instantiate success with literal %d in", lit); + stats.vivifyinst++; + // strengthen clause + if (lrat) { + clear_analyzed_literals (); + assert (lrat_chain.empty ()); + vivify_build_lrat (0, c, lrat_stack); + vivify_build_lrat (0, conflict, lrat_stack); + clear_analyzed_literals (); + } + int remove = lit; + conflict = nullptr; + unwatch_clause (c); + backtrack_without_updating_phases (level - 2); + strengthen_clause (c, remove); + vivify_sort_watched (c); + watch_clause (c); + assert (!conflict); + return true; + } else { + LOG ("vivify instantiation failed"); + return false; + } +} + +/*------------------------------------------------------------------------*/ + +// Main function: try to vivify this candidate clause in the given mode. + +bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { + + assert (c->size > 2); // see (NO-BINARY) below + assert (analyzed.empty ()); + + c->vivify = false; // mark as checked / tried + c->vivified = true; // and globally remember + + assert (!c->garbage); + + auto &lrat_stack = vivifier.lrat_stack; + auto &ticks = vivifier.ticks; + ticks++; + + // First check whether the candidate clause is already satisfied and at + // the same time copy its non fixed literals to 'sorted'. The literals + // in the candidate clause might not be sorted anymore due to replacing + // watches during propagation, even though we sorted them initially + // while pushing the clause onto the schedule and sorting the schedule. + // + auto &sorted = vivifier.sorted; + sorted.clear (); + + for (const auto &lit : *c) { + const int tmp = fixed (lit); + if (tmp > 0) { + LOG (c, "satisfied by propagated unit %d", lit); + mark_garbage (c); + return false; + } else if (!tmp) + sorted.push_back (lit); + } + + assert (sorted.size () > 1); + if (sorted.size () == 2) { + LOG ("skipping actual binary"); + return false; + } + + sort (sorted.begin (), sorted.end (), vivify_more_noccs_kissat (this)); + + // The actual vivification checking is performed here, by assuming the + // negation of each of the remaining literals of the clause in turn and + // propagating it. If a conflict occurs or another literal in the + // clause becomes assigned during propagation, we can stop. + // + LOG (c, "vivification checking"); + stats.vivifychecks++; + + // If the decision 'level' is non-zero, then we can reuse decisions for + // the previous candidate, and avoid re-propagating them. In preliminary + // experiments this saved between 30%-50% decisions (and thus + // propagations), which in turn lets us also vivify more clauses within + // the same propagation bounds, or terminate earlier if vivify runs to + // completion. + // + if (level) { +#ifdef LOGGING + int orig_level = level; +#endif + // First check whether this clause is actually a reason for forcing + // one of its literals to true and then backtrack one level before + // that happened. Otherwise this clause might be incorrectly + // considered to be redundant or if this situation is checked then + // redundancy by other clauses using this forced literal becomes + // impossible. + // + int forced = 0; + + // This search could be avoided if we would eagerly set the 'reason' + // boolean flag of clauses, which however we do not want to do for + // binary clauses (during propagation) and thus would still require + // a version of 'protect_reason' for binary clauses during 'reduce' + // (well binary clauses are not collected during 'reduce', but again + // this exception from the exception is pretty complex and thus a + // simply search here is probably easier to understand). + + for (const auto &lit : *c) { + const signed char tmp = val (lit); + if (tmp < 0) + continue; + if (tmp > 0 && var (lit).reason == c) + forced = lit; + break; + } + if (forced) { + LOG ("clause is reason forcing %d", forced); + assert (var (forced).level); + backtrack_without_updating_phases (var (forced).level - 1); + } + + // As long the (remaining) literals of the sorted clause match + // decisions on the trail we just reuse them. + // + if (level) { + + int l = 1; // This is the decision level we want to reuse. + + for (const auto &lit : sorted) { + assert (!fixed (lit)); + const int decision = control[l].decision; + if (-lit == decision) { + LOG ("reusing decision %d at decision level %d", decision, l); + stats.vivifyreused++; + if (++l > level) + break; + } else { + LOG ("literal %d does not match decision %d at decision level %d", + lit, decision, l); + backtrack_without_updating_phases (l - 1); + break; + } + } + } + + LOG ("reused %d decision levels from %d", level, orig_level); + } + + LOG (sorted, "sorted size %zd probing schedule", sorted.size ()); + + // Make sure to ignore this clause during propagation. This is not that + // easy for binary clauses (NO-BINARY), e.g., ignoring binary clauses, + // without changing 'propagate'. Actually, we do not want to remove binary + // clauses which are subsumed. Those are hyper binary resolvents and + // should be kept as learned clauses instead, unless they are transitive + // in the binary implication graph, which in turn is detected during + // transitive reduction in 'transred'. + // + ignore = c; + + int subsume = 0; // determined to be redundant / subsumed + + // If the candidate is redundant, i.e., we are in redundant mode, the + // clause is subsumed (in one of the two cases below where 'subsume' is + // assigned) and further all reasons involved are only binary clauses, + // then this redundant clause is what we once called a hidden tautology, + // and even for redundant clauses it makes sense to remove the candidate. + // It does not add anything to propagation power of the formula. This is + // the same argument as removing transitive clauses in the binary + // implication graph during transitive reduction. + // + + // Go over the literals in the candidate clause in sorted order. + // + for (const auto &lit : sorted) { + + // Exit loop as soon a literal is positively implied (case '@5' below) + // or propagation of the negation of a literal fails ('@6'). + // + if (subsume) + break; + + // We keep on assigning literals, even though we know already that we + // can remove one (was negatively implied), since we either might run + // into the 'subsume' case above or more false literals become implied. + // In any case this might result in stronger vivified clauses. As a + // consequence continue with this loop even if 'remove' is non-zero. + + const signed char tmp = val (lit); + + if (tmp) { // literal already assigned + + const Var &v = var (lit); + assert (v.level); + if (!v.reason) { + LOG ("skipping decision %d", lit); + continue; + } + + if (tmp < 0) { + assert (v.level); + LOG ("literal %d is already false and can be removed", lit); + continue; + } + + assert (tmp > 0); + LOG ("subsumed since literal %d already true", lit); + subsume = lit; // will be able to subsume candidate '@5' + break; + } + + assert (!tmp); + + stats.vivifydecs++; + vivify_assume (-lit); + LOG ("negated decision %d score %" PRId64 "", lit, noccs (lit)); + + if (!vivify_propagate (ticks)) { + break; // hot-spot + } + } + + if (subsume) { + int better_subsume_trail = var (subsume).trail; + for (auto lit : sorted) { + if (val (lit) <= 0) + continue; + const Var v = var (lit); + if (v.trail < better_subsume_trail) { + LOG ("improving subsume from %d at %d to %d at %d", subsume, + better_subsume_trail, lit, v.trail); + better_subsume_trail = v.trail; + subsume = lit; + } + } + } + + Clause *subsuming = nullptr; + bool redundant = false; + const int level_after_assumptions = level; + assert (level_after_assumptions); + vivify_deduce (c, conflict, subsume, &subsuming, redundant); + + bool res; + + // reverse lrat_chain. We could probably work with reversed iterators + // (views) to be more efficient but we would have to distinguish in proof + // + if (lrat) { + for (auto id : unit_chain) + lrat_chain.push_back (id); + unit_chain.clear (); + reverse (lrat_chain.begin (), lrat_chain.end ()); + } + + if (subsuming) { + assert (c != subsuming); + LOG (c, "deleting subsumed clause"); + if (c->redundant && subsuming->redundant && c->glue < subsuming->glue) { + promote_clause (c, c->glue); + } + vivify_subsume_clause (subsuming, c); + res = false; + // stats.vivifysubs++; // already done in vivify_subsume_clause + } else if (vivify_shrinkable (sorted, conflict)) { + vivify_increment_stats (vivifier); + LOG ("vivify succeeded, learning new clause"); + clear_analyzed_literals (); + LOG (lrat_chain, "lrat"); + LOG (clause, "learning clause"); + conflict = nullptr; // TODO dup from below + vivify_strengthen (c); + res = true; + } else if (subsume && c->redundant) { + LOG (c, "vivification implied"); + mark_garbage (c); + ++stats.vivifyimplied; + res = true; + } else if ((conflict || subsume) && !c->redundant && !redundant) { + LOG ("demote clause from irredundant to redundant"); + if (opts.vivifydemote) { + demote_clause (c); + const int new_glue = recompute_glue (c); + promote_clause (c, new_glue); + res = false; + } else { + mark_garbage (c); + ++stats.vivifyimplied; + res = true; + } + } else if (subsume) { + LOG (c, "no vivification instantiation with implied literal %d", + subsume); + assert (!c->redundant); + assert (redundant); + res = false; + ++stats.vivifyimplied; + } else { + assert (level > 2); + assert ((size_t) level == sorted.size ()); + LOG (c, "vivification failed on"); + lrat_chain.clear (); + assert (!subsume); + if (!subsume && opts.vivifyinst) { + res = vivify_instantiate (sorted, c, lrat_stack, ticks); + assert (!conflict); + } else { + LOG ("cannot apply instantiation"); + res = false; + } + } + + if (conflict && level == level_after_assumptions) { + LOG ("forcing backtracking at least one level after conflict"); + backtrack_without_updating_phases (level - 1); + } + + clause.clear (); + clear_analyzed_literals (); // TODO why needed? + lrat_chain.clear (); + conflict = nullptr; + return res; +} + +// when we can strengthen clause c we have to build lrat. +// uses f.seen so do not forget to clear flags afterwards. +// this can happen in three cases. (1), (2) are only sound in redundant mode +// (1) literal l in c is positively implied. in this case we call the +// function with (l, l.reason). This justifies the reduction because the new +// clause c' will include l and all decisions so l.reason is a conflict +// assuming -c' (2) conflict during vivify propagation. function is called +// with (0, conflict) similar to (1) but more direct. (3) some literals in c +// are negatively implied and can therefore be removed. in this case we call +// the function with (0, c). originally we justified each literal in c on +// its own but this is not actually necessary. +// + +// Non-recursive version, as some bugs have been found. DFS over the +// reasons with preordering (aka we explore the entire reason before +// exploring deeper) +void Internal::vivify_build_lrat ( + int lit, Clause *reason, + std::vector<std::tuple<int, Clause *, bool>> &stack) { + assert (stack.empty ()); + stack.push_back ({lit, reason, false}); + while (!stack.empty ()) { + int lit; + Clause *reason; + bool finished; + std::tie (lit, reason, finished) = stack.back (); + LOG ("VIVIFY LRAT justifying %d", lit); + stack.pop_back (); + if (lit && flags (lit).seen) { + LOG ("skipping already justified"); + continue; + } + if (finished) { + lrat_chain.push_back (reason->id); + if (lit && reason) { + Flags &f = flags (lit); + f.seen = true; + analyzed.push_back (lit); // assert (val (other) < 0); + assert (flags (lit).seen); + } + continue; + } else + stack.push_back ({lit, reason, true}); + for (const auto &other : *reason) { + if (other == lit) + continue; + Var &v = var (other); + Flags &f = flags (other); + if (f.seen) + continue; + if (!v.level) { + const int64_t id = unit_id (-other); + lrat_chain.push_back (id); + f.seen = true; + analyzed.push_back (other); + continue; + } + if (v.reason) { // recursive justification + LOG ("VIVIFY LRAT pushing %d", other); + stack.push_back ({other, v.reason, false}); + } + } + } + stack.clear (); +} + +// calculate lrat_chain +// +inline void Internal::vivify_chain_for_units (int lit, Clause *reason) { + if (!lrat) + return; + // LOG ("building chain for units"); bad line for debugging + // equivalence if (opts.chrono && assignment_level (lit, reason)) return; + if (level) + return; // not decision level 0 + assert (lrat_chain.empty ()); + for (auto &reason_lit : *reason) { + if (lit == reason_lit) + continue; + assert (val (reason_lit)); + const int signed_reason_lit = val (reason_lit) * reason_lit; + int64_t id = unit_id (signed_reason_lit); + lrat_chain.push_back (id); + } + lrat_chain.push_back (reason->id); +} + +vivify_ref create_ref (Internal *internal, Clause *c) { + LOG (c, "creating vivify_refs of clause"); + vivify_ref ref; + ref.clause = c; + ref.size = c->size; + for (int i = 0; i < COUNTREF_COUNTS; ++i) + ref.count[i] = 0; + ref.vivify = c->vivify; + int lits[COUNTREF_COUNTS] = {0}; + for (int i = 0; i != std::min (COUNTREF_COUNTS, c->size); ++i) { + int best = 0; + unsigned best_count = 0; + for (auto lit : *c) { + LOG ("to find best number of occurrences for literal %d, looking at " + "literal %d", + i, lit); + for (int j = 0; j != i; ++j) { + LOG ("comparing %d with literal %d", lit, lits[j]); + if (lits[j] == lit) + goto CONTINUE_WITH_NEXT_LITERAL; + } + { + const int64_t lit_count = internal->noccs (lit); + assert (lit_count); + LOG ("checking literal %d with %zd occurrences", lit, lit_count); + if (lit_count <= best_count) + continue; + best_count = lit_count; + best = lit; + } + CONTINUE_WITH_NEXT_LITERAL:; + } + assert (best); + assert (best_count); + assert (best_count < UINT32_MAX); + ref.count[i] = + ((uint64_t) best_count << 32) + (uint64_t) internal->vlit (best); + LOG ("final count at position %d is %d - %d: %lu", i, best, best_count, + ref.count[i]); + lits[i] = best; + } + return ref; +} +/*------------------------------------------------------------------------*/ +inline void +Internal::vivify_prioritize_leftovers ([[maybe_unused]] char tag, + size_t prioritized, + std::vector<Clause *> &schedule) { + if (prioritized) { + PHASE ("vivify", stats.vivifications, + "[phase %c] leftovers of %" PRId64 " clause", tag, prioritized); + } else { + PHASE ("vivify", stats.vivifications, + "[phase %c] prioritizing all clause", tag); + for (auto c : schedule) + c->vivify = true; + } + const size_t max = opts.vivifyschedmax; + if (schedule.size () > max) { + if (prioritized) { + std::partition (begin (schedule), end (schedule), + [] (Clause *c) { return c->vivify; }); + } + schedule.resize (max); + } + // let's try to save a bit of memory + shrink_vector (schedule); +} + +void Internal::vivify_initialize (Vivifier &vivifier, int64_t &ticks) { + + const int tier1 = vivifier.tier1_limit; + const int tier2 = vivifier.tier2_limit; + // Count the number of occurrences of literals in all clauses, + // particularly binary clauses, which are usually responsible + // for most of the propagations. + // + init_noccs (); + + // Disconnect all watches since we sort literals within clauses. + // + assert (watching ()); +#if 0 + clear_watches (); +#endif + + size_t prioritized_irred = 0, prioritized_tier1 = 0, + prioritized_tier2 = 0, prioritized_tier3 = 0; + for (const auto &c : clauses) { + ++ticks; + if (c->size == 2) + continue; // see also (NO-BINARY) above + if (!consider_to_vivify_clause (c)) + continue; + + // This computes an approximation of the Jeroslow Wang heuristic + // score + // + // nocc (L) = sum 2^(12-|C|) + // L in C in F + // + // but we cap the size at 12, that is all clauses of size 12 and + // larger contribute '1' to the score, which allows us to use 'long' + // numbers. See the example above (search for '@1'). + // + const int shift = 12 - c->size; + const int64_t score = shift < 1 ? 1 : (1l << shift); // @4 + for (const auto lit : *c) { + noccs (lit) += score; + } + LOG (c, "putting clause in candidates"); + if (!c->redundant) + vivifier.schedule_irred.push_back (c), + prioritized_irred += (c->vivify); + else if (c->glue <= tier1) + vivifier.schedule_tier1.push_back (c), + prioritized_tier1 += (c->vivify); + else if (c->glue <= tier2) + vivifier.schedule_tier2.push_back (c), + prioritized_tier2 += (c->vivify); + else + vivifier.schedule_tier3.push_back (c), + prioritized_tier3 += (c->vivify); + ++ticks; + } + + vivify_prioritize_leftovers ('x', prioritized_irred, + vivifier.schedule_irred); + vivify_prioritize_leftovers ('u', prioritized_tier1, + vivifier.schedule_tier1); + vivify_prioritize_leftovers ('v', prioritized_tier2, + vivifier.schedule_tier2); + vivify_prioritize_leftovers ('w', prioritized_tier3, + vivifier.schedule_tier3); + + if (opts.vivifyflush) { + clear_watches (); + for (auto &sched : vivifier.schedules) { + for (const auto &c : sched) { + // Literals in scheduled clauses are sorted with their highest score + // literals first (as explained above in the example at '@2'). This + // is also needed in the prefix subsumption checking below. We do an + // approximation below that is done only in the vivify_ref structure + // below. + // + sort (c->begin (), c->end (), vivify_more_noccs (this)); + } + // Flush clauses subsumed by another clause with the same prefix, + // which also includes flushing syntactically identical clauses. + // + flush_vivification_schedule (sched, ticks); + } + connect_watches (); // watch all relevant clauses + } +#if 0 + connect_watches (); // watch all relevant clauses + vivify_propagate (ticks); +#endif + vivify_propagate (ticks); +} + +inline std::vector<vivify_ref> ¤t_refs_schedule (Vivifier &vivifier) { + switch (vivifier.tier) { + case Vivify_Mode::TIER1: + return vivifier.refs_schedule_tier1; + break; + case Vivify_Mode::TIER2: + return vivifier.refs_schedule_tier2; + break; + case Vivify_Mode::TIER3: + return vivifier.refs_schedule_tier3; + break; + default: + return vivifier.refs_schedule_irred; + break; + } + __builtin_unreachable (); +} + +inline std::vector<Clause *> ¤t_schedule (Vivifier &vivifier) { + switch (vivifier.tier) { + case Vivify_Mode::TIER1: + return vivifier.schedule_tier1; + break; + case Vivify_Mode::TIER2: + return vivifier.schedule_tier2; + break; + case Vivify_Mode::TIER3: + return vivifier.schedule_tier3; + break; + default: + return vivifier.schedule_irred; + break; + } + __builtin_unreachable (); +} + +struct vivify_refcount_rank { + int offset; + vivify_refcount_rank (int j) : offset (j) { + assert (offset < COUNTREF_COUNTS); + } + typedef uint64_t Type; + Type operator() (const vivify_ref &a) const { return a.count[offset]; } +}; + +struct vivify_refcount_smaller { + int offset; + vivify_refcount_smaller (int j) : offset (j) { + assert (offset < COUNTREF_COUNTS); + } + bool operator() (const vivify_ref &a, const vivify_ref &b) const { + const auto s = vivify_refcount_rank (offset) (a); + const auto t = vivify_refcount_rank (offset) (b); + return s < t; + } +}; + +struct vivify_inversesize_rank { + vivify_inversesize_rank () {} + typedef uint64_t Type; + Type operator() (const vivify_ref &a) const { return ~a.size; } +}; + +struct vivify_inversesize_smaller { + vivify_inversesize_smaller () {} + bool operator() (const vivify_ref &a, const vivify_ref &b) const { + const auto s = vivify_inversesize_rank () (a); + const auto t = vivify_inversesize_rank () (b); + return s < t; + } +}; + +/*------------------------------------------------------------------------*/ +// There are two modes of vivification, one using all clauses and one +// focusing on irredundant clauses only. The latter variant working on +// irredundant clauses only can also remove irredundant asymmetric +// tautologies (clauses subsumed through unit propagation), which in +// redundant mode is incorrect (due to propagating over redundant clauses). + +void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) { + + if (unsat) + return; + if (terminated_asynchronously ()) + return; + + PHASE ("vivify", stats.vivifications, + "starting %c vivification round ticks limit %" PRId64 "", + vivifier.tag, ticks_limit); + + PHASE ("vivify", stats.vivifications, + "starting %c vivification round ticks limit %" PRId64 "", + vivifier.tag, ticks_limit); + + assert (watching ()); + + auto &refs_schedule = current_refs_schedule (vivifier); + auto &schedule = current_schedule (vivifier); + + int64_t ticks = 1 + schedule.size (); + + // Sort candidates, with first to be tried candidate clause last, i.e., + // many occurrences and high score literals) as in the example explained + // above (search for '@3'). + // + if (vivifier.tier != Vivify_Mode::IRREDUNDANT || + irredundant () / 10 < redundant ()) { + // Literals in scheduled clauses are sorted with their highest score + // literals first (as explained above in the example at '@2'). This is + // also needed in the prefix subsumption checking below. We do an + // approximation below that is done only in the vivify_ref structure + // below. + // + + // first build the schedule with vivifier_refs + auto end_schedule = end (schedule); + refs_schedule.resize (schedule.size ()); + std::transform (begin (schedule), end_schedule, begin (refs_schedule), + [&] (Clause *c) { return create_ref (this, c); }); + // now sort by size + MSORT (opts.radixsortlim, refs_schedule.begin (), refs_schedule.end (), + vivify_inversesize_rank (), vivify_inversesize_smaller ()); + // now (stable) sort by number of occurrences + for (int i = 0; i < COUNTREF_COUNTS; ++i) { + const int offset = COUNTREF_COUNTS - 1 - i; + MSORT (opts.radixsortlim, refs_schedule.begin (), + refs_schedule.end (), vivify_refcount_rank (offset), + vivify_refcount_smaller (offset)); + } + // force left-overs at the end + std::stable_partition (begin (refs_schedule), end (refs_schedule), + [] (vivify_ref c) { return !c.vivify; }); + std::transform (begin (refs_schedule), end (refs_schedule), + begin (schedule), + [] (vivify_ref c) { return c.clause; }); + erase_vector (refs_schedule); + LOG ("clause after sorting final:"); + } else { + // skip sorting but still put clauses with the vivify tag at the end to + // be done first Kissat does this implicitely by going twice over all + // clauses + std::stable_partition (begin (schedule), end (schedule), + [] (Clause *c) { return !c->vivify; }); + } + + // Remember old values of counters to summarize after each round with + // verbose messages what happened in that round. + // + int64_t checked = stats.vivifychecks; + int64_t subsumed = stats.vivifysubs; + int64_t strengthened = stats.vivifystrs; + int64_t units = stats.vivifyunits; + + int64_t scheduled = schedule.size (); + stats.vivifysched += scheduled; + + PHASE ("vivify", stats.vivifications, + "scheduled %" PRId64 " clauses to be vivified %.0f%%", scheduled, + percent (scheduled, stats.current.irredundant)); + + // Limit the number of propagations during vivification as in 'probe'. + // + const int64_t limit = ticks_limit - stats.ticks.vivify; + assert (limit >= 0); + + // the clauses might still contain set literals, so propagation since the + // beginning + propagated2 = propagated = 0; + + if (!unsat && !propagate ()) { + LOG ("propagation after connecting watches in inconsistency"); + learn_empty_clause (); + } + + vivifier.ticks = ticks; + int retry = 0; + while (!unsat && !terminated_asynchronously () && !schedule.empty () && + vivifier.ticks < limit) { + Clause *c = schedule.back (); // Next candidate. + schedule.pop_back (); + if (vivify_clause (vivifier, c) && !c->garbage && c->size > 2 && + retry < opts.vivifyretry) { + ++retry; + schedule.push_back (c); + } else + retry = 0; + } + + if (level) + backtrack_without_updating_phases (); + + if (!unsat) { + int64_t still_need_to_be_vivified = schedule.size (); +#if 0 + // in the current round we have new_clauses_to_vivify @ leftovers from previous round There are + // now two possibilities: (i) we consider all clauses as leftovers, or (ii) only the leftovers + // from previous round are considered leftovers. + // + // CaDiCaL had the first version before. If + // commented out we go to the second version. + for (auto c : schedule) + c->vivify = true; +#elif 1 + // if we have gone through all the leftovers, the current clauses are + // leftovers for the next round + if (!schedule.empty () && !schedule.front ()->vivify && + schedule.back ()->vivify) + for (auto c : schedule) + c->vivify = true; +#else + // do nothing like in kissat and use the candidates for next time. +#endif + // Preference clauses scheduled but not vivified yet next time. + // + if (still_need_to_be_vivified) + PHASE ("vivify", stats.vivifications, + "still need to vivify %" PRId64 " clauses %.02f%% of %" PRId64 + " scheduled", + still_need_to_be_vivified, + percent (still_need_to_be_vivified, scheduled), scheduled); + else { + PHASE ("vivify", stats.vivifications, + "no previously not yet vivified clause left"); + } + + erase_vector (schedule); // Reclaim memory early. + } + + if (!unsat) { + + // Since redundant clause were disconnected during propagating vivified + // units in redundant mode, and further irredundant clauses are + // arbitrarily sorted, we have to propagate all literals again after + // connecting the first two literals in the clauses, in order to + // reestablish the watching invariant. + // + propagated2 = propagated = 0; + + if (!propagate ()) { + LOG ("propagating vivified units leads to conflict"); + learn_empty_clause (); + } + } + + checked = stats.vivifychecks - checked; + subsumed = stats.vivifysubs - subsumed; + strengthened = stats.vivifystrs - strengthened; + units = stats.vivifyunits - units; + + PHASE ("vivify", stats.vivifications, + "checked %" PRId64 " clauses %.02f%% of %" PRId64 + " scheduled using %" PRIu64 " ticks", + checked, percent (checked, scheduled), scheduled, vivifier.ticks); + if (units) + PHASE ("vivify", stats.vivifications, + "found %" PRId64 " units %.02f%% of %" PRId64 " checked", units, + percent (units, checked), checked); + if (subsumed) + PHASE ("vivify", stats.vivifications, + "subsumed %" PRId64 " clauses %.02f%% of %" PRId64 " checked", + subsumed, percent (subsumed, checked), checked); + if (strengthened) + PHASE ("vivify", stats.vivifications, + "strengthened %" PRId64 " clauses %.02f%% of %" PRId64 + " checked", + strengthened, percent (strengthened, checked), checked); + + stats.subsumed += subsumed; + stats.strengthened += strengthened; + stats.ticks.vivify += vivifier.ticks; + + bool unsuccessful = !(subsumed + strengthened + units); + report (vivifier.tag, unsuccessful); +} + +void set_vivifier_mode (Vivifier &vivifier, Vivify_Mode tier) { + vivifier.tier = tier; + switch (tier) { + case Vivify_Mode::TIER1: + vivifier.tag = 'u'; + break; + case Vivify_Mode::TIER2: + vivifier.tag = 'v'; + break; + case Vivify_Mode::TIER3: + vivifier.tag = 'w'; + break; + default: + assert (tier == Vivify_Mode::IRREDUNDANT); + vivifier.tag = 'x'; + break; + } +} +/*------------------------------------------------------------------------*/ + +void Internal::compute_tier_limits (Vivifier &vivifier) { + if (!opts.vivifycalctier) { + vivifier.tier1_limit = 2; + vivifier.tier2_limit = 6; + return; + } + vivifier.tier1_limit = tier1[false]; + vivifier.tier2_limit = tier2[false]; +} + +/*------------------------------------------------------------------------*/ + +bool Internal::vivify () { + + if (unsat) + return false; + if (terminated_asynchronously ()) + return false; + if (!opts.vivify) + return false; + if (!stats.current.irredundant) + return false; + if (level) + backtrack (); + assert (opts.vivify); + assert (!level); + + SET_EFFORT_LIMIT (totallimit, vivify, true); + + private_steps = true; + + START_SIMPLIFIER (vivify, VIVIFY); + stats.vivifications++; + + // the effort is normalized by dividing by sumeffort below, hence no need + // to multiply by 1e-3 (also making the precision better) + double tier1effort = !opts.vivifytier1 ? 0 : (double) opts.vivifytier1eff; + double tier2effort = !opts.vivifytier2 ? 0 : (double) opts.vivifytier2eff; + double tier3effort = !opts.vivifytier3 ? 0 : (double) opts.vivifytier3eff; + double irreffort = + delaying_vivify_irredundant.bumpreasons.delay () || !opts.vivifyirred + ? 0 + : (double) opts.vivifyirredeff; + double sumeffort = tier1effort + tier2effort + tier3effort + irreffort; + if (!stats.current.redundant) + tier1effort = tier2effort = tier3effort = 0; + if (!sumeffort) + sumeffort = irreffort = 1; + int64_t total = totallimit - stats.ticks.vivify; + + PHASE ("vivify", stats.vivifications, + "vivification limit of %" PRId64 " ticks", total); + Vivifier vivifier (Vivify_Mode::TIER1); + compute_tier_limits (vivifier); + + if (vivifier.tier1_limit == vivifier.tier2_limit) { + tier1effort += tier2effort; + tier2effort = 0; + LOG ("vivification tier1 matches tier2 " + "thus using tier2 budget for tier1"); + } + int64_t init_ticks = 0; + + // Refill the schedule every time. Unchecked clauses are 'saved' by + // setting their 'vivify' bit, such that they can be tried next time. + // + // TODO: count against ticks.vivify directly instead of this unholy + // shifting. + vivify_initialize (vivifier, init_ticks); + stats.ticks.vivify += init_ticks; + int64_t limit = stats.ticks.vivify; + const double shared_effort = (double) init_ticks / 4.0; + if (opts.vivifytier1) { + set_vivifier_mode (vivifier, Vivify_Mode::TIER1); + if (limit < stats.ticks.vivify) + limit = stats.ticks.vivify; + const double effort = (total * tier1effort) / sumeffort; + assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= + limit); + limit += effort; + if (limit - shared_effort > stats.ticks.vivify) { + limit -= shared_effort; + assert (limit >= 0); + vivify_round (vivifier, limit); + } else { + LOG ("building the schedule already used our entire ticks budget for " + "tier1"); + } + } + + if (!unsat && tier2effort) { + erase_vector ( + vivifier.schedule_tier1); // save memory (well, not really as we + // already reached the peak memory) + if (limit < stats.ticks.vivify) + limit = stats.ticks.vivify; + const double effort = (total * tier2effort) / sumeffort; + assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= + limit); + limit += effort; + if (limit - shared_effort > stats.ticks.vivify) { + limit -= shared_effort; + assert (limit >= 0); + set_vivifier_mode (vivifier, Vivify_Mode::TIER2); + vivify_round (vivifier, limit); + } else { + LOG ("building the schedule already used our entire ticks budget for " + "tier2"); + } + } + + if (!unsat && tier3effort) { + erase_vector (vivifier.schedule_tier2); + if (limit < stats.ticks.vivify) + limit = stats.ticks.vivify; + const double effort = (total * tier3effort) / sumeffort; + assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= + limit); + limit += effort; + if (limit - shared_effort > stats.ticks.vivify) { + limit -= shared_effort; + assert (limit >= 0); + set_vivifier_mode (vivifier, Vivify_Mode::TIER3); + vivify_round (vivifier, limit); + } else { + LOG ("building the schedule already used our entire ticks budget for " + "tier3"); + } + } + + if (!unsat && irreffort) { + erase_vector (vivifier.schedule_tier3); + if (limit < stats.ticks.vivify) + limit = stats.ticks.vivify; + const double effort = (total * irreffort) / sumeffort; + assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= + limit); + limit += effort; + if (limit - shared_effort > stats.ticks.vivify) { + limit -= shared_effort; + assert (limit >= 0); + set_vivifier_mode (vivifier, Vivify_Mode::IRREDUNDANT); + const int old = stats.vivifystrirr; + const int old_tried = stats.vivifychecks; + vivify_round (vivifier, limit); + if (stats.vivifychecks - old_tried == 0 || + (float) (stats.vivifystrirr - old) / + (float) (stats.vivifychecks - old_tried) < + 0.01) { + delaying_vivify_irredundant.bumpreasons.bump_delay (); + } else { + delaying_vivify_irredundant.bumpreasons.reduce_delay (); + } + } else { + delaying_vivify_irredundant.bumpreasons.bump_delay (); + LOG ("building the schedule already used our entire ticks budget for " + "irredundant"); + } + } + + reset_noccs (); + STOP_SIMPLIFIER (vivify, VIVIFY); + + private_steps = false; + + return true; +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/vivify.hpp b/src/sat/cadical/vivify.hpp new file mode 100644 index 000000000..a23acb9ff --- /dev/null +++ b/src/sat/cadical/vivify.hpp @@ -0,0 +1,62 @@ +#ifndef _vivify_hpp_INCLUDED +#define _vivify_hpp_INCLUDED + +#include "util.hpp" + +#include <array> +#include <cstdint> +#include <vector> + +namespace CaDiCaL { + +struct Clause; + +enum class Vivify_Mode { TIER1, TIER2, TIER3, IRREDUNDANT }; + +#define COUNTREF_COUNTS 2 + +struct vivify_ref { + bool vivify; + std::size_t size; + uint64_t count[COUNTREF_COUNTS]; + Clause *clause; +}; + +// In the vivifier structure, we put the schedules in an array in order to +// be able to iterate over them, but we provide the reference to them to +// make sure that you do need to remember the order. +struct Vivifier { + std::array<std::vector<vivify_ref>, 4> refs_schedules; + std::vector<vivify_ref> &refs_schedule_tier1, &refs_schedule_tier2, + &refs_schedule_tier3, &refs_schedule_irred; + std::array<std::vector<Clause *>, 4> schedules; + std::vector<Clause *> &schedule_tier1, &schedule_tier2, &schedule_tier3, + &schedule_irred; + std::vector<int> sorted; + Vivify_Mode tier; + char tag; + int tier1_limit; + int tier2_limit; + int64_t ticks; + std::vector<std::tuple<int, Clause *, bool>> lrat_stack; + Vivifier (Vivify_Mode mode_tier) + : refs_schedule_tier1 (refs_schedules[0]), + refs_schedule_tier2 (refs_schedules[1]), + refs_schedule_tier3 (refs_schedules[2]), + refs_schedule_irred (refs_schedules[3]), + schedule_tier1 (schedules[0]), schedule_tier2 (schedules[1]), + schedule_tier3 (schedules[2]), schedule_irred (schedules[3]), + tier (mode_tier) {} + + void erase () { + erase_vector (refs_schedule_tier1); + erase_vector (refs_schedule_tier2); + erase_vector (refs_schedule_tier3); + erase_vector (refs_schedule_irred); + erase_vector (sorted); + } +}; + +} // namespace CaDiCaL + +#endif diff --git a/src/sat/cadical/walk.cpp b/src/sat/cadical/walk.cpp new file mode 100644 index 000000000..ac671a7ad --- /dev/null +++ b/src/sat/cadical/walk.cpp @@ -0,0 +1,704 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +/*------------------------------------------------------------------------*/ + +// Random walk local search based on 'ProbSAT' ideas. + +struct Walker { + + Internal *internal; + + Random random; // local random number generator + int64_t propagations; // number of propagations + int64_t limit; // limit on number of propagations + vector<Clause *> broken; // currently unsatisfied clauses + double epsilon; // smallest considered score + vector<double> table; // break value to score table + vector<double> scores; // scores of candidate literals + + double score (unsigned); // compute score from break count + + Walker (Internal *, double size, int64_t limit); +}; + +// These are in essence the CB values from Adrian Balint's thesis. They +// denote the inverse 'cb' of the base 'b' of the (probability) weight +// 'b^-i' for picking a literal with the break value 'i' (first column is +// the 'size', second the 'CB' value). + +static double cbvals[][2] = { + {0.0, 2.00}, {3.0, 2.50}, {4.0, 2.85}, {5.0, 3.70}, + {6.0, 5.10}, {7.0, 7.40}, // Adrian has '5.4', but '7.4' looks better. +}; + +static const int ncbvals = sizeof cbvals / sizeof cbvals[0]; + +// We interpolate the CB values for uniform random SAT formula to the non +// integer situation of average clause size by piecewise linear functions. +// +// y2 - y1 +// ------- * (x - x1) + y1 +// x2 - x1 +// +// where 'x' is the average size of clauses and 'y' the CB value. + +inline static double fitcbval (double size) { + int i = 0; + while (i + 2 < ncbvals && + (cbvals[i][0] > size || cbvals[i + 1][0] < size)) + i++; + const double x2 = cbvals[i + 1][0], x1 = cbvals[i][0]; + const double y2 = cbvals[i + 1][1], y1 = cbvals[i][1]; + const double dx = x2 - x1, dy = y2 - y1; + assert (dx); + const double res = dy * (size - x1) / dx + y1; + assert (res > 0); + return res; +} + +// Initialize the data structures for one local search round. + +Walker::Walker (Internal *i, double size, int64_t l) + : internal (i), random (internal->opts.seed), // global random seed + propagations (0), limit (l) { + random += internal->stats.walk.count; // different seed every time + + // This is the magic constant in ProbSAT (also called 'CB'), which we pick + // according to the average size every second invocation and otherwise + // just the default '2.0', which turns into the base '0.5'. + // + const bool use_size_based_cb = (internal->stats.walk.count & 1); + const double cb = use_size_based_cb ? fitcbval (size) : 2.0; + assert (cb); + const double base = 1 / cb; // scores are 'base^0,base^1,base^2,... + + double next = 1; + for (epsilon = next; next; next = epsilon * base) + table.push_back (epsilon = next); + + PHASE ("walk", internal->stats.walk.count, + "CB %.2f with inverse %.2f as base and table size %zd", cb, base, + table.size ()); +} + +// The scores are tabulated for faster computation (to avoid 'pow'). + +inline double Walker::score (unsigned i) { + const double res = (i < table.size () ? table[i] : epsilon); + LOG ("break %u mapped to score %g", i, res); + return res; +} + +/*------------------------------------------------------------------------*/ + +Clause *Internal::walk_pick_clause (Walker &walker) { + require_mode (WALK); + assert (!walker.broken.empty ()); + int64_t size = walker.broken.size (); + if (size > INT_MAX) + size = INT_MAX; + int pos = walker.random.pick_int (0, size - 1); + Clause *res = walker.broken[pos]; + LOG (res, "picking random position %d", pos); + return res; +} + +/*------------------------------------------------------------------------*/ + +// Compute the number of clauses which would be become unsatisfied if 'lit' +// is flipped and set to false. This is called the 'break-count' of 'lit'. + +unsigned Internal::walk_break_value (int lit) { + + require_mode (WALK); + assert (val (lit) > 0); + + unsigned res = 0; // The computed break-count of 'lit'. + + for (auto &w : watches (lit)) { + assert (w.blit != lit); + if (val (w.blit) > 0) + continue; + if (w.binary ()) { + res++; + continue; + } + + Clause *c = w.clause; + assert (lit == c->literals[0]); + + // Now try to find a second satisfied literal starting at 'literals[1]' + // shifting all the traversed literals to right by one position in order + // to move such a second satisfying literal to 'literals[1]'. This move + // to front strategy improves the chances to find the second satisfying + // literal earlier in subsequent break-count computations. + // + auto begin = c->begin () + 1; + const auto end = c->end (); + auto i = begin; + int prev = 0; + while (i != end) { + const int other = *i; + *i++ = prev; + prev = other; + if (val (other) < 0) + continue; + + // Found 'other' as second satisfying literal. + + w.blit = other; // Update 'blit' + *begin = other; // and move to front. + + break; + } + + if (i != end) + continue; // Double satisfied! + + // Otherwise restore literals (undo shift to the right). + // + while (i != begin) { + const int other = *--i; + *i = prev; + prev = other; + } + + res++; // Literal 'lit' single satisfies clause 'c'. + } + + return res; +} + +/*------------------------------------------------------------------------*/ + +// Given an unsatisfied clause 'c', in which we want to flip a literal, we +// first determine the exponential score based on the break-count of its +// literals and then sample the literals based on these scores. The CB +// value is smaller than one and thus the score is exponentially decreasing +// with the break-count increasing. The sampling works as in 'ProbSAT' and +// 'YalSAT' by summing up the scores and then picking a random limit in the +// range of zero to the sum, then summing up the scores again and picking +// the first literal which reaches the limit. Note, that during incremental +// SAT solving we can not flip assumed variables. Those are assigned at +// decision level one, while the other variables are assigned at two. + +int Internal::walk_pick_lit (Walker &walker, Clause *c) { + LOG ("picking literal by break-count"); + assert (walker.scores.empty ()); + double sum = 0; + int64_t propagations = 0; + for (const auto lit : *c) { + assert (active (lit)); + if (var (lit).level == 1) { + LOG ("skipping assumption %d for scoring", -lit); + continue; + } + assert (active (lit)); + propagations++; + unsigned tmp = walk_break_value (-lit); + double score = walker.score (tmp); + LOG ("literal %d break-count %u score %g", lit, tmp, score); + walker.scores.push_back (score); + sum += score; + } + LOG ("scored %zd literals", walker.scores.size ()); + assert (!walker.scores.empty ()); + walker.propagations += propagations; + stats.propagations.walk += propagations; + assert (walker.scores.size () <= (size_t) c->size); + const double lim = sum * walker.random.generate_double (); + LOG ("score sum %g limit %g", sum, lim); + const auto end = c->end (); + auto i = c->begin (); + auto j = walker.scores.begin (); + int res; + for (;;) { + assert (i != end); + res = *i++; + if (var (res).level > 1) + break; + LOG ("skipping assumption %d without score", -res); + } + sum = *j++; + while (sum <= lim && i != end) { + res = *i++; + if (var (res).level == 1) { + LOG ("skipping assumption %d without score", -res); + continue; + } + sum += *j++; + } + walker.scores.clear (); + LOG ("picking literal %d by break-count", res); + return res; +} + +/*------------------------------------------------------------------------*/ + +void Internal::walk_flip_lit (Walker &walker, int lit) { + + require_mode (WALK); + LOG ("flipping assign %d", lit); + assert (val (lit) < 0); + + // First flip the literal value. + // + const int tmp = sign (lit); + const int idx = abs (lit); + set_val (idx, tmp); + assert (val (lit) > 0); + + // Then remove 'c' and all other now satisfied (made) clauses. + { + // Simply go over all unsatisfied (broken) clauses. + + LOG ("trying to make %zd broken clauses", walker.broken.size ()); + + // We need to measure (and bound) the memory accesses during traversing + // broken clauses in terms of 'propagations'. This is tricky since we + // are not actually propagating literals. Instead we use the clause + // variable 'ratio' as an approximation to the number of clauses used + // during propagating a literal. Note that we use a one-watch scheme. + // Accordingly the number of broken clauses traversed divided by that + // ratio is an approximation of the number of propagation this would + // correspond to (in terms of memory access). To eagerly update these + // statistics we simply increment the propagation counter after every + // 'ratio' traversed clause. These propagations are particularly + // expensive if the number of broken clauses is large which usually + // happens initially. + // + const double ratio = clause_variable_ratio (); + const auto eou = walker.broken.end (); + auto j = walker.broken.begin (), i = j; +#ifdef LOGGING + int64_t made = 0; +#endif + int64_t count = 0; + + while (i != eou) { + + Clause *d = *j++ = *i++; + + int *literals = d->literals, prev = 0; + + // Find 'lit' in 'd'. + // + const int size = d->size; + for (int i = 0; i < size; i++) { + const int other = literals[i]; + assert (active (other)); + literals[i] = prev; + prev = other; + if (other == lit) + break; + assert (val (other) < 0); + } + + // If 'lit' is in 'd' then move it to the front to watch it. + // + if (prev == lit) { + literals[0] = lit; + LOG (d, "made"); + watch_literal (literals[0], literals[1], d); +#ifdef LOGGING + made++; +#endif + j--; + + } else { // Otherwise the clause is not satisfied, undo shift. + + for (int i = size - 1; i >= 0; i--) { + int other = literals[i]; + literals[i] = prev; + prev = other; + } + } + + if (count--) + continue; + + // Update these counters eagerly. Otherwise if we delay the update + // until all clauses are traversed, interrupting the solver has a high + // chance of giving bogus statistics on the number of 'propagations' + // in 'walk', if it is interrupted in this loop. + + count = ratio; // Starting counting down again. + walker.propagations++; + stats.propagations.walk++; + } + LOG ("made %" PRId64 " clauses by flipping %d", made, lit); + walker.broken.resize (j - walker.broken.begin ()); + } + + // Finally add all new unsatisfied (broken) clauses. + { + walker.propagations++; // This really corresponds now to one + stats.propagations.walk++; // propagation (in a one-watch scheme). + +#ifdef LOGGING + int64_t broken = 0; +#endif + Watches &ws = watches (-lit); + + LOG ("trying to break %zd watched clauses", ws.size ()); + + for (const auto &w : ws) { + Clause *d = w.clause; + LOG (d, "unwatch %d in", -lit); + int *literals = d->literals, replacement = 0, prev = -lit; + assert (literals[0] == -lit); + const int size = d->size; + for (int i = 1; i < size; i++) { + const int other = literals[i]; + assert (active (other)); + literals[i] = prev; // shift all to right + prev = other; + const signed char tmp = val (other); + if (tmp < 0) + continue; + replacement = other; // satisfying literal + break; + } + if (replacement) { + literals[1] = -lit; + literals[0] = replacement; + assert (-lit != replacement); + watch_literal (replacement, -lit, d); + } else { + for (int i = size - 1; i > 0; i--) { // undo shift + const int other = literals[i]; + literals[i] = prev; + prev = other; + } + assert (literals[0] == -lit); + LOG (d, "broken"); + walker.broken.push_back (d); +#ifdef LOGGING + broken++; +#endif + } + } + LOG ("broken %" PRId64 " clauses by flipping %d", broken, lit); + ws.clear (); + } +} + +/*------------------------------------------------------------------------*/ + +// Check whether to save the current phases as new global minimum. + +inline void Internal::walk_save_minimum (Walker &walker) { + int64_t broken = walker.broken.size (); + if (broken >= stats.walk.minimum) + return; + VERBOSE (3, "new global minimum %" PRId64 "", broken); + stats.walk.minimum = broken; + for (auto i : vars) { + const signed char tmp = vals[i]; + if (tmp) + phases.min[i] = phases.saved[i] = tmp; + } +} + +/*------------------------------------------------------------------------*/ + +int Internal::walk_round (int64_t limit, bool prev) { + + backtrack (); + if (propagated < trail.size () && !propagate ()) { + LOG ("empty clause after root level propagation"); + learn_empty_clause (); + return 20; + } + + stats.walk.count++; + + clear_watches (); + + // Remove all fixed variables first (assigned at decision level zero). + // + if (last.collect.fixed < stats.all.fixed) + garbage_collection (); + +#ifndef QUIET + // We want to see more messages during initial local search. + // + if (localsearching) { + assert (!force_phase_messages); + force_phase_messages = true; + } +#endif + + PHASE ("walk", stats.walk.count, + "random walk limit of %" PRId64 " propagations", limit); + + // First compute the average clause size for picking the CB constant. + // + double size = 0; + int64_t n = 0; + for (const auto c : clauses) { + if (c->garbage) + continue; + if (c->redundant) { + if (!opts.walkredundant) + continue; + if (!likely_to_be_kept_clause (c)) + continue; + } + size += c->size; + n++; + } + double average_size = relative (size, n); + + PHASE ("walk", stats.walk.count, + "%" PRId64 " clauses average size %.2f over %d variables", n, + average_size, active ()); + + // Instantiate data structures for this local search round. + // + Walker walker (internal, average_size, limit); + + bool failed = false; // Inconsistent assumptions? + + level = 1; // Assumed variables assigned at level 1. + + if (assumptions.empty ()) { + LOG ("no assumptions so assigning all variables to decision phase"); + } else { + LOG ("assigning assumptions to their forced phase first"); + for (const auto lit : assumptions) { + signed char tmp = val (lit); + if (tmp > 0) + continue; + if (tmp < 0) { + LOG ("inconsistent assumption %d", lit); + failed = true; + break; + } + if (!active (lit)) + continue; + tmp = sign (lit); + const int idx = abs (lit); + LOG ("initial assign %d to assumption phase", tmp < 0 ? -idx : idx); + set_val (idx, tmp); + assert (level == 1); + var (idx).level = 1; + } + if (!failed) + LOG ("now assigning remaining variables to their decision phase"); + } + + level = 2; // All other non assumed variables assigned at level 2. + + if (!failed) { + + for (auto idx : vars) { + if (!active (idx)) { + LOG ("skipping inactive variable %d", idx); + continue; + } + if (vals[idx]) { + assert (var (idx).level == 1); + LOG ("skipping assumed variable %d", idx); + continue; + } + int tmp = 0; + if (prev) + tmp = phases.prev[idx]; + if (!tmp) + tmp = sign (decide_phase (idx, true)); + assert (tmp == 1 || tmp == -1); + set_val (idx, tmp); + assert (level == 2); + var (idx).level = 2; + LOG ("initial assign %d to decision phase", tmp < 0 ? -idx : idx); + } + + LOG ("watching satisfied and registering broken clauses"); +#ifdef LOGGING + int64_t watched = 0; +#endif + for (const auto c : clauses) { + + if (c->garbage) + continue; + if (c->redundant) { + if (!opts.walkredundant) + continue; + if (!likely_to_be_kept_clause (c)) + continue; + } + + bool satisfiable = false; // contains not only assumptions + int satisfied = 0; // clause satisfied? + + int *lits = c->literals; + const int size = c->size; + + // Move to front satisfied literals and determine whether there + // is at least one (non-assumed) literal that can be flipped. + // + for (int i = 0; satisfied < 2 && i < size; i++) { + const int lit = lits[i]; + assert (active (lit)); // Due to garbage collection. + if (val (lit) > 0) { + swap (lits[satisfied], lits[i]); + if (!satisfied++) + LOG ("first satisfying literal %d", lit); + } else if (!satisfiable && var (lit).level > 1) { + LOG ("non-assumption potentially satisfying literal %d", lit); + satisfiable = true; + } + } + + if (!satisfied && !satisfiable) { + LOG (c, "due to assumptions unsatisfiable"); + LOG ("stopping local search since assumptions falsify a clause"); + failed = true; + break; + } + + if (satisfied) { + watch_literal (lits[0], lits[1], c); +#ifdef LOGGING + watched++; +#endif + } else { + assert (satisfiable); // at least one non-assumed variable ... + LOG (c, "broken"); + walker.broken.push_back (c); + } + } +#ifdef LOGGING + if (!failed) { + int64_t broken = walker.broken.size (); + int64_t total = watched + broken; + LOG ("watching %" PRId64 " clauses %.0f%% " + "out of %" PRId64 " (watched and broken)", + watched, percent (watched, total), total); + } +#endif + } + + int64_t old_global_minimum = stats.walk.minimum; + + int res; // Tells caller to continue with local search. + + if (!failed) { + + int64_t broken = walker.broken.size (); + + PHASE ("walk", stats.walk.count, + "starting with %" PRId64 " unsatisfied clauses " + "(%.0f%% out of %" PRId64 ")", + broken, percent (broken, stats.current.irredundant), + stats.current.irredundant); + + walk_save_minimum (walker); + + int64_t minimum = broken; +#ifndef QUIET + int64_t flips = 0; +#endif + while (!terminated_asynchronously () && !walker.broken.empty () && + walker.propagations < walker.limit) { +#ifndef QUIET + flips++; +#endif + stats.walk.flips++; + stats.walk.broken += broken; + Clause *c = walk_pick_clause (walker); + const int lit = walk_pick_lit (walker, c); + walk_flip_lit (walker, lit); + broken = walker.broken.size (); + LOG ("now have %" PRId64 " broken clauses in total", broken); + if (broken >= minimum) + continue; + minimum = broken; + VERBOSE (3, "new phase minimum %" PRId64 " after %" PRId64 " flips", + minimum, flips); + walk_save_minimum (walker); + } + + if (minimum < old_global_minimum) + PHASE ("walk", stats.walk.count, + "%snew global minimum %" PRId64 "%s in %" PRId64 " flips and " + "%" PRId64 " propagations", + tout.bright_yellow_code (), minimum, tout.normal_code (), + flips, walker.propagations); + else + PHASE ("walk", stats.walk.count, + "best phase minimum %" PRId64 " in %" PRId64 " flips and " + "%" PRId64 " propagations", + minimum, flips, walker.propagations); + + if (opts.profile >= 2) { + PHASE ("walk", stats.walk.count, + "%.2f million propagations per second", + relative (1e-6 * walker.propagations, + time () - profiles.walk.started)); + + PHASE ("walk", stats.walk.count, "%.2f thousand flips per second", + relative (1e-3 * flips, time () - profiles.walk.started)); + + } else { + PHASE ("walk", stats.walk.count, "%.2f million propagations", + 1e-6 * walker.propagations); + + PHASE ("walk", stats.walk.count, "%.2f thousand flips", 1e-3 * flips); + } + + if (minimum > 0) { + LOG ("minimum %" PRId64 " non-zero thus potentially continue", + minimum); + res = 0; + } else { + LOG ("minimum is zero thus stop local search"); + res = 10; + } + + } else { + + res = 20; + + PHASE ("walk", stats.walk.count, + "aborted due to inconsistent assumptions"); + } + + copy_phases (phases.prev); + + for (auto idx : vars) + if (active (idx)) + set_val (idx, 0); + + assert (level == 2); + level = 0; + + clear_watches (); + connect_watches (); + +#ifndef QUIET + if (localsearching) { + assert (force_phase_messages); + force_phase_messages = false; + } +#endif + + return res; +} + +void Internal::walk () { + START_INNER_WALK (); + int64_t limit = stats.propagations.search; + limit *= 1e-3 * opts.walkeffort; + if (limit < opts.walkmineff) + limit = opts.walkmineff; + if (limit > opts.walkmaxeff) + limit = opts.walkmaxeff; + (void) walk_round (limit, false); + STOP_INNER_WALK (); +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/watch.cpp b/src/sat/cadical/watch.cpp new file mode 100644 index 000000000..2456866d3 --- /dev/null +++ b/src/sat/cadical/watch.cpp @@ -0,0 +1,126 @@ +#include "internal.hpp" + +namespace CaDiCaL { + +void Internal::init_watches () { + assert (wtab.empty ()); + if (wtab.size () < 2 * vsize) + wtab.resize (2 * vsize, Watches ()); + LOG ("initialized watcher tables"); +} + +void Internal::clear_watches () { + for (auto lit : lits) + watches (lit).clear (); +} + +void Internal::reset_watches () { + assert (!wtab.empty ()); + erase_vector (wtab); + LOG ("reset watcher tables"); +} + +// This can be quite costly since lots of memory is accessed in a rather +// random fashion, and thus we optionally profile it. + +void Internal::connect_watches (bool irredundant_only) { + START (connect); + assert (watching ()); + + LOG ("watching all %sclauses", irredundant_only ? "irredundant " : ""); + + // First connect binary clauses. + // + for (const auto &c : clauses) { + if (irredundant_only && c->redundant) + continue; + if (c->garbage || c->size > 2) + continue; + watch_clause (c); + } + + // Then connect non-binary clauses. + // + for (const auto &c : clauses) { + if (irredundant_only && c->redundant) + continue; + if (c->garbage || c->size == 2) + continue; + watch_clause (c); + if (!level) { + const int lit0 = c->literals[0]; + const int lit1 = c->literals[1]; + const signed char tmp0 = val (lit0); + const signed char tmp1 = val (lit1); + if (tmp0 > 0) + continue; + if (tmp1 > 0) + continue; + if (tmp0 < 0) { + const size_t pos0 = var (lit0).trail; + if (pos0 < propagated) { + propagated = pos0; + LOG ("literal %d resets propagated to %zd", lit0, pos0); + } + } + if (tmp1 < 0) { + const size_t pos1 = var (lit1).trail; + if (pos1 < propagated) { + propagated = pos1; + LOG ("literal %d resets propagated to %zd", lit1, pos1); + } + } + } + } + + STOP (connect); +} + +// This can be quite costly since lots of memory is accessed in a rather +// random fashion, and thus we optionally profile it. + +void Internal::connect_binary_watches () { + START (connect); + assert (watching ()); + + LOG ("watching binary clauses"); + + // First connect binary clauses. + // + for (const auto &c : clauses) { + if (c->garbage || c->size > 2) + continue; + watch_clause (c); + } + + STOP (connect); +} + +void Internal::sort_watches () { + assert (watching ()); + LOG ("sorting watches"); + Watches saved; + for (auto lit : lits) { + Watches &ws = watches (lit); + + const const_watch_iterator end = ws.end (); + watch_iterator j = ws.begin (); + const_watch_iterator i; + + assert (saved.empty ()); + + for (i = j; i != end; i++) { + const Watch w = *i; + if (w.binary ()) + *j++ = w; + else + saved.push_back (w); + } + + std::copy (saved.cbegin (), saved.cend (), j); + + saved.clear (); + } +} + +} // namespace CaDiCaL diff --git a/src/sat/cadical/watch.hpp b/src/sat/cadical/watch.hpp new file mode 100644 index 000000000..3adc4c47f --- /dev/null +++ b/src/sat/cadical/watch.hpp @@ -0,0 +1,72 @@ +#ifndef _watch_hpp_INCLUDED +#define _watch_hpp_INCLUDED + +#include <cassert> +#include <vector> + +namespace CaDiCaL { + +// Watch lists for CDCL search. The blocking literal (see also comments +// related to 'propagate') is a must and thus combining that with a 64 bit +// pointer will give a 16 byte (8 byte aligned) structure anyhow, which +// means the additional 4 bytes for the size come for free. As alternative +// one could use a 32-bit reference instead of the pointer which would +// however limit the number of clauses to '2^32 - 1'. One would also need +// to use at least one more bit (either taken away from the variable space +// or the clauses) to denote whether the watch is binary. + +// in fashion of Intel Sat 10.4230/LIPIcs.SAT.2022.8 we try to +// guarantee the following invariant: +// For both watches: +// if the watched literal is negatively assigned +// either it will be propagated in the future +// or the corresponding blocking literal is positively assigned +// and its level is smaller than the level of the watched literal +// + +struct Clause; + +struct Watch { + + Clause *clause; + int blit; + int size; + + Watch (int b, Clause *c) : clause (c), blit (b), size (c->size) {} + Watch () {} + + bool binary () const { return size == 2; } +}; + +typedef vector<Watch> Watches; // of one literal + +typedef Watches::iterator watch_iterator; +typedef Watches::const_iterator const_watch_iterator; + +inline void remove_watch (Watches &ws, Clause *clause) { + const auto end = ws.end (); + auto i = ws.begin (); + for (auto j = i; j != end; j++) { + const Watch &w = *i++ = *j; + if (w.clause == clause) + i--; + } + assert (i + 1 == end); + ws.resize (i - ws.begin ()); +} + +// search for the clause and updates the size marked in the watch lists +inline void update_watch_size (Watches &ws, int blit, Clause *conflict) { + bool found = false; + const int size = conflict->size; + for (Watch &w : ws) { + if (w.clause == conflict) + w.size = size, w.blit = blit, found = true; + assert (w.clause->garbage || w.size == 2 || w.clause->size != 2); + } + assert (found), (void) found; +} + +} // namespace CaDiCaL + +#endif From 14b451b52fe2b4c6539cbf7b8dfd074bcb9ab33e Mon Sep 17 00:00:00 2001 From: MyskYko <yoyuohlhjl@yahoo.co.jp> Date: Fri, 7 Mar 2025 00:25:11 -0800 Subject: [PATCH 2/2] patch --- Makefile | 2 +- abclib.dsp | 368 +++ src/base/abci/abc.c | 3 + src/sat/cadical/arena.hpp | 12 +- src/sat/cadical/averages.hpp | 6 + src/sat/cadical/bins.hpp | 6 + src/sat/cadical/block.hpp | 6 + src/sat/cadical/cadical.hpp | 31 +- src/sat/cadical/cadicalSolver.c | 305 ++ src/sat/cadical/cadicalSolver.h | 76 + src/sat/cadical/cadicalTest.c | 210 ++ .../{analyze.cpp => cadical_analyze.cpp} | 186 +- .../cadical/{arena.cpp => cadical_arena.cpp} | 8 +- .../{assume.cpp => cadical_assume.cpp} | 104 +- .../{averages.cpp => cadical_averages.cpp} | 8 +- .../{backtrack.cpp => cadical_backtrack.cpp} | 22 +- .../{backward.cpp => cadical_backward.cpp} | 34 +- .../cadical/{bins.cpp => cadical_bins.cpp} | 10 +- .../cadical/{block.cpp => cadical_block.cpp} | 164 +- .../{ccadical.cpp => cadical_ccadical.cpp} | 21 +- .../{checker.cpp => cadical_checker.cpp} | 84 +- .../{clause.cpp => cadical_clause.cpp} | 110 +- .../{collect.cpp => cadical_collect.cpp} | 58 +- .../{compact.cpp => cadical_compact.cpp} | 106 +- .../{condition.cpp => cadical_condition.cpp} | 124 +- .../{config.cpp => cadical_config.cpp} | 8 +- ...{congruence.cpp => cadical_congruence.cpp} | 1526 +++++----- .../{constrain.cpp => cadical_constrain.cpp} | 6 + .../{contract.cpp => cadical_contract.cpp} | 8 +- .../cadical/{cover.cpp => cadical_cover.cpp} | 70 +- .../{decide.cpp => cadical_decide.cpp} | 22 +- .../{decompose.cpp => cadical_decompose.cpp} | 120 +- ...eduplicate.cpp => cadical_deduplicate.cpp} | 18 +- ...{definition.cpp => cadical_definition.cpp} | 96 +- ...{drattracer.cpp => cadical_drattracer.cpp} | 34 +- .../cadical/{elim.cpp => cadical_elim.cpp} | 134 +- .../{elimfast.cpp => cadical_elimfast.cpp} | 80 +- src/sat/cadical/{ema.cpp => cadical_ema.cpp} | 10 +- .../{extend.cpp => cadical_extend.cpp} | 76 +- .../{external.cpp => cadical_external.cpp} | 152 +- ...ate.cpp => cadical_external_propagate.cpp} | 168 +- .../{factor.cpp => cadical_factor.cpp} | 134 +- .../cadical/{file.cpp => cadical_file.cpp} | 53 +- .../cadical/{flags.cpp => cadical_flags.cpp} | 70 +- .../cadical/{flip.cpp => cadical_flip.cpp} | 48 +- .../{format.cpp => cadical_format.cpp} | 8 +- ...{frattracer.cpp => cadical_frattracer.cpp} | 36 +- .../cadical/{gates.cpp => cadical_gates.cpp} | 78 +- ...druptracer.cpp => cadical_idruptracer.cpp} | 78 +- ...nstantiate.cpp => cadical_instantiate.cpp} | 62 +- .../{internal.cpp => cadical_internal.cpp} | 90 +- .../{ipasir.cpp => cadical_ipasir.cpp} | 7 +- src/sat/cadical/cadical_kitten.c | 2609 +++++++++++++++++ ...ruptracer.cpp => cadical_lidruptracer.cpp} | 86 +- .../cadical/{limit.cpp => cadical_limit.cpp} | 6 + .../{logging.cpp => cadical_logging.cpp} | 6 + .../{lookahead.cpp => cadical_lookahead.cpp} | 32 +- ...ratchecker.cpp => cadical_lratchecker.cpp} | 78 +- ...{lrattracer.cpp => cadical_lrattracer.cpp} | 38 +- .../cadical/{lucky.cpp => cadical_lucky.cpp} | 80 +- .../{message.cpp => cadical_message.cpp} | 10 +- .../{minimize.cpp => cadical_minimize.cpp} | 38 +- .../cadical/{occs.cpp => cadical_occs.cpp} | 16 +- .../{options.cpp => cadical_options.cpp} | 36 +- .../cadical/{parse.cpp => cadical_parse.cpp} | 30 +- .../{phases.cpp => cadical_phases.cpp} | 6 + .../cadical/{probe.cpp => cadical_probe.cpp} | 144 +- .../{profile.cpp => cadical_profile.cpp} | 22 +- .../cadical/{proof.cpp => cadical_proof.cpp} | 80 +- .../{propagate.cpp => cadical_propagate.cpp} | 80 +- .../cadical/{queue.cpp => cadical_queue.cpp} | 18 +- .../{random.cpp => cadical_random.cpp} | 32 +- .../cadical/{reap.cpp => cadical_reap.cpp} | 53 +- .../{reduce.cpp => cadical_reduce.cpp} | 14 +- .../{rephase.cpp => cadical_rephase.cpp} | 10 +- .../{report.cpp => cadical_report.cpp} | 14 +- .../{resources.cpp => cadical_resources.cpp} | 12 +- .../{restart.cpp => cadical_restart.cpp} | 10 +- .../{restore.cpp => cadical_restore.cpp} | 46 +- .../cadical/{score.cpp => cadical_score.cpp} | 8 +- .../{shrink.cpp => cadical_shrink.cpp} | 132 +- .../{signal.cpp => cadical_signal.cpp} | 26 +- .../{solution.cpp => cadical_solution.cpp} | 14 +- .../{solver.cpp => cadical_solver.cpp} | 76 +- .../{stable.cpp => cadical_stable.cpp} | 14 +- .../cadical/{stats.cpp => cadical_stats.cpp} | 14 +- .../{subsume.cpp => cadical_subsume.cpp} | 50 +- .../cadical/{sweep.cpp => cadical_sweep.cpp} | 332 ++- .../{terminal.cpp => cadical_terminal.cpp} | 15 +- .../{ternary.cpp => cadical_ternary.cpp} | 62 +- .../cadical/{tier.cpp => cadical_tier.cpp} | 12 +- .../{transred.cpp => cadical_transred.cpp} | 22 +- .../{unstable.cpp => cadical_unstable.cpp} | 15 +- .../cadical/{util.cpp => cadical_util.cpp} | 12 +- src/sat/cadical/{var.cpp => cadical_var.cpp} | 20 +- ...ipbtracer.cpp => cadical_veripbtracer.cpp} | 52 +- .../{version.cpp => cadical_version.cpp} | 18 +- .../{vivify.cpp => cadical_vivify.cpp} | 230 +- .../cadical/{walk.cpp => cadical_walk.cpp} | 74 +- .../cadical/{watch.cpp => cadical_watch.cpp} | 18 +- src/sat/cadical/ccadical.h | 15 +- src/sat/cadical/checker.hpp | 6 + src/sat/cadical/clause.hpp | 8 +- src/sat/cadical/config.hpp | 6 + src/sat/cadical/congruence.hpp | 18 +- src/sat/cadical/contract.hpp | 10 +- src/sat/cadical/cover.hpp | 6 +- src/sat/cadical/decompose.hpp | 6 + src/sat/cadical/delay.hpp | 6 + src/sat/cadical/drattracer.hpp | 10 +- src/sat/cadical/elim.hpp | 8 +- src/sat/cadical/ema.hpp | 10 +- src/sat/cadical/external.hpp | 40 +- src/sat/cadical/factor.hpp | 6 + src/sat/cadical/file.hpp | 43 +- src/sat/cadical/flags.hpp | 6 + src/sat/cadical/format.hpp | 6 + src/sat/cadical/frattracer.hpp | 10 +- src/sat/cadical/global.h | 21 + src/sat/cadical/heap.hpp | 40 +- src/sat/cadical/idruptracer.hpp | 10 +- src/sat/cadical/instantiate.hpp | 6 + src/sat/cadical/internal.hpp | 188 +- src/sat/cadical/inttypes.hpp | 2 + src/sat/cadical/ipasir.h | 10 +- src/sat/cadical/kitten.c | 2593 ---------------- src/sat/cadical/kitten.h | 98 +- src/sat/cadical/level.hpp | 6 + src/sat/cadical/lidruptracer.hpp | 10 +- src/sat/cadical/limit.hpp | 6 + src/sat/cadical/logging.hpp | 6 + src/sat/cadical/lratchecker.hpp | 6 + src/sat/cadical/lrattracer.hpp | 10 +- src/sat/cadical/message.hpp | 8 +- src/sat/cadical/module.make | 91 + src/sat/cadical/occs.hpp | 8 +- src/sat/cadical/options.hpp | 16 +- src/sat/cadical/parse.hpp | 6 + src/sat/cadical/phases.hpp | 6 + src/sat/cadical/profile.hpp | 44 +- src/sat/cadical/proof.hpp | 6 + src/sat/cadical/queue.hpp | 6 + src/sat/cadical/radix.hpp | 14 +- src/sat/cadical/random.h | 14 +- src/sat/cadical/random.hpp | 24 +- src/sat/cadical/range.hpp | 18 +- src/sat/cadical/reap.hpp | 6 + src/sat/cadical/reluctant.hpp | 8 +- src/sat/cadical/resources.hpp | 6 + src/sat/cadical/score.hpp | 6 + src/sat/cadical/signal.hpp | 10 +- src/sat/cadical/stack.h | 22 +- src/sat/cadical/stats.hpp | 6 + src/sat/cadical/sweep.hpp | 8 +- src/sat/cadical/terminal.hpp | 10 +- src/sat/cadical/testing.hpp | 6 + src/sat/cadical/tracer.hpp | 6 + src/sat/cadical/util.hpp | 18 +- src/sat/cadical/var.hpp | 6 + src/sat/cadical/veripbtracer.hpp | 12 +- src/sat/cadical/version.hpp | 6 + src/sat/cadical/vivify.hpp | 6 + src/sat/cadical/watch.hpp | 12 +- 163 files changed, 7897 insertions(+), 5828 deletions(-) create mode 100644 src/sat/cadical/cadicalSolver.c create mode 100644 src/sat/cadical/cadicalSolver.h create mode 100644 src/sat/cadical/cadicalTest.c rename src/sat/cadical/{analyze.cpp => cadical_analyze.cpp} (91%) rename src/sat/cadical/{arena.cpp => cadical_arena.cpp} (85%) rename src/sat/cadical/{assume.cpp => cadical_assume.cpp} (88%) rename src/sat/cadical/{averages.cpp => cadical_averages.cpp} (88%) rename src/sat/cadical/{backtrack.cpp => cadical_backtrack.cpp} (91%) rename src/sat/cadical/{backward.cpp => cadical_backward.cpp} (90%) rename src/sat/cadical/{bins.cpp => cadical_bins.cpp} (75%) rename src/sat/cadical/{block.cpp => cadical_block.cpp} (87%) rename src/sat/cadical/{ccadical.cpp => cadical_ccadical.cpp} (93%) rename src/sat/cadical/{checker.cpp => cadical_checker.cpp} (91%) rename src/sat/cadical/{clause.cpp => cadical_clause.cpp} (88%) rename src/sat/cadical/{collect.cpp => cadical_collect.cpp} (93%) rename src/sat/cadical/{compact.cpp => cadical_compact.cpp} (87%) rename src/sat/cadical/{condition.cpp => cadical_condition.cpp} (91%) rename src/sat/cadical/{config.cpp => cadical_config.cpp} (95%) rename src/sat/cadical/{congruence.cpp => cadical_congruence.cpp} (86%) rename src/sat/cadical/{constrain.cpp => cadical_constrain.cpp} (96%) rename src/sat/cadical/{contract.cpp => cadical_contract.cpp} (86%) rename src/sat/cadical/{cover.cpp => cadical_cover.cpp} (93%) rename src/sat/cadical/{decide.cpp => cadical_decide.cpp} (94%) rename src/sat/cadical/{decompose.cpp => cadical_decompose.cpp} (89%) rename src/sat/cadical/{deduplicate.cpp => cadical_deduplicate.cpp} (94%) rename src/sat/cadical/{definition.cpp => cadical_definition.cpp} (77%) rename src/sat/cadical/{drattracer.cpp => cadical_drattracer.cpp} (87%) rename src/sat/cadical/{elim.cpp => cadical_elim.cpp} (93%) rename src/sat/cadical/{elimfast.cpp => cadical_elimfast.cpp} (91%) rename src/sat/cadical/{ema.cpp => cadical_ema.cpp} (96%) rename src/sat/cadical/{extend.cpp => cadical_extend.cpp} (86%) rename src/sat/cadical/{external.cpp => cadical_external.cpp} (90%) rename src/sat/cadical/{external_propagate.cpp => cadical_external_propagate.cpp} (92%) rename src/sat/cadical/{factor.cpp => cadical_factor.cpp} (90%) rename src/sat/cadical/{file.cpp => cadical_file.cpp} (94%) rename src/sat/cadical/{flags.cpp => cadical_flags.cpp} (64%) rename src/sat/cadical/{flip.cpp => cadical_flip.cpp} (86%) rename src/sat/cadical/{format.cpp => cadical_format.cpp} (94%) rename src/sat/cadical/{frattracer.cpp => cadical_frattracer.cpp} (93%) rename src/sat/cadical/{gates.cpp => cadical_gates.cpp} (92%) rename src/sat/cadical/{idruptracer.cpp => cadical_idruptracer.cpp} (91%) rename src/sat/cadical/{instantiate.cpp => cadical_instantiate.cpp} (89%) rename src/sat/cadical/{internal.cpp => cadical_internal.cpp} (94%) rename src/sat/cadical/{ipasir.cpp => cadical_ipasir.cpp} (93%) create mode 100644 src/sat/cadical/cadical_kitten.c rename src/sat/cadical/{lidruptracer.cpp => cadical_lidruptracer.cpp} (91%) rename src/sat/cadical/{limit.cpp => cadical_limit.cpp} (97%) rename src/sat/cadical/{logging.cpp => cadical_logging.cpp} (98%) rename src/sat/cadical/{lookahead.cpp => cadical_lookahead.cpp} (96%) rename src/sat/cadical/{lratchecker.cpp => cadical_lratchecker.cpp} (94%) rename src/sat/cadical/{lrattracer.cpp => cadical_lrattracer.cpp} (89%) rename src/sat/cadical/{lucky.cpp => cadical_lucky.cpp} (89%) rename src/sat/cadical/{message.cpp => cadical_message.cpp} (97%) rename src/sat/cadical/{minimize.cpp => cadical_minimize.cpp} (90%) rename src/sat/cadical/{occs.cpp => cadical_occs.cpp} (80%) rename src/sat/cadical/{options.cpp => cadical_options.cpp} (93%) rename src/sat/cadical/{parse.cpp => cadical_parse.cpp} (96%) rename src/sat/cadical/{phases.cpp => cadical_phases.cpp} (94%) rename src/sat/cadical/{probe.cpp => cadical_probe.cpp} (90%) rename src/sat/cadical/{profile.cpp => cadical_profile.cpp} (87%) rename src/sat/cadical/{proof.cpp => cadical_proof.cpp} (92%) rename src/sat/cadical/{propagate.cpp => cadical_propagate.cpp} (90%) rename src/sat/cadical/{queue.cpp => cadical_queue.cpp} (87%) rename src/sat/cadical/{random.cpp => cadical_random.cpp} (94%) rename src/sat/cadical/{reap.cpp => cadical_reap.cpp} (70%) rename src/sat/cadical/{reduce.cpp => cadical_reduce.cpp} (96%) rename src/sat/cadical/{rephase.cpp => cadical_rephase.cpp} (98%) rename src/sat/cadical/{report.cpp => cadical_report.cpp} (97%) rename src/sat/cadical/{resources.cpp => cadical_resources.cpp} (97%) rename src/sat/cadical/{restart.cpp => cadical_restart.cpp} (97%) rename src/sat/cadical/{restore.cpp => cadical_restore.cpp} (90%) rename src/sat/cadical/{score.cpp => cadical_score.cpp} (92%) rename src/sat/cadical/{shrink.cpp => cadical_shrink.cpp} (82%) rename src/sat/cadical/{signal.cpp => cadical_signal.cpp} (90%) rename src/sat/cadical/{solution.cpp => cadical_solution.cpp} (89%) rename src/sat/cadical/{solver.cpp => cadical_solver.cpp} (97%) rename src/sat/cadical/{stable.cpp => cadical_stable.cpp} (67%) rename src/sat/cadical/{stats.cpp => cadical_stats.cpp} (99%) rename src/sat/cadical/{subsume.cpp => cadical_subsume.cpp} (95%) rename src/sat/cadical/{sweep.cpp => cadical_sweep.cpp} (86%) rename src/sat/cadical/{terminal.cpp => cadical_terminal.cpp} (78%) rename src/sat/cadical/{ternary.cpp => cadical_ternary.cpp} (92%) rename src/sat/cadical/{tier.cpp => cadical_tier.cpp} (89%) rename src/sat/cadical/{transred.cpp => cadical_transred.cpp} (94%) rename src/sat/cadical/{unstable.cpp => cadical_unstable.cpp} (67%) rename src/sat/cadical/{util.cpp => cadical_util.cpp} (94%) rename src/sat/cadical/{var.cpp => cadical_var.cpp} (63%) rename src/sat/cadical/{veripbtracer.cpp => cadical_veripbtracer.cpp} (92%) rename src/sat/cadical/{version.cpp => cadical_version.cpp} (89%) rename src/sat/cadical/{vivify.cpp => cadical_vivify.cpp} (92%) rename src/sat/cadical/{walk.cpp => cadical_walk.cpp} (93%) rename src/sat/cadical/{watch.cpp => cadical_watch.cpp} (90%) create mode 100644 src/sat/cadical/global.h delete mode 100644 src/sat/cadical/kitten.c create mode 100644 src/sat/cadical/module.make diff --git a/Makefile b/Makefile index 2da81734b..a546f65fd 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ MODULES := \ src/opt/cut src/opt/fxu src/opt/fxch src/opt/rwr src/opt/mfs src/opt/sim \ src/opt/ret src/opt/fret src/opt/res src/opt/lpk src/opt/nwk src/opt/rwt src/opt/rar \ src/opt/cgt src/opt/csw src/opt/dar src/opt/dau src/opt/dsc src/opt/sfm src/opt/sbd \ - src/sat/bsat src/sat/xsat src/sat/satoko src/sat/csat src/sat/msat src/sat/psat src/sat/cnf src/sat/bmc src/sat/glucose src/sat/glucose2 src/sat/kissat \ + src/sat/bsat src/sat/xsat src/sat/satoko src/sat/csat src/sat/msat src/sat/psat src/sat/cnf src/sat/bmc src/sat/glucose src/sat/glucose2 src/sat/kissat src/sat/cadical \ src/bool/bdc src/bool/deco src/bool/dec src/bool/kit src/bool/lucky \ src/bool/rsb src/bool/rpo \ src/proof/pdr src/proof/abs src/proof/live src/proof/ssc src/proof/int \ diff --git a/abclib.dsp b/abclib.dsp index 7f6af6f81..921030b34 100644 --- a/abclib.dsp +++ b/abclib.dsp @@ -2886,6 +2886,374 @@ SOURCE=.\src\sat\kissat\watch.c SOURCE=.\src\sat\kissat\weaken.c # End Source File # End Group +# Begin Group "cadical" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_kitten.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_analyze.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_arena.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_assume.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_averages.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_backtrack.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_backward.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_bins.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_block.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_ccadical.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_checker.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_clause.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_collect.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_compact.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_condition.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_config.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_congruence.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_constrain.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_contract.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_cover.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_decide.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_decompose.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_deduplicate.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_definition.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_drattracer.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_elim.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_elimfast.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_ema.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_extend.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_external.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_external_propagate.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_factor.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_file.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_flags.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_flip.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_format.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_frattracer.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_gates.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_idruptracer.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_instantiate.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_internal.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_ipasir.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_lidruptracer.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_limit.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_logging.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_lookahead.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_lratchecker.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_lrattracer.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_lucky.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_message.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_minimize.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_occs.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_options.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_parse.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_phases.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_probe.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_profile.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_proof.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_propagate.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_queue.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_random.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_reap.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_reduce.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_rephase.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_report.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_resources.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_restart.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_restore.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_score.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_shrink.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_signal.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_solution.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_solver.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_stable.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_stats.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_subsume.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_sweep.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_terminal.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_ternary.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_tier.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_transred.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_unstable.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_util.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_var.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_veripbtracer.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_version.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_vivify.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_walk.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadical_watch.cpp +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadicalSolver.c +# End Source File +# Begin Source File + +SOURCE=.\src\sat\cadical\cadicalTest.c +# End Source File +# End Group # End Group # Begin Group "opt" diff --git a/src/base/abci/abc.c b/src/base/abci/abc.c index 0cd356d36..d3131c705 100644 --- a/src/base/abci/abc.c +++ b/src/base/abci/abc.c @@ -56048,6 +56048,9 @@ int Abc_CommandAbc9Test( Abc_Frame_t * pAbc, int argc, char ** argv ) } } + extern void cadical_solver_test(); + cadical_solver_test(); + return 0; extern void kissat_solver_test(); kissat_solver_test(); return 0; diff --git a/src/sat/cadical/arena.hpp b/src/sat/cadical/arena.hpp index 6dbd51121..522fce36e 100644 --- a/src/sat/cadical/arena.hpp +++ b/src/sat/cadical/arena.hpp @@ -1,6 +1,10 @@ #ifndef _arena_hpp_INCLUDED #define _arena_hpp_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { // This memory allocation arena provides fixed size pre-allocated memory for @@ -36,7 +40,7 @@ namespace CaDiCaL { // q1 = arena.copy (p1, bytes1); // ... // qn = arena.copy (pn, bytesn); -// assert (bytes1 + ... + bytesn <= bytes); +// CADICAL_assert (bytes1 + ... + bytesn <= bytes); // arena.swap (); // ... // if (!arena.contains (q)) delete q; @@ -45,7 +49,7 @@ namespace CaDiCaL { // q1 = arena.copy (p1, bytes1); // ... // qn = arena.copy (pn, bytesn); -// assert (bytes1 + ... + bytesn <= bytes); +// CADICAL_assert (bytes1 + ... + bytesn <= bytes); // arena.swap (); // ... // @@ -88,7 +92,7 @@ public: char *copy (const char *p, size_t bytes) { char *res = to.top; to.top += bytes; - assert (to.top <= to.end); + CADICAL_assert (to.top <= to.end); memcpy (res, p, bytes); return res; } @@ -102,4 +106,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/averages.hpp b/src/sat/cadical/averages.hpp index 9f259509d..3ebdcddfa 100644 --- a/src/sat/cadical/averages.hpp +++ b/src/sat/cadical/averages.hpp @@ -1,8 +1,12 @@ #ifndef _averages_hpp_INCLUDED #define _averages_hpp_INCLUDED +#include "global.h" + #include "ema.hpp" // alphabetically after 'averages.hpp' +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct Averages { @@ -34,4 +38,6 @@ struct Averages { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/bins.hpp b/src/sat/cadical/bins.hpp index ec0f0f1dc..aeefeceeb 100644 --- a/src/sat/cadical/bins.hpp +++ b/src/sat/cadical/bins.hpp @@ -1,8 +1,12 @@ #ifndef _bins_hpp_INCLUDED #define _bins_hpp_INCLUDED +#include "global.h" + #include "util.hpp" // Alphabetically after 'bins'. +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { using namespace std; @@ -19,4 +23,6 @@ inline void erase_bins (Bins &bs) { erase_vector (bs); } } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/block.hpp b/src/sat/cadical/block.hpp index c56707563..202355f82 100644 --- a/src/sat/cadical/block.hpp +++ b/src/sat/cadical/block.hpp @@ -1,8 +1,12 @@ #ifndef _block_hpp_INCLUDED #define _block_hpp_INCLUDED +#include "global.h" + #include "heap.hpp" // Alphabetically after 'block.hpp'. +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct Internal; @@ -34,4 +38,6 @@ class Blocker { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/cadical.hpp b/src/sat/cadical/cadical.hpp index 8dacaf3dc..c9ed41332 100644 --- a/src/sat/cadical/cadical.hpp +++ b/src/sat/cadical/cadical.hpp @@ -1,10 +1,14 @@ #ifndef _cadical_hpp_INCLUDED #define _cadical_hpp_INCLUDED +#include "global.h" + #include <cstdint> #include <cstdio> #include <vector> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { /*========================================================================*/ @@ -49,13 +53,13 @@ enum Status { // solver->add (-TIE), solver->add (-SHIRT), solver->add (0); // // int res = solver->solve (); // Solve instance. -// assert (res == 10); // Check it is 'SATISFIABLE'. +// CADICAL_assert (res == 10); // Check it is 'SATISFIABLE'. // // res = solver->val (TIE); // Obtain assignment of 'TIE'. -// assert (res < 0); // Check 'TIE' assigned to 'false'. +// CADICAL_assert (res < 0); // Check 'TIE' assigned to 'false'. // // res = solver->val (SHIRT); // Obtain assignment of 'SHIRT'. -// assert (res > 0); // Check 'SHIRT' assigned to 'true'. +// CADICAL_assert (res > 0); // Check 'SHIRT' assigned to 'true'. // // // ------------------------------------------------------------------ // // Incrementally solve again under one assumption. @@ -63,13 +67,13 @@ enum Status { // solver->assume (TIE); // Now force 'TIE' to true. // // res = solver->solve (); // Solve again incrementally. -// assert (res == 20); // Check it is 'UNSATISFIABLE'. +// CADICAL_assert (res == 20); // Check it is 'UNSATISFIABLE'. // // res = solver->failed (TIE); // Check 'TIE' responsible. -// assert (res); // Yes, 'TIE' in core. +// CADICAL_assert (res); // Yes, 'TIE' in core. // // res = solver->failed (SHIRT); // Check 'SHIRT' responsible. -// assert (!res); // No, 'SHIRT' not in core. +// CADICAL_assert (!res); // No, 'SHIRT' not in core. // // // ------------------------------------------------------------------ // // Incrementally solve once more under another assumption. @@ -77,13 +81,13 @@ enum Status { // solver->assume (-SHIRT); // Now force 'SHIRT' to false. // // res = solver->solve (); // Solve again incrementally. -// assert (res == 20); // Check it is 'UNSATISFIABLE'. +// CADICAL_assert (res == 20); // Check it is 'UNSATISFIABLE'. // // res = solver->failed (TIE); // Check 'TIE' responsible. -// assert (!res); // No, 'TIE' not in core. +// CADICAL_assert (!res); // No, 'TIE' not in core. // // res = solver->failed (-SHIRT); // Check '!SHIRT' responsible. -// assert (res); // Yes, '!SHIRT' in core. +// CADICAL_assert (res); // Yes, '!SHIRT' in core. // // // ------------------------------------------------------------------ // @@ -595,7 +599,7 @@ public: // int reserve_difference (int number_of_vars); -#ifndef NTRACING +#ifndef CADICAL_NTRACING //------------------------------------------------------------------------ // This function can be used to write API calls to a file. The same // format is used which 'mobical' can read, execute and also shrink @@ -1029,7 +1033,7 @@ private: friend class Testing; // Access to 'internal' for testing only! -#ifndef NTRACING +#ifndef CADICAL_NTRACING // The API calls to the solver can be traced by setting the environment // variable 'CADICAL_API_TRACE' to point to the path of a file to which // API calls are written. The same format is used which 'mobical' can @@ -1092,10 +1096,13 @@ private: // // TODO: support for other compilers (beside 'gcc' and 'clang'). + /* #define CADICAL_ATTRIBUTE_FORMAT(FORMAT_POSITION, \ VARIADIC_ARGUMENT_POSITION) \ __attribute__ ((format (PRINTF_FORMAT, FORMAT_POSITION, \ VARIADIC_ARGUMENT_POSITION))) + */ +#define CADICAL_ATTRIBUTE_FORMAT(FORMAT_POSITION, VARIADIC_ARGUMENT_POSITION) // Messages in a common style. // @@ -1339,4 +1346,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/cadicalSolver.c b/src/sat/cadical/cadicalSolver.c new file mode 100644 index 000000000..9caea6605 --- /dev/null +++ b/src/sat/cadical/cadicalSolver.c @@ -0,0 +1,305 @@ +/**CFile**************************************************************** + + FileName [cadicalSolver.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [SAT solver CaDiCaL by Armin Biere, University of Freiburg] + + Synopsis [https://github.com/arminbiere/cadical] + + Author [Integrated into ABC by Yukio Miyasaka] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cadicalSolver.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "ccadical.h" +#include "cadicalSolver.h" + +ABC_NAMESPACE_IMPL_START + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +/**Function************************************************************* + + Synopsis [allocate solver] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +cadical_solver* cadical_solver_new(void) { + cadical_solver* s = (cadical_solver*)malloc(sizeof(cadical_solver)); + s->p = (void*)ccadical_init(); + s->nVars = 0; + s->vAssumptions = NULL; + s->vCore = NULL; + return s; +} + +/**Function************************************************************* + + Synopsis [delete solver] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void cadical_solver_delete(cadical_solver* s) { + ccadical_release((CCaDiCaL*)s->p); + if(s->vAssumptions) { + Vec_IntFree(s->vAssumptions); + } + if(s->vCore) { + Vec_IntFree(s->vCore); + } + free(s); +} + +/**Function************************************************************* + + Synopsis [add clause] + + Description [cadical takes x and -x as a literal for a variable x > 0, + where 0 is an indicator of the end of a clause. + since variables start from 0 in abc, a variable v is + translated into v + 1 in cadical.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int cadical_solver_addclause(cadical_solver* s, int* begin, int* end) { + for(;begin != end; begin++) { + if(*begin & 1) { + ccadical_add((CCaDiCaL*)s->p, -(1 + ((*begin) >> 1))); + } else { + ccadical_add((CCaDiCaL*)s->p, 1 + ((*begin) >> 1) ); + } + } + ccadical_add((CCaDiCaL*)s->p, 0); + return !ccadical_is_inconsistent((CCaDiCaL*)s->p); +} + +/**Function************************************************************* + + Synopsis [solve with resource limits] + + Description [assumptions and inspection limits are not supported.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int cadical_solver_solve(cadical_solver* s, int* begin, int* end, ABC_INT64_T nConfLimit, ABC_INT64_T nInsLimit, ABC_INT64_T nConfLimitGlobal, ABC_INT64_T nInsLimitGlobal) { + // inspection limits are not supported + assert(nInsLimit == 0); + assert(nInsLimitGlobal == 0); + // set conflict limits + if(nConfLimit) + ccadical_limit((CCaDiCaL*)s->p, "conflicts", nConfLimit); + if(nConfLimitGlobal && (nConfLimit == 0 || nConfLimit > nConfLimitGlobal)) + ccadical_limit((CCaDiCaL*)s->p, "conflicts", nConfLimitGlobal); + // assumptions + if(begin != end) { + // save + if(s->vAssumptions == NULL) { + s->vAssumptions = Vec_IntAllocArrayCopy(begin, end - begin); + } else { + Vec_IntClear(s->vAssumptions); + Vec_IntGrow(s->vAssumptions, end - begin); + Vec_IntPushArray(s->vAssumptions, begin, end - begin); + } + // assume + for(;begin != end; begin++) { + if(*begin & 1) { + ccadical_assume((CCaDiCaL*)s->p, -(1 + ((*begin) >> 1))); + } else { + ccadical_assume((CCaDiCaL*)s->p, 1 + ((*begin) >> 1) ); + } + } + } + // solve + int res = ccadical_solve((CCaDiCaL*)s->p); + // translate this cadical return value into a corresponding ABC status value + switch(res) { + case 0: // UNDETERMINED + return 0; + case 10: // SATISFIABLE + return 1; + case 20: // UNSATISFIABLE + return -1; + default: + assert(0); + } + return 0; +} + +/**Function************************************************************* + + Synopsis [get unsat core] + + Description [following minisat, return number of literals in core, + which consists of responsible assumptions, negated. + array will be freed by solver.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int cadical_solver_final(cadical_solver* s, int** ppArray) { + int v, i; + if(s->vCore == NULL) { + s->vCore = Vec_IntAlloc(Vec_IntSize(s->vAssumptions)); + } else { + Vec_IntClear(s->vCore); + } + Vec_IntForEachEntry(s->vAssumptions, v, i) { + int failed; + if(v & 1) { + failed = ccadical_failed((CCaDiCaL*)s->p, -(1 + (v >> 1))); + } else { + failed = ccadical_failed((CCaDiCaL*)s->p, 1 + (v >> 1) ); + } + if(failed) { + Vec_IntPush(s->vCore, Abc_LitNot(v)); + } + } + *ppArray = Vec_IntArray(s->vCore); + return Vec_IntSize(s->vCore); +} + +/**Function************************************************************* + + Synopsis [get number of variables] + + Description [emulated using "nVars".] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int cadical_solver_nvars(cadical_solver* s) { + return s->nVars; +} + +/**Function************************************************************* + + Synopsis [add new variable] + + Description [emulated using "nVars".] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int cadical_solver_addvar(cadical_solver* s) { + return s->nVars++; +} + +/**Function************************************************************* + + Synopsis [set number of variables] + + Description [not only emulate with "nVars" but also reserve memory.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +void cadical_solver_setnvars(cadical_solver* s,int n) { + s->nVars = n; + ccadical_reserve((CCaDiCaL*)s->p, n); +} + +/**Function************************************************************* + + Synopsis [get value of variable] + + Description [cadical returns x (true) or -x (false) for a variable x. + note a variable v was translated into v + 1 in cadical.] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +int cadical_solver_get_var_value(cadical_solver* s, int v) { + return ccadical_val((CCaDiCaL*)s->p, v + 1) > 0; +} + + +/**Function************************************************************* + + Synopsis [Solves the given CNF using cadical.] + + Description [] + + SideEffects [] + + SeeAlso [] + +***********************************************************************/ +Vec_Int_t * cadical_solve_cnf( Cnf_Dat_t * pCnf, char * pArgs, int nConfs, int nTimeLimit, int fSat, int fUnsat, int fPrintCex, int fVerbose ) +{ + abctime clk = Abc_Clock(); + Vec_Int_t * vRes = NULL; + int i, * pBeg, * pEnd, RetValue; + if ( fVerbose ) + printf( "CNF stats: Vars = %6d. Clauses = %7d. Literals = %8d. ", pCnf->nVars, pCnf->nClauses, pCnf->nLiterals ); + cadical_solver *pSat = cadical_solver_new(); + cadical_solver_setnvars(pSat, pCnf->nVars); + assert(cadical_solver_nvars(pSat) == pCnf->nVars); + Cnf_CnfForClause( pCnf, pBeg, pEnd, i ) { + if ( !cadical_solver_addclause(pSat, pBeg, pEnd) ) + { + assert( 0 ); // if it happens, can return 1 (unsatisfiable) + return NULL; + } + } + RetValue = cadical_solver_solve(pSat, NULL, NULL, 0, 0, 0, 0); + if ( RetValue == 1 ) + printf( "Result: Satisfiable. " ); + else if ( RetValue == -1 ) + printf( "Result: Unsatisfiable. " ); + else + printf( "Result: Undecided. " ); + if ( RetValue == 1 ) { + vRes = Vec_IntAlloc( pCnf->nVars ); + for ( i = 0; i < pCnf->nVars; i++ ) + Vec_IntPush( vRes, cadical_solver_get_var_value(pSat, i) ); + } + cadical_solver_delete(pSat); + Abc_PrintTime( 1, "Time", Abc_Clock() - clk ); + return vRes; +} + + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadicalSolver.h b/src/sat/cadical/cadicalSolver.h new file mode 100644 index 000000000..0192ada3c --- /dev/null +++ b/src/sat/cadical/cadicalSolver.h @@ -0,0 +1,76 @@ +/**CFile**************************************************************** + + FileName [cadicalSolver.h] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [SAT solver CaDiCaL by Armin Biere, University of Freiburg] + + Synopsis [https://github.com/arminbiere/cadical] + + Author [Integrated into ABC by Yukio Miyasaka] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cadicalSolver.h,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#ifndef ABC_SAT_CADICAL_SOLVER_H_ +#define ABC_SAT_CADICAL_SOLVER_H_ + +//////////////////////////////////////////////////////////////////////// +/// INCLUDES /// +//////////////////////////////////////////////////////////////////////// + +#include "aig/gia/gia.h" +#include "sat/cnf/cnf.h" + +//////////////////////////////////////////////////////////////////////// +/// PARAMETERS /// +//////////////////////////////////////////////////////////////////////// + +ABC_NAMESPACE_HEADER_START + +//////////////////////////////////////////////////////////////////////// +/// BASIC TYPES /// +//////////////////////////////////////////////////////////////////////// + +typedef struct cadical_solver_ cadical_solver; +struct cadical_solver_ +{ + void* p; + int nVars; + Vec_Int_t* vAssumptions; + Vec_Int_t* vCore; +}; + + +//////////////////////////////////////////////////////////////////////// +/// MACRO DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +extern cadical_solver* cadical_solver_new(void); +extern void cadical_solver_delete(cadical_solver* s); +extern int cadical_solver_addclause(cadical_solver* s, int* begin, int* end); +extern int cadical_solver_solve(cadical_solver* s, int* begin, int* end, ABC_INT64_T nConfLimit, ABC_INT64_T nInsLimit, ABC_INT64_T nConfLimitGlobal, ABC_INT64_T nInsLimitGlobal); +extern int cadical_solver_final(cadical_solver* s, int** ppArray); +extern int cadical_solver_nvars(cadical_solver* s); +extern int cadical_solver_addvar(cadical_solver* s); +extern void cadical_solver_setnvars(cadical_solver* s,int n); +extern int cadical_solver_get_var_value(cadical_solver* s, int v); +extern Vec_Int_t * cadical_solve_cnf( Cnf_Dat_t * pCnf, char * pArgs, int nConfs, int nTimeLimit, int fSat, int fUnsat, int fPrintCex, int fVerbose ); + +ABC_NAMESPACE_HEADER_END + +#endif + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// diff --git a/src/sat/cadical/cadicalTest.c b/src/sat/cadical/cadicalTest.c new file mode 100644 index 000000000..f3919160b --- /dev/null +++ b/src/sat/cadical/cadicalTest.c @@ -0,0 +1,210 @@ +/**CFile**************************************************************** + + FileName [cadicalTest.c] + + SystemName [ABC: Logic synthesis and verification system.] + + PackageName [] + + Synopsis [] + + Author [Alan Mishchenko] + + Affiliation [UC Berkeley] + + Date [Ver. 1.0. Started - June 20, 2005.] + + Revision [$Id: cadicalTest.c,v 1.00 2005/06/20 00:00:00 alanmi Exp $] + +***********************************************************************/ + +#include "cadicalSolver.h" + +ABC_NAMESPACE_IMPL_START + +//////////////////////////////////////////////////////////////////////// +/// DECLARATIONS /// +//////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////// +/// FUNCTION DEFINITIONS /// +//////////////////////////////////////////////////////////////////////// +void cadical_solver_test() { + int RetValue; + int Lits[3]; + // test 1 + { + cadical_solver *pSat = cadical_solver_new(); + int a = cadical_solver_addvar(pSat); + int b = cadical_solver_addvar(pSat); + int c = cadical_solver_addvar(pSat); + assert(cadical_solver_nvars(pSat) == 3); + Lits[0] = Abc_Var2Lit(a, 0); + Lits[1] = Abc_Var2Lit(b, 0); + Lits[2] = Abc_Var2Lit(c, 0); + printf("adding (a, b, c)\n"); + RetValue = cadical_solver_addclause(pSat, Lits, Lits + 3); + assert(RetValue); + Lits[0] = Abc_Var2Lit(a, 0); + Lits[1] = Abc_Var2Lit(b, 1); + printf("adding (a, !b)\n"); + RetValue = cadical_solver_addclause(pSat, Lits, Lits + 2); + assert(RetValue); + Lits[0] = Abc_Var2Lit(a, 1); + printf("adding (!a)\n"); + RetValue = cadical_solver_addclause(pSat, Lits, Lits + 1); + assert(RetValue); + RetValue = cadical_solver_solve(pSat, NULL, NULL, 0, 0, 0, 0); + printf("solved: %d\n", RetValue); + assert(RetValue == 1); + int a_val = cadical_solver_get_var_value(pSat, a); + int b_val = cadical_solver_get_var_value(pSat, b); + int c_val = cadical_solver_get_var_value(pSat, c); + printf("a = %d, b = %d, c = %d\n", a_val, b_val, c_val); + assert(a_val == 0); + assert(b_val == 0); + assert(c_val == 1); + cadical_solver_delete(pSat); + printf("test 1 passed\n"); + } + // test 2 + { + cadical_solver *pSat = cadical_solver_new(); + cadical_solver_setnvars(pSat, 2); + assert(cadical_solver_nvars(pSat) == 2); + Lits[0] = Abc_Var2Lit(0, 0); + Lits[1] = Abc_Var2Lit(1, 0); + printf("adding (x0, x1)\n"); + RetValue = cadical_solver_addclause(pSat, Lits, Lits + 2); + assert(RetValue); + Lits[0] = Abc_Var2Lit(0, 0); + Lits[1] = Abc_Var2Lit(1, 1); + printf("adding (x0, !x1)\n"); + RetValue = cadical_solver_addclause(pSat, Lits, Lits + 2); + assert(RetValue); + Lits[0] = Abc_Var2Lit(0, 1); + Lits[1] = Abc_Var2Lit(1, 1); + printf("adding (!x0, !x1)\n"); + RetValue = cadical_solver_addclause(pSat, Lits, Lits + 2); + assert(RetValue); + RetValue = cadical_solver_solve(pSat, NULL, NULL, 0, 0, 0, 0); + printf("solved: %d\n", RetValue); + assert(RetValue == 1); + printf("x0 = %d, x1 = %d\n", cadical_solver_get_var_value(pSat, 0), cadical_solver_get_var_value(pSat, 1)); + assert(cadical_solver_get_var_value(pSat, 0) == 1); + assert(cadical_solver_get_var_value(pSat, 1) == 0); + cadical_solver_delete(pSat); + printf("test 2 passed\n"); + } + // test 3 + { + cadical_solver *pSat = cadical_solver_new(); + cadical_solver_setnvars(pSat, 3); + assert(cadical_solver_nvars(pSat) == 3); + Lits[0] = Abc_Var2Lit(0, 1); + Lits[1] = Abc_Var2Lit(1, 0); + Lits[2] = Abc_Var2Lit(2, 1); + printf("adding (!x0, x1, !x2)\n"); + RetValue = cadical_solver_addclause(pSat, Lits, Lits + 3); + assert(RetValue); + Lits[0] = Abc_Var2Lit(0, 0); + printf("adding (x0)\n"); + RetValue = cadical_solver_addclause(pSat, Lits, Lits + 1); + assert(RetValue); + Lits[0] = Abc_Var2Lit(1, 1); + printf("adding (!x1)\n"); + RetValue = cadical_solver_addclause(pSat, Lits, Lits + 1); + assert(RetValue); + Lits[0] = Abc_Var2Lit(2, 0); + printf("adding (x2)\n"); + RetValue = cadical_solver_addclause(pSat, Lits, Lits + 1); + RetValue = cadical_solver_solve(pSat, NULL, NULL, 0, 0, 0, 0); + printf("solved: %d\n", RetValue); + assert(RetValue == -1); + cadical_solver_delete(pSat); + printf("test 3 passed\n"); + } + // test 4 + { + cadical_solver *pSat = cadical_solver_new(); + cadical_solver_setnvars(pSat, 3); + assert(cadical_solver_nvars(pSat) == 3); + Lits[0] = Abc_Var2Lit(0, 1); + Lits[1] = Abc_Var2Lit(1, 0); + Lits[2] = Abc_Var2Lit(2, 1); + printf("adding (!x0, x1, !x2)\n"); + RetValue = cadical_solver_addclause(pSat, Lits, Lits + 3); + assert(RetValue); + Lits[0] = Abc_Var2Lit(0, 0); + printf("adding (x0)\n"); + RetValue = cadical_solver_addclause(pSat, Lits, Lits + 1); + assert(RetValue); + Lits[0] = Abc_Var2Lit(1, 1); + printf("assume (!x1)\n"); + RetValue = cadical_solver_solve(pSat, Lits, Lits + 1, 0, 0, 0, 0); + printf("solved: %d\n", RetValue); + assert(RetValue == 1); + printf("x0 = %d, x1 = %d, x2 = %d\n", cadical_solver_get_var_value(pSat, 0), cadical_solver_get_var_value(pSat, 1), cadical_solver_get_var_value(pSat, 2)); + assert(cadical_solver_get_var_value(pSat, 0) == 1); + assert(cadical_solver_get_var_value(pSat, 1) == 0); + assert(cadical_solver_get_var_value(pSat, 2) == 0); + cadical_solver_delete(pSat); + printf("test 4 passed\n"); + } + // test 5 + { + cadical_solver *pSat = cadical_solver_new(); + cadical_solver_setnvars(pSat, 3); + assert(cadical_solver_nvars(pSat) == 3); + Lits[0] = Abc_Var2Lit(0, 1); + Lits[1] = Abc_Var2Lit(1, 0); + Lits[2] = Abc_Var2Lit(2, 1); + printf("adding (!x0, x1, !x2)\n"); + RetValue = cadical_solver_addclause(pSat, Lits, Lits + 3); + assert(RetValue); + Lits[0] = Abc_Var2Lit(0, 1); + Lits[1] = Abc_Var2Lit(1, 1); + Lits[2] = Abc_Var2Lit(2, 1); + printf("adding (!x0, !x1, !x2)\n"); + RetValue = cadical_solver_addclause(pSat, Lits, Lits + 3); + assert(RetValue); + Lits[0] = Abc_Var2Lit(0, 0); + printf("adding (x0)\n"); + RetValue = cadical_solver_addclause(pSat, Lits, Lits + 1); + assert(RetValue); + Lits[0] = Abc_Var2Lit(1, 0); + Lits[1] = Abc_Var2Lit(2, 0); + printf("assume (x1, x2)\n"); + RetValue = cadical_solver_solve(pSat, Lits, Lits + 2, 0, 0, 0, 0); + printf("solved: %d\n", RetValue); + assert(RetValue == -1); + int *pCore; + int nSize = cadical_solver_final(pSat, &pCore); + printf("core: "); + for(int i = 0; i < nSize; i++) { + if(i) { + printf(", "); + } + if(Abc_LitIsCompl(pCore[i])) { + printf("!"); + } + printf("x%d", Abc_Lit2Var(pCore[i])); + } + printf("\n"); + int neg_x2_in_core = 0; + for(int i = 0; i < nSize; i++) { + if(Abc_LitIsCompl(pCore[i]) && Abc_Lit2Var(pCore[i]) == 2) { + neg_x2_in_core = 1; + } + } + assert(neg_x2_in_core); + cadical_solver_delete(pSat); + printf("test 5 passed\n"); + } +} + +//////////////////////////////////////////////////////////////////////// +/// END OF FILE /// +//////////////////////////////////////////////////////////////////////// + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/analyze.cpp b/src/sat/cadical/cadical_analyze.cpp similarity index 91% rename from src/sat/cadical/analyze.cpp rename to src/sat/cadical/cadical_analyze.cpp index 667d59633..74b67fb06 100644 --- a/src/sat/cadical/analyze.cpp +++ b/src/sat/cadical/cadical_analyze.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -13,7 +17,7 @@ namespace CaDiCaL { /*------------------------------------------------------------------------*/ void Internal::learn_empty_clause () { - assert (!unsat); + CADICAL_assert (!unsat); build_chain_for_empty (); LOG ("learned empty clause"); external->check_learned_empty_clause (); @@ -29,7 +33,7 @@ void Internal::learn_empty_clause () { } void Internal::learn_unit_clause (int lit) { - assert (!unsat); + CADICAL_assert (!unsat); LOG ("learned unit clause %d, stored at position %d", lit, vlit (lit)); external->check_learned_unit_clause (lit); int64_t id = ++clause_id; @@ -50,13 +54,13 @@ void Internal::learn_unit_clause (int lit) { // whether the 'queue.assigned' pointer has to be moved in 'unassign'. void Internal::bump_queue (int lit) { - assert (opts.bump); + CADICAL_assert (opts.bump); const int idx = vidx (lit); if (!links[idx].next) return; queue.dequeue (links, idx); queue.enqueue (links, idx); - assert (stats.bumped != INT64_MAX); + CADICAL_assert (stats.bumped != INT64_MAX); btab[idx] = ++stats.bumped; LOG ("moved to front variable %d and bumped to %" PRId64 "", idx, btab[idx]); @@ -74,7 +78,7 @@ void Internal::bump_queue (int lit) { // and simply put a hard limit here. It is less elegant but easy to port. static inline bool evsids_limit_hit (double score) { - assert (sizeof (score) == 8); // assume IEEE 754 64-bit double + CADICAL_assert (sizeof (score) == 8); // assume IEEE 754 64-bit double return score > 1e150; // MAX_DOUBLE is around 1.8e308 } @@ -92,7 +96,7 @@ void Internal::rescale_variable_scores () { } PHASE ("rescore", stats.rescored, "rescoring %d variable scores by 1/%g", max_var, divider); - assert (divider > 0); + CADICAL_assert (divider > 0); double factor = 1.0 / divider; for (auto idx : vars) stab[idx] *= factor; @@ -103,19 +107,19 @@ void Internal::rescale_variable_scores () { } void Internal::bump_variable_score (int lit) { - assert (opts.bump); + CADICAL_assert (opts.bump); int idx = vidx (lit); double old_score = score (idx); - assert (!evsids_limit_hit (old_score)); + CADICAL_assert (!evsids_limit_hit (old_score)); double new_score = old_score + score_inc; if (evsids_limit_hit (new_score)) { LOG ("bumping %g score of %d hits EVSIDS score limit", old_score, idx); rescale_variable_scores (); old_score = score (idx); - assert (!evsids_limit_hit (old_score)); + CADICAL_assert (!evsids_limit_hit (old_score)); new_score = old_score + score_inc; } - assert (!evsids_limit_hit (new_score)); + CADICAL_assert (!evsids_limit_hit (new_score)); LOG ("new %g score of %d", new_score, idx); score (idx) = new_score; if (scores.contains (idx)) @@ -135,8 +139,8 @@ void Internal::bump_variable (int lit) { // factor (if we are currently using scores). void Internal::bump_variable_score_inc () { - assert (use_scores ()); - assert (!evsids_limit_hit (score_inc)); + CADICAL_assert (use_scores ()); + CADICAL_assert (!evsids_limit_hit (score_inc)); double f = 1e3 / opts.scorefactor; double new_score_inc = score_inc * f; if (evsids_limit_hit (new_score_inc)) { @@ -145,7 +149,7 @@ void Internal::bump_variable_score_inc () { rescale_variable_scores (); new_score_inc = score_inc * f; } - assert (!evsids_limit_hit (new_score_inc)); + CADICAL_assert (!evsids_limit_hit (new_score_inc)); LOG ("bumped score increment from %g to %g with factor %g", score_inc, new_score_inc, f); score_inc = new_score_inc; @@ -174,7 +178,7 @@ struct analyze_bumped_smaller { void Internal::bump_variables () { - assert (opts.bump); + CADICAL_assert (opts.bump); START (bump); @@ -208,7 +212,7 @@ int Internal::recompute_glue (Clause *c) { const int64_t stamp = ++stats.recomputed; for (const auto &lit : *c) { int level = var (lit).level; - assert (gtab[level] <= stamp); + CADICAL_assert (gtab[level] <= stamp); if (gtab[level] == stamp) continue; gtab[level] = stamp; @@ -252,7 +256,7 @@ void Internal::bump_clause2 (Clause *c) { bump_clause (c); } inline void Internal::analyze_literal (int lit, int &open, int &resolvent_size, int &antecedent_size) { - assert (lit); + CADICAL_assert (lit); Var &v = var (lit); Flags &f = flags (lit); @@ -261,7 +265,7 @@ inline void Internal::analyze_literal (int lit, int &open, return; f.seen = true; unit_analyzed.push_back (lit); - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); int64_t id = unit_id (-lit); unit_chain.push_back (id); return; @@ -272,10 +276,10 @@ inline void Internal::analyze_literal (int lit, int &open, // before marking as seen, get reason and check for missed unit - assert (val (lit) < 0); - assert (v.level <= level); + CADICAL_assert (val (lit) < 0); + CADICAL_assert (v.level <= level); if (v.reason == external_reason) { - assert (!opts.exteagerreasons); + CADICAL_assert (!opts.exteagerreasons); v.reason = learn_external_reason_clause (-lit, 0, true); if (!v.reason) { // actually a unit --antecedent_size; @@ -284,10 +288,10 @@ inline void Internal::analyze_literal (int lit, int &open, return; f.seen = true; unit_analyzed.push_back (lit); - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); const unsigned uidx = vlit (-lit); int64_t id = unit_clauses (uidx); - assert (id); + CADICAL_assert (id); unit_chain.push_back (id); return; } @@ -296,7 +300,7 @@ inline void Internal::analyze_literal (int lit, int &open, f.seen = true; analyzed.push_back (lit); - assert (v.reason != external_reason); + CADICAL_assert (v.reason != external_reason); if (v.level < level) clause.push_back (lit); Level &l = control[v.level]; @@ -315,8 +319,8 @@ inline void Internal::analyze_literal (int lit, int &open, inline void Internal::analyze_reason (int lit, Clause *reason, int &open, int &resolvent_size, int &antecedent_size) { - assert (reason); - assert (reason != external_reason); + CADICAL_assert (reason); + CADICAL_assert (reason != external_reason); bump_clause (reason); if (lrat) lrat_chain.push_back (reason->id); @@ -339,8 +343,8 @@ inline void Internal::analyze_reason (int lit, Clause *reason, int &open, // 30 million propagations, which then is increased geometrically by 10%. inline bool Internal::bump_also_reason_literal (int lit) { - assert (lit); - assert (val (lit) < 0); + CADICAL_assert (lit); + CADICAL_assert (val (lit) < 0); Flags &f = flags (lit); if (f.seen) return false; @@ -357,10 +361,10 @@ inline bool Internal::bump_also_reason_literal (int lit) { inline void Internal::bump_also_reason_literals (int lit, int depth_limit, size_t analyzed_limit) { - assert (lit); - assert (depth_limit > 0); + CADICAL_assert (lit); + CADICAL_assert (depth_limit > 0); const Var &v = var (lit); - assert (val (lit)); + CADICAL_assert (val (lit)); if (!v.level) return; Clause *reason = v.reason; @@ -381,7 +385,7 @@ inline void Internal::bump_also_reason_literals (int lit, int depth_limit, } inline void Internal::bump_also_all_reason_literals () { - assert (opts.bump); + CADICAL_assert (opts.bump); if (!opts.bumpreason) return; if (averages.current.decisions > opts.bumpreasonrate) { @@ -395,7 +399,7 @@ inline void Internal::bump_also_all_reason_literals () { delay[stable].bumpreasons.limit--; return; } - assert (opts.bumpreasondepth > 0); + CADICAL_assert (opts.bumpreasondepth > 0); const int depth_limit = opts.bumpreasondepth + stable; size_t saved_analyzed = analyzed.size (); size_t analyzed_limit = saved_analyzed * opts.bumpreasonlimit; @@ -409,7 +413,7 @@ inline void Internal::bump_also_all_reason_literals () { for (size_t i = saved_analyzed; i != analyzed.size (); i++) { const int lit = analyzed[i]; Flags &f = flags (lit); - assert (f.seen); + CADICAL_assert (f.seen); f.seen = false; } delay[stable].bumpreasons.interval++; @@ -428,12 +432,12 @@ void Internal::clear_unit_analyzed_literals () { LOG ("clearing %zd unit analyzed literals", unit_analyzed.size ()); for (const auto &lit : unit_analyzed) { Flags &f = flags (lit); - assert (f.seen); - assert (!var (lit).level); + CADICAL_assert (f.seen); + CADICAL_assert (!var (lit).level); f.seen = false; - assert (!f.keep); - assert (!f.poison); - assert (!f.removable); + CADICAL_assert (!f.keep); + CADICAL_assert (!f.poison); + CADICAL_assert (!f.removable); } unit_analyzed.clear (); } @@ -442,11 +446,11 @@ void Internal::clear_analyzed_literals () { LOG ("clearing %zd analyzed literals", analyzed.size ()); for (const auto &lit : analyzed) { Flags &f = flags (lit); - assert (f.seen); + CADICAL_assert (f.seen); f.seen = false; - assert (!f.keep); - assert (!f.poison); - assert (!f.removable); + CADICAL_assert (!f.keep); + CADICAL_assert (!f.poison); + CADICAL_assert (!f.removable); } analyzed.clear (); #if 0 // to expensive, even for debugging mode @@ -454,7 +458,7 @@ void Internal::clear_analyzed_literals () { return; for (auto idx : vars) { Flags &f = flags (idx); - assert (!f.seen); + CADICAL_assert (!f.seen); } #endif } @@ -517,7 +521,7 @@ Clause *Internal::new_driving_clause (const int glue, int &jump) { } else { - assert (clause.size () > 1); + CADICAL_assert (clause.size () > 1); // We have to get the last assigned literals into the watch position. // Sorting all literals with respect to reverse assignment order is @@ -545,7 +549,7 @@ Clause *Internal::new_driving_clause (const int glue, int &jump) { // not have to fix the clause inline int Internal::otfs_find_backtrack_level (int &forced) { - assert (opts.otfs); + CADICAL_assert (opts.otfs); int res = 0; for (const auto &lit : *conflict) { @@ -570,8 +574,8 @@ inline int Internal::otfs_find_backtrack_level (int &forced) { inline int Internal::find_conflict_level (int &forced) { - assert (conflict); - assert (opts.chrono || opts.otfs || external_prop); + CADICAL_assert (conflict); + CADICAL_assert (opts.chrono || opts.otfs || external_prop); int res = 0, count = 0; @@ -649,7 +653,7 @@ inline int Internal::determine_actual_backtrack_level (int jump) { int res; - assert (level > jump); + CADICAL_assert (level > jump); if (!opts.chrono) { res = jump; @@ -693,7 +697,7 @@ inline int Internal::determine_actual_backtrack_level (int jump) { } LOG ("best variable bumped %" PRId64 "", bumped (best_idx)); } - assert (best_idx); + CADICAL_assert (best_idx); LOG ("best variable %d at trail position %d", best_idx, best_pos); // Now find the frame and decision level in the control stack of that @@ -725,7 +729,7 @@ inline int Internal::determine_actual_backtrack_level (int jump) { /*------------------------------------------------------------------------*/ void Internal::eagerly_subsume_recently_learned_clauses (Clause *c) { - assert (opts.eagersubsume); + CADICAL_assert (opts.eagersubsume); LOG (c, "trying eager subsumption with"); mark (c); int64_t lim = stats.eagertried + opts.eagersubsumelim; @@ -767,20 +771,20 @@ void Internal::eagerly_subsume_recently_learned_clauses (Clause *c) { /*------------------------------------------------------------------------*/ Clause *Internal::on_the_fly_strengthen (Clause *new_conflict, int uip) { - assert (new_conflict); - assert (new_conflict->size > 2); + CADICAL_assert (new_conflict); + CADICAL_assert (new_conflict->size > 2); LOG (new_conflict, "applying OTFS on lit %d", uip); auto sorted = std::vector<int> (); sorted.reserve (new_conflict->size); - assert (sorted.empty ()); + CADICAL_assert (sorted.empty ()); ++stats.otfs.strengthened; int *lits = new_conflict->literals; - assert (lits[0] == uip || lits[1] == uip); + CADICAL_assert (lits[0] == uip || lits[1] == uip); const int other_init = lits[0] ^ lits[1] ^ uip; - assert (mini_chain.empty ()); + CADICAL_assert (mini_chain.empty ()); const int old_size = new_conflict->size; int new_size = 0; @@ -793,7 +797,7 @@ Clause *Internal::on_the_fly_strengthen (Clause *new_conflict, int uip) { LOG (new_conflict, "removing all units in"); - assert (lits[0] == uip || lits[1] == uip); + CADICAL_assert (lits[0] == uip || lits[1] == uip); const int other = lits[0] ^ lits[1] ^ uip; lits[0] = other; lits[1] = lits[--new_size]; @@ -803,9 +807,9 @@ Clause *Internal::on_the_fly_strengthen (Clause *new_conflict, int uip) { remove_watch (watches (other_init), new_conflict); remove_watch (watches (uip), new_conflict); - assert (!lrat || lrat_chain.back () == new_conflict->id); + CADICAL_assert (!lrat || lrat_chain.back () == new_conflict->id); if (lrat) { - assert (!lrat_chain.empty ()); + CADICAL_assert (!lrat_chain.empty ()); for (const auto &id : unit_chain) { mini_chain.push_back (id); } @@ -819,16 +823,16 @@ Clause *Internal::on_the_fly_strengthen (Clause *new_conflict, int uip) { clear_unit_analyzed_literals (); unit_chain.clear (); } - assert (unit_analyzed.empty ()); + CADICAL_assert (unit_analyzed.empty ()); // sort the clause { int highest_pos = 0; int highest_level = 0; for (int i = 1; i < new_size; i++) { const unsigned other = lits[i]; - assert (val (other) < 0); + CADICAL_assert (val (other) < 0); const int level = var (other).level; - assert (level); + CADICAL_assert (level); LOG ("checking %d", other); if (level <= highest_level) continue; @@ -842,11 +846,11 @@ Clause *Internal::on_the_fly_strengthen (Clause *new_conflict, int uip) { if (new_size == 1) { LOG (new_conflict, "new size = 1, so interrupting"); - assert (!opts.exteagerreasons); + CADICAL_assert (!opts.exteagerreasons); return 0; } else { otfs_strengthen_clause (new_conflict, uip, new_size, sorted); - assert (new_size == new_conflict->size); + CADICAL_assert (new_size == new_conflict->size); } } @@ -867,7 +871,7 @@ Clause *Internal::on_the_fly_strengthen (Clause *new_conflict, int uip) { inline void Internal::otfs_subsume_clause (Clause *subsuming, Clause *subsumed) { stats.subsumed++; - assert (subsuming->size <= subsumed->size); + CADICAL_assert (subsuming->size <= subsumed->size); LOG (subsumed, "subsumed"); if (subsumed->redundant) stats.subred++; @@ -885,9 +889,9 @@ inline void Internal::otfs_subsume_clause (Clause *subsuming, stats.current.irredundant++; stats.added.irredundant++; stats.irrlits += subsuming->size; - assert (stats.current.redundant > 0); + CADICAL_assert (stats.current.redundant > 0); stats.current.redundant--; - assert (stats.added.redundant > 0); + CADICAL_assert (stats.added.redundant > 0); stats.added.redundant--; // ... and keep 'stats.added.total'. } @@ -899,7 +903,7 @@ inline void Internal::otfs_subsume_clause (Clause *subsuming, void Internal::otfs_strengthen_clause (Clause *c, int lit, int new_size, const std::vector<int> &old) { stats.strengthened++; - assert (c->size > 2); + CADICAL_assert (c->size > 2); (void) shrink_clause (c, new_size); if (proof) { proof->otfs_strengthen_clause (c, old, mini_chain); @@ -940,11 +944,11 @@ void Internal::analyze () { START (analyze); - assert (conflict); - assert (lrat_chain.empty ()); - assert (unit_chain.empty ()); - assert (unit_analyzed.empty ()); - assert (clause.empty ()); + CADICAL_assert (conflict); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (unit_chain.empty ()); + CADICAL_assert (unit_analyzed.empty ()); + CADICAL_assert (clause.empty ()); // First update moving averages of trail height at conflict. // @@ -975,8 +979,8 @@ void Internal::analyze () { if (forced) { - assert (forced); - assert (conflict_level > 0); + CADICAL_assert (forced); + CADICAL_assert (conflict_level > 0); LOG ("single highest level literal %d", forced); // The pseudo code in the SAT'18 paper actually backtracks to the @@ -1042,8 +1046,8 @@ void Internal::analyze () { Clause *reason = conflict; LOG (reason, "analyzing conflict"); - assert (clause.empty ()); - assert (lrat_chain.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (lrat_chain.empty ()); const auto &t = &trail; int i = t->size (); // Start at end-of-trail. @@ -1060,43 +1064,43 @@ void Internal::analyze () { analyze_reason (uip, reason, open, resolvent_size, antecedent_size); if (resolved == 0) conflict_size = antecedent_size - 1; - assert (resolvent_size == open + (int) clause.size ()); + CADICAL_assert (resolvent_size == open + (int) clause.size ()); if (otfs && resolved > 0 && antecedent_size > 2 && resolvent_size < antecedent_size) { - assert (reason != conflict); + CADICAL_assert (reason != conflict); LOG (analyzed, "found candidate for OTFS conflict"); LOG (clause, "found candidate for OTFS conflict"); LOG (reason, "found candidate (size %d) for OTFS resolvent", antecedent_size); const int other = reason->literals[0] ^ reason->literals[1] ^ uip; - assert (other != uip); + CADICAL_assert (other != uip); reason = on_the_fly_strengthen (reason, uip); if (opts.bump) bump_variables (); - assert (conflict_size); + CADICAL_assert (conflict_size); if (!reason) { uip = -other; - assert (open == 1); + CADICAL_assert (open == 1); LOG ("clause is actually unit %d, stopping", -uip); reverse (begin (mini_chain), end (mini_chain)); for (auto id : mini_chain) lrat_chain.push_back (id); mini_chain.clear (); clear_analyzed_levels (); - assert (!opts.exteagerreasons); + CADICAL_assert (!opts.exteagerreasons); clause.clear (); break; } - assert (conflict_size >= 2); + CADICAL_assert (conflict_size >= 2); if (resolved == 1 && resolvent_size < conflict_size) { // here both clauses are part of the CNF, so one subsumes the other otfs_subsume_clause (reason, conflict); LOG (reason, "changing conflict to"); --conflict_size; - assert (conflict_size == reason->size); + CADICAL_assert (conflict_size == reason->size); ++stats.otfs.subsumed; ++stats.subsumed; } @@ -1134,14 +1138,14 @@ void Internal::analyze () { open = 0; analyze_reason (0, reason, open, resolvent_size, antecedent_size); conflict_size = antecedent_size - 1; - assert (open > 1); + CADICAL_assert (open > 1); } ++resolved; uip = 0; while (!uip) { - assert (i > 0); + CADICAL_assert (i > 0); const int lit = (*t)[--i]; if (!flags (lit).seen) continue; @@ -1152,13 +1156,13 @@ void Internal::analyze () { break; reason = var (uip).reason; if (reason == external_reason) { - assert (!opts.exteagerreasons); + CADICAL_assert (!opts.exteagerreasons); reason = learn_external_reason_clause (-uip, 0, true); var (uip).reason = reason; } - assert (reason != external_reason); + CADICAL_assert (reason != external_reason); LOG (reason, "analyzing %d reason", uip); - assert (resolvent_size); + CADICAL_assert (resolvent_size); --resolvent_size; } LOG ("first UIP %d", uip); @@ -1173,7 +1177,7 @@ void Internal::analyze () { UPDATE_AVERAGE (averages.current.glue.slow, glue); stats.learned.literals += size; stats.learned.clauses++; - assert (glue < size); + CADICAL_assert (glue < size); // up to this point lrat_chain contains the proof for current clause in // reversed order. in minimize and shrink the clause is changed and @@ -1277,3 +1281,5 @@ void Internal::iterate () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/arena.cpp b/src/sat/cadical/cadical_arena.cpp similarity index 85% rename from src/sat/cadical/arena.cpp rename to src/sat/cadical/cadical_arena.cpp index e8e2eea64..a8a9b183f 100644 --- a/src/sat/cadical/arena.cpp +++ b/src/sat/cadical/cadical_arena.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { Arena::Arena (Internal *i) { @@ -14,7 +18,7 @@ Arena::~Arena () { void Arena::prepare (size_t bytes) { LOG ("preparing 'to' space of arena with %zd bytes", bytes); - assert (!to.start); + CADICAL_assert (!to.start); to.top = to.start = new char[bytes]; to.end = to.start + bytes; } @@ -28,3 +32,5 @@ void Arena::swap () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/assume.cpp b/src/sat/cadical/cadical_assume.cpp similarity index 88% rename from src/sat/cadical/assume.cpp rename to src/sat/cadical/cadical_assume.cpp index a49d36af0..e7d5b79b7 100644 --- a/src/sat/cadical/assume.cpp +++ b/src/sat/cadical/cadical_assume.cpp @@ -1,6 +1,10 @@ +#include "global.h" + #include "internal.hpp" #include "options.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { // Failed literal handling as pioneered by MiniSAT. This first function @@ -27,26 +31,26 @@ void Internal::assume (int lit) { // for non-lrat use BFS. TODO: maybe derecursify to avoid stack overflow // void Internal::assume_analyze_literal (int lit) { - assert (lit); + CADICAL_assert (lit); Flags &f = flags (lit); if (f.seen) return; f.seen = true; analyzed.push_back (lit); Var &v = var (lit); - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); if (v.reason == external_reason) { v.reason = wrapped_learn_external_reason_clause (-lit); - assert (v.reason || !v.level); + CADICAL_assert (v.reason || !v.level); } - assert (v.reason != external_reason); + CADICAL_assert (v.reason != external_reason); if (!v.level) { int64_t id = unit_id (-lit); lrat_chain.push_back (id); return; } if (v.reason) { - assert (v.level); + CADICAL_assert (v.level); LOG (v.reason, "analyze reason"); for (const auto &other : *v.reason) { assume_analyze_literal (other); @@ -54,16 +58,16 @@ void Internal::assume_analyze_literal (int lit) { lrat_chain.push_back (v.reason->id); return; } - assert (assumed (-lit)); + CADICAL_assert (assumed (-lit)); LOG ("failed assumption %d", -lit); clause.push_back (lit); } void Internal::assume_analyze_reason (int lit, Clause *reason) { - assert (reason); - assert (lrat_chain.empty ()); - assert (reason != external_reason); - assert (lrat); + CADICAL_assert (reason); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (reason != external_reason); + CADICAL_assert (lrat); for (const auto &other : *reason) if (other != lit) assume_analyze_literal (other); @@ -80,11 +84,11 @@ void Internal::failing () { LOG ("analyzing failing assumptions"); - assert (analyzed.empty ()); - assert (clause.empty ()); - assert (lrat_chain.empty ()); - assert (!marked_failed); - assert (!conflict_id); + CADICAL_assert (analyzed.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (!marked_failed); + CADICAL_assert (!conflict_id); if (!unsat_constraint) { // Search for failing assumptions in the (internal) assumption stack. @@ -130,7 +134,7 @@ void Internal::failing () { for (const auto &other : *ev.reason) { if (other == -lit) continue; - assert (val (other)); + CADICAL_assert (val (other)); int tmp = var (other).level; if (tmp > ev.level) ev.level = tmp; @@ -141,7 +145,7 @@ void Internal::failing () { break; } } - assert (v.reason != external_reason); + CADICAL_assert (v.reason != external_reason); if (!v.reason) { failed_clashing = lit; efailed = elit; @@ -152,7 +156,7 @@ void Internal::failing () { } } - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); // Get the 'failed' assumption from one of the three cases. int failed; @@ -162,25 +166,25 @@ void Internal::failing () { failed = failed_clashing; else failed = first_failed; - assert (failed); - assert (efailed); + CADICAL_assert (failed); + CADICAL_assert (efailed); // In any case mark literal 'failed' as failed assumption. { Flags &f = flags (failed); const unsigned bit = bign (failed); - assert (!(f.failed & bit)); + CADICAL_assert (!(f.failed & bit)); f.failed |= bit; } // First case (1). if (failed_unit) { - assert (failed == failed_unit); + CADICAL_assert (failed == failed_unit); LOG ("root-level falsified assumption %d", failed); if (proof) { if (lrat) { unsigned eidx = (efailed > 0) + 2u * (unsigned) abs (efailed); - assert ((size_t) eidx < external->ext_units.size ()); + CADICAL_assert ((size_t) eidx < external->ext_units.size ()); const int64_t id = external->ext_units[eidx]; if (id) { lrat_chain.push_back (id); @@ -198,11 +202,11 @@ void Internal::failing () { // Second case (2). if (failed_clashing) { - assert (failed == failed_clashing); + CADICAL_assert (failed == failed_clashing); LOG ("clashing assumptions %d and %d", failed, -failed); Flags &f = flags (-failed); const unsigned bit = bign (-failed); - assert (!(f.failed & bit)); + CADICAL_assert (!(f.failed & bit)); f.failed |= bit; if (proof) { vector<int> clash = {externalize (failed), externalize (-failed)}; @@ -217,8 +221,8 @@ void Internal::failing () { "%d", first_failed, failed_level); - assert (first_failed); - assert (failed_level > 0); + CADICAL_assert (first_failed); + CADICAL_assert (failed_level > 0); // The 'analyzed' stack serves as working stack for a BFS through the // implication graph until decisions, which are all assumptions, or @@ -227,9 +231,9 @@ void Internal::failing () { { LOG ("failed assumption %d", first_failed); Flags &f = flags (first_failed); - assert (!f.seen); + CADICAL_assert (!f.seen); f.seen = true; - assert (f.failed & bign (first_failed)); + CADICAL_assert (f.failed & bign (first_failed)); analyzed.push_back (-first_failed); clause.push_back (-first_failed); } @@ -239,7 +243,7 @@ void Internal::failing () { // collected. for (auto lit : constraint) { lit *= -1; - assert (lit != INT_MIN); + CADICAL_assert (lit != INT_MIN); flags (lit).seen = true; analyzed.push_back (lit); } @@ -271,7 +275,7 @@ void Internal::failing () { size_t next = 0; while (next < analyzed.size ()) { const int lit = analyzed[next++]; - assert (val (lit) > 0); + CADICAL_assert (val (lit) > 0); Var &v = var (lit); if (!v.level) continue; @@ -282,38 +286,38 @@ void Internal::failing () { continue; } } - assert (v.reason != external_reason); + CADICAL_assert (v.reason != external_reason); if (v.reason) { - assert (v.level); + CADICAL_assert (v.level); LOG (v.reason, "analyze reason"); for (const auto &other : *v.reason) { Flags &f = flags (other); if (f.seen) continue; f.seen = true; - assert (val (other) < 0); + CADICAL_assert (val (other) < 0); analyzed.push_back (-other); } } else { - assert (assumed (lit)); + CADICAL_assert (assumed (lit)); LOG ("failed assumption %d", lit); clause.push_back (-lit); Flags &f = flags (lit); const unsigned bit = bign (lit); - assert (!(f.failed & bit)); + CADICAL_assert (!(f.failed & bit)); f.failed |= bit; } } clear_analyzed_literals (); } else if (!unsat_constraint) { // LRAT for case (3) - assert (clause.size () == 1); + CADICAL_assert (clause.size () == 1); const int lit = clause[0]; Var &v = var (lit); - assert (v.reason); + CADICAL_assert (v.reason); if (v.reason == external_reason) { // does this even happen? v.reason = wrapped_learn_external_reason_clause (lit); } - assert (v.reason != external_reason); + CADICAL_assert (v.reason != external_reason); if (v.reason) assume_analyze_reason (lit, v.reason); else { @@ -328,13 +332,13 @@ void Internal::failing () { } clear_analyzed_literals (); } else { // LRAT for unsat_constraint - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); clear_analyzed_literals (); for (auto lit : constraint) { // make sure nothing gets marked failed twice // also might shortcut the case where // lrat_chain is empty because clause is tautological - assert (lit != INT_MIN); + CADICAL_assert (lit != INT_MIN); assume_analyze_literal (lit); vector<int64_t> empty; vector<int> empty2; @@ -346,7 +350,7 @@ void Internal::failing () { const unsigned bit = bign (-ign); if (!(f.failed & bit)) { sum_constraints.push_back (ign); - assert (!(f.failed & bit)); + CADICAL_assert (!(f.failed & bit)); f.failed |= bit; } } @@ -386,7 +390,7 @@ void Internal::failing () { conclusion.push_back (clause_id); } } else { - assert (!lrat || (constraint.size () == constraint_clauses.size () && + CADICAL_assert (!lrat || (constraint.size () == constraint_clauses.size () && constraint.size () == constraint_chains.size ())); for (auto p = constraint.rbegin (); p != constraint.rend (); p++) { const auto &lit = *p; @@ -419,7 +423,7 @@ void Internal::failing () { for (auto &elit : econstraints) { if (lrat) { unsigned eidx = (elit > 0) + 2u * (unsigned) abs (elit); - assert ((size_t) eidx < external->ext_units.size ()); + CADICAL_assert ((size_t) eidx < external->ext_units.size ()); const int64_t id = external->ext_units[eidx]; if (id) { lrat_chain.push_back (id); @@ -463,7 +467,7 @@ void Internal::conclude_unsat () { return; concluded = true; if (!marked_failed) { - assert (conclusion.empty ()); + CADICAL_assert (conclusion.empty ()); if (!conflict_id) failing (); marked_failed = true; @@ -486,7 +490,7 @@ void Internal::reset_concluded () { concluded = false; } if (conflict_id) { - assert (conclusion.size () == 1); + CADICAL_assert (conclusion.size () == 1); return; } conclusion.clear (); @@ -548,7 +552,7 @@ struct sort_assumptions_smaller { // to the first place where the assumptions and the current trail differ. void Internal::sort_and_reuse_assumptions () { - assert (opts.ilbassumptions); + CADICAL_assert (opts.ilbassumptions); if (assumptions.empty ()) return; MSORT (opts.radixsortlim, assumptions.begin (), assumptions.end (), @@ -565,7 +569,7 @@ void Internal::sort_and_reuse_assumptions () { } const unsigned size = min (level + 1u, max_level + 1); - assert ((size_t) level == control.size () - 1); + CADICAL_assert ((size_t) level == control.size () - 1); LOG (assumptions, "sorted assumptions"); int target = 0; for (unsigned i = 1, j = 0; i < size;) { @@ -585,7 +589,7 @@ void Internal::sort_and_reuse_assumptions () { break; } ++i, ++j; - assert (var (lit).level == lev); + CADICAL_assert (var (lit).level == lev); if (l.decision == alit) { continue; } @@ -605,3 +609,5 @@ void Internal::sort_and_reuse_assumptions () { stats.assumptionsreused += level; } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/averages.cpp b/src/sat/cadical/cadical_averages.cpp similarity index 88% rename from src/sat/cadical/averages.cpp rename to src/sat/cadical/cadical_averages.cpp index 971598bd8..d40a98cdb 100644 --- a/src/sat/cadical/averages.cpp +++ b/src/sat/cadical/cadical_averages.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { void Internal::init_averages () { @@ -18,7 +22,7 @@ void Internal::init_averages () { INIT_EMA (averages.current.trail.fast, opts.ematrailfast); INIT_EMA (averages.current.trail.slow, opts.ematrailslow); - assert (!averages.swapped); + CADICAL_assert (!averages.swapped); } void Internal::swap_averages () { @@ -32,3 +36,5 @@ void Internal::swap_averages () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/backtrack.cpp b/src/sat/cadical/cadical_backtrack.cpp similarity index 91% rename from src/sat/cadical/backtrack.cpp rename to src/sat/cadical/cadical_backtrack.cpp index 3a70b2775..b3c519ba2 100644 --- a/src/sat/cadical/backtrack.cpp +++ b/src/sat/cadical/cadical_backtrack.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { // The global assignment stack can only be (partially) reset through @@ -8,7 +12,7 @@ namespace CaDiCaL { // specialization for 'probe' nor 'vivify' and thus it is shared. inline void Internal::unassign (int lit) { - assert (val (lit) > 0); + CADICAL_assert (val (lit) > 0); set_val (lit, 0); int idx = vidx (lit); @@ -74,7 +78,7 @@ void Internal::update_target_and_best () { /*------------------------------------------------------------------------*/ void Internal::backtrack (int new_level) { - assert (new_level <= level); + CADICAL_assert (new_level <= level); if (new_level == level) return; @@ -84,13 +88,13 @@ void Internal::backtrack (int new_level) { void Internal::backtrack_without_updating_phases (int new_level) { - assert (new_level <= level); + CADICAL_assert (new_level <= level); if (new_level == level) return; stats.backtracks++; - assert (num_assigned == trail.size ()); + CADICAL_assert (num_assigned == trail.size ()); const size_t assigned = control[new_level + 1].trail; @@ -127,7 +131,7 @@ 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). - assert (opts.chrono || external_prop || did_external_prop); + CADICAL_assert (opts.chrono || external_prop || did_external_prop); #ifdef LOGGING if (!v.level) LOG ("reassign %d @ 0 unit clause %d", lit, lit); @@ -154,7 +158,7 @@ void Internal::backtrack_without_updating_phases (int new_level) { propergated = 0; // Always go back to root-level. - assert (notified <= assigned + reassigned); + CADICAL_assert (notified <= assigned + reassigned); if (reassigned) { notify_assignments (); } @@ -162,12 +166,14 @@ void Internal::backtrack_without_updating_phases (int new_level) { control.resize (new_level + 1); level = new_level; if (tainted_literal) { - assert (opts.ilb); + CADICAL_assert (opts.ilb); if (!val (tainted_literal)) { tainted_literal = 0; } } - assert (num_assigned == trail.size ()); + CADICAL_assert (num_assigned == trail.size ()); } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/backward.cpp b/src/sat/cadical/cadical_backward.cpp similarity index 90% rename from src/sat/cadical/backward.cpp rename to src/sat/cadical/cadical_backward.cpp index e7228c521..5778029a5 100644 --- a/src/sat/cadical/backward.cpp +++ b/src/sat/cadical/cadical_backward.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -24,7 +28,7 @@ Clause *Eliminator::dequeue () { return 0; Clause *res = backward.front (); backward.pop (); - assert (res->enqueued); + CADICAL_assert (res->enqueued); res->enqueued = false; LOG (res, "backward dequeue"); return res; @@ -38,8 +42,8 @@ Eliminator::~Eliminator () { /*------------------------------------------------------------------------*/ void Internal::elim_backward_clause (Eliminator &eliminator, Clause *c) { - assert (opts.elimbackward); - assert (!c->redundant); + CADICAL_assert (opts.elimbackward); + CADICAL_assert (!c->redundant); if (c->garbage) return; LOG (c, "attempting backward subsumption and strengthening with"); @@ -47,7 +51,7 @@ void Internal::elim_backward_clause (Eliminator &eliminator, Clause *c) { unsigned size = 0; int best = 0; bool satisfied = false; - assert (mini_chain.empty ()); + CADICAL_assert (mini_chain.empty ()); for (const auto &lit : *c) { const signed char tmp = val (lit); if (tmp > 0) { @@ -70,7 +74,7 @@ void Internal::elim_backward_clause (Eliminator &eliminator, Clause *c) { } else if (len > (size_t) opts.elimocclim) { LOG ("skipping backward subsumption due to too many occurrences"); } else { - assert (len); + CADICAL_assert (len); LOG ("literal %d has smallest number of occurrences %zd", best, len); LOG ("marked %d literals in clause of size %d", size, c->size); for (auto &d : occs (best)) { @@ -117,9 +121,9 @@ void Internal::elim_backward_clause (Eliminator &eliminator, Clause *c) { stats.elimbwsub++; } else { int unit = 0; - assert (minimize_chain.empty ()); - assert (analyzed.empty ()); - assert (lrat_chain.empty ()); + CADICAL_assert (minimize_chain.empty ()); + CADICAL_assert (analyzed.empty ()); + CADICAL_assert (lrat_chain.empty ()); // figure out wether we strengthen c or get a new unit for (const auto &lit : *d) { const signed char tmp = val (lit); @@ -127,7 +131,7 @@ void Internal::elim_backward_clause (Eliminator &eliminator, Clause *c) { if (!lrat) continue; Flags &f = flags (lit); - assert (!f.seen); + CADICAL_assert (!f.seen); if (f.seen) continue; f.seen = true; @@ -151,7 +155,7 @@ void Internal::elim_backward_clause (Eliminator &eliminator, Clause *c) { // {c\d}U{d\c} otherwise just the unit ids from {c\d} for (const auto &lit : *c) { const signed char tmp = val (lit); - assert (tmp <= 0); + CADICAL_assert (tmp <= 0); if (tmp >= 0) continue; Flags &f = flags (lit); @@ -183,11 +187,11 @@ void Internal::elim_backward_clause (Eliminator &eliminator, Clause *c) { } else if (lrat) clear_analyzed_literals (); if (satisfied) { - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); mark_garbage (d); elim_update_removed_clause (eliminator, d); } else if (unit && unit != INT_MIN) { - assert (unit); + CADICAL_assert (unit); LOG (d, "unit %d through hyper unary resolution with", unit); assign_unit (unit); elim_propagate (eliminator, unit); @@ -198,7 +202,7 @@ void Internal::elim_backward_clause (Eliminator &eliminator, Clause *c) { remove_occs (occs (negated), d); elim_update_removed_lit (eliminator, negated); stats.elimbwstr++; - assert (negated != best); + CADICAL_assert (negated != best); eliminator.enqueue (d); } lrat_chain.clear (); @@ -214,7 +218,7 @@ void Internal::elim_backward_clause (Eliminator &eliminator, Clause *c) { void Internal::elim_backward_clauses (Eliminator &eliminator) { if (!opts.elimbackward) { - assert (eliminator.backward.empty ()); + CADICAL_assert (eliminator.backward.empty ()); return; } START (backward); @@ -229,3 +233,5 @@ void Internal::elim_backward_clauses (Eliminator &eliminator) { /*------------------------------------------------------------------------*/ } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/bins.cpp b/src/sat/cadical/cadical_bins.cpp similarity index 75% rename from src/sat/cadical/bins.cpp rename to src/sat/cadical/cadical_bins.cpp index 406074eff..fba96f216 100644 --- a/src/sat/cadical/bins.cpp +++ b/src/sat/cadical/cadical_bins.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -7,16 +11,18 @@ namespace CaDiCaL { // Binary implication graph lists. void Internal::init_bins () { - assert (big.empty ()); + CADICAL_assert (big.empty ()); if (big.size () < 2 * vsize) big.resize (2 * vsize, Bins ()); LOG ("initialized binary implication graph"); } void Internal::reset_bins () { - assert (!big.empty ()); + CADICAL_assert (!big.empty ()); erase_vector (big); LOG ("reset binary implication graph"); } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/block.cpp b/src/sat/cadical/cadical_block.cpp similarity index 87% rename from src/sat/cadical/block.cpp rename to src/sat/cadical/cadical_block.cpp index 11721d8bd..12f852f08 100644 --- a/src/sat/cadical/block.cpp +++ b/src/sat/cadical/cadical_block.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -44,13 +48,13 @@ bool Internal::is_blocked_clause (Clause *c, int lit) { LOG (c, "trying to block on %d", lit); - assert (c->size >= opts.blockminclslim); - assert (c->size <= opts.blockmaxclslim); - assert (active (lit)); - assert (!val (lit)); - assert (!c->garbage); - assert (!c->redundant); - assert (!level); + CADICAL_assert (c->size >= opts.blockminclslim); + CADICAL_assert (c->size <= opts.blockmaxclslim); + CADICAL_assert (active (lit)); + CADICAL_assert (!val (lit)); + CADICAL_assert (!c->garbage); + CADICAL_assert (!c->redundant); + CADICAL_assert (!level); mark (c); // First mark all literals in 'c'. @@ -72,9 +76,9 @@ bool Internal::is_blocked_clause (Clause *c, int lit) { // Clause *d = *i; - assert (!d->garbage); - assert (!d->redundant); - assert (d->size <= opts.blockmaxclslim); + CADICAL_assert (!d->garbage); + CADICAL_assert (!d->redundant); + CADICAL_assert (d->size <= opts.blockmaxclslim); *i = prev_d; // Move previous non-tautological clause prev_d = d; // backwards but remember clause at this position. @@ -99,9 +103,9 @@ bool Internal::is_blocked_clause (Clause *c, int lit) { prev_other = other; if (other == -lit) continue; - assert (other != lit); - assert (active (other)); - assert (!val (other)); + CADICAL_assert (other != lit); + CADICAL_assert (active (other)); + CADICAL_assert (!val (other)); if (marked (other) < 0) { LOG ("found tautological literal %d", other); d->literals[0] = other; // Move to front of 'd'. @@ -132,7 +136,7 @@ bool Internal::is_blocked_clause (Clause *c, int lit) { // restore the old order of clauses in the occurrence list of '-lit'. // if (res) { - assert (i == end_of_os); + CADICAL_assert (i == end_of_os); const auto boc = os.begin (); while (i != boc) { Clause *d = *--i; @@ -172,8 +176,8 @@ void Internal::block_schedule (Blocker &blocker) { continue; for (const auto &lit : *c) { - assert (active (lit)); - assert (!val (lit)); + CADICAL_assert (active (lit)); + CADICAL_assert (!val (lit)); occs (lit).push_back (c); } } @@ -187,7 +191,7 @@ void Internal::block_schedule (Blocker &blocker) { for (auto lit : lits) { if (!active (lit)) continue; - assert (!val (lit)); + CADICAL_assert (!val (lit)); Occs &os = occs (lit); noccs (lit) = os.size (); } @@ -199,7 +203,7 @@ void Internal::block_schedule (Blocker &blocker) { // schedule can not be fused with the previous loop (easily) since we // first have to initialize 'noccs' for both 'lit' and '-lit'. -#ifndef QUIET +#ifndef CADICAL_QUIET int skipped = 0; #endif @@ -207,16 +211,16 @@ void Internal::block_schedule (Blocker &blocker) { if (!active (idx)) continue; if (frozen (idx)) { -#ifndef QUIET +#ifndef CADICAL_QUIET skipped += 2; #endif continue; } - assert (!val (idx)); + CADICAL_assert (!val (idx)); for (int sign = -1; sign <= 1; sign += 2) { const int lit = sign * idx; if (marked_skip (lit)) { -#ifndef QUIET +#ifndef CADICAL_QUIET skipped++; #endif continue; @@ -249,15 +253,15 @@ void Internal::block_schedule (Blocker &blocker) { void Internal::block_pure_literal (Blocker &blocker, int lit) { if (frozen (lit)) return; - assert (active (lit)); + CADICAL_assert (active (lit)); Occs &pos = occs (lit); Occs &nos = occs (-lit); - assert (!noccs (-lit)); -#ifndef NDEBUG + CADICAL_assert (!noccs (-lit)); +#ifndef CADICAL_NDEBUG for (const auto &c : nos) - assert (c->garbage); + CADICAL_assert (c->garbage); #endif stats.blockpurelits++; LOG ("found pure literal %d", lit); @@ -267,7 +271,7 @@ void Internal::block_pure_literal (Blocker &blocker, int lit) { for (const auto &c : pos) { if (c->garbage) continue; - assert (!c->redundant); + CADICAL_assert (!c->redundant); LOG (c, "pure literal %d in", lit); blocker.reschedule.push_back (c); if (proof) { @@ -297,25 +301,25 @@ void Internal::block_pure_literal (Blocker &blocker, int lit) { void Internal::block_literal_with_one_negative_occ (Blocker &blocker, int lit) { - assert (active (lit)); - assert (!frozen (lit)); - assert (noccs (lit) > 0); - assert (noccs (-lit) == 1); + CADICAL_assert (active (lit)); + CADICAL_assert (!frozen (lit)); + CADICAL_assert (noccs (lit) > 0); + CADICAL_assert (noccs (-lit) == 1); Occs &nos = occs (-lit); - assert (nos.size () >= 1); + CADICAL_assert (nos.size () >= 1); Clause *d = 0; for (const auto &c : nos) { if (c->garbage) continue; - assert (!d); + CADICAL_assert (!d); d = c; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG break; #endif } - assert (d); + CADICAL_assert (d); nos.resize (1); nos[0] = d; @@ -324,9 +328,9 @@ void Internal::block_literal_with_one_negative_occ (Blocker &blocker, return; } - assert (!d->garbage); - assert (!d->redundant); - assert (d->size <= opts.blockmaxclslim); + CADICAL_assert (!d->garbage); + CADICAL_assert (!d->redundant); + CADICAL_assert (d->size <= opts.blockmaxclslim); LOG (d, "common %d antecedent", lit); mark (d); @@ -379,9 +383,9 @@ void Internal::block_literal_with_one_negative_occ (Blocker &blocker, prev_other = other; if (other == lit) continue; - assert (other != -lit); - assert (active (other)); - assert (!val (other)); + CADICAL_assert (other != -lit); + CADICAL_assert (active (other)); + CADICAL_assert (!val (other)); if (marked (other) < 0) { LOG ("found tautological literal %d", other); c->literals[0] = other; // Move to front of 'c'. @@ -434,13 +438,13 @@ void Internal::block_literal_with_one_negative_occ (Blocker &blocker, size_t Internal::block_candidates (Blocker &blocker, int lit) { - assert (blocker.candidates.empty ()); + CADICAL_assert (blocker.candidates.empty ()); Occs &pos = occs (lit); // Positive occurrences of 'lit'. Occs &nos = occs (-lit); - assert ((size_t) noccs (lit) <= pos.size ()); - assert ((size_t) noccs (-lit) == nos.size ()); // Already flushed. + CADICAL_assert ((size_t) noccs (lit) <= pos.size ()); + CADICAL_assert ((size_t) noccs (-lit) == nos.size ()); // Already flushed. // Mark all literals in clauses with '-lit'. Note that 'mark2' uses // separate bits for 'lit' and '-lit'. @@ -457,7 +461,7 @@ size_t Internal::block_candidates (Blocker &blocker, int lit) { j--; continue; } - assert (!c->redundant); + CADICAL_assert (!c->redundant); if (c->size > opts.blockmaxclslim) continue; if (c->size < opts.blockminclslim) @@ -468,9 +472,9 @@ size_t Internal::block_candidates (Blocker &blocker, int lit) { const int other = *l; if (other == lit) continue; - assert (other != -lit); - assert (active (other)); - assert (!val (other)); + CADICAL_assert (other != -lit); + CADICAL_assert (active (other)); + CADICAL_assert (!val (other)); if (marked2 (-other)) break; } @@ -482,7 +486,7 @@ size_t Internal::block_candidates (Blocker &blocker, int lit) { else pos.resize (j - pos.begin ()); - assert (pos.size () == (size_t) noccs (lit)); // Now also flushed. + CADICAL_assert (pos.size () == (size_t) noccs (lit)); // Now also flushed. for (const auto &c : nos) unmark (c); @@ -498,8 +502,8 @@ size_t Internal::block_candidates (Blocker &blocker, int lit) { // resolvent with that clause. Clause *Internal::block_impossible (Blocker &blocker, int lit) { - assert (noccs (-lit) > 1); - assert (blocker.candidates.size () > 1); + CADICAL_assert (noccs (-lit) > 1); + CADICAL_assert (blocker.candidates.size () > 1); for (const auto &c : blocker.candidates) mark2 (c); @@ -508,18 +512,18 @@ Clause *Internal::block_impossible (Blocker &blocker, int lit) { Clause *res = 0; for (const auto &c : nos) { - assert (!c->garbage); - assert (!c->redundant); - assert (c->size <= opts.blockmaxclslim); + CADICAL_assert (!c->garbage); + CADICAL_assert (!c->redundant); + CADICAL_assert (c->size <= opts.blockmaxclslim); const const_literal_iterator eoc = c->end (); const_literal_iterator l; for (l = c->begin (); l != eoc; l++) { const int other = *l; if (other == -lit) continue; - assert (other != lit); - assert (active (other)); - assert (!val (other)); + CADICAL_assert (other != lit); + CADICAL_assert (active (other)); + CADICAL_assert (!val (other)); if (marked2 (-other)) break; } @@ -544,13 +548,13 @@ Clause *Internal::block_impossible (Blocker &blocker, int lit) { void Internal::block_literal_with_at_least_two_negative_occs ( Blocker &blocker, int lit) { - assert (active (lit)); - assert (!frozen (lit)); - assert (noccs (lit) > 0); - assert (noccs (-lit) > 1); + CADICAL_assert (active (lit)); + CADICAL_assert (!frozen (lit)); + CADICAL_assert (noccs (lit) > 0); + CADICAL_assert (noccs (-lit) > 1); Occs &nos = occs (-lit); - assert ((size_t) noccs (-lit) <= nos.size ()); + CADICAL_assert ((size_t) noccs (-lit) <= nos.size ()); int max_size = 0; @@ -571,8 +575,8 @@ void Internal::block_literal_with_at_least_two_negative_occs ( else nos.resize (j - nos.begin ()); - assert (nos.size () == (size_t) noccs (-lit)); - assert (nos.size () > 1); + CADICAL_assert (nos.size () == (size_t) noccs (-lit)); + CADICAL_assert (nos.size () > 1); // If the maximum size of a negative clause (with '-lit') exceeds the // maximum clause size limit ignore this candidate literal. @@ -604,7 +608,7 @@ void Internal::block_literal_with_at_least_two_negative_occs ( // if (candidates > 1 && block_impossible (blocker, lit)) { LOG ("impossible to block any candidate clause on %d", lit); - assert (blocker.candidates.empty ()); + CADICAL_assert (blocker.candidates.empty ()); return; } @@ -616,8 +620,8 @@ void Internal::block_literal_with_at_least_two_negative_occs ( // Go over all remaining candidates and try to block them on 'lit'. // for (const auto &c : blocker.candidates) { - assert (!c->garbage); - assert (!c->redundant); + CADICAL_assert (!c->garbage); + CADICAL_assert (!c->redundant); if (!is_blocked_clause (c, lit)) continue; blocked++; @@ -646,15 +650,15 @@ void Internal::block_literal_with_at_least_two_negative_occs ( void Internal::block_reschedule_clause (Blocker &blocker, int lit, Clause *c) { -#ifdef NDEBUG +#ifdef CADICAL_NDEBUG (void) lit; #endif - assert (c->garbage); + CADICAL_assert (c->garbage); for (const auto &other : *c) { int64_t &n = noccs (other); - assert (n > 0); + CADICAL_assert (n > 0); n--; LOG ("updating %d with %" PRId64 " positive and %" PRId64 @@ -669,7 +673,7 @@ void Internal::block_reschedule_clause (Blocker &blocker, int lit, } if (blocker.schedule.contains (vlit (other))) { - assert (other != lit); + CADICAL_assert (other != lit); blocker.schedule.update (vlit (other)); } } @@ -688,14 +692,14 @@ void Internal::block_reschedule (Blocker &blocker, int lit) { /*------------------------------------------------------------------------*/ void Internal::block_literal (Blocker &blocker, int lit) { - assert (!marked_skip (lit)); + CADICAL_assert (!marked_skip (lit)); if (!active (lit)) return; // Pure literal '-lit'. if (frozen (lit)) return; - assert (!val (lit)); + CADICAL_assert (!val (lit)); // If the maximum number of a negative clauses (with '-lit') exceeds the // occurrence limit ignore this candidate literal. @@ -709,8 +713,8 @@ void Internal::block_literal (Blocker &blocker, int lit) { stats.blockcands++; - assert (blocker.reschedule.empty ()); - assert (blocker.candidates.empty ()); + CADICAL_assert (blocker.reschedule.empty ()); + CADICAL_assert (blocker.candidates.empty ()); if (!noccs (-lit)) block_pure_literal (blocker, lit); @@ -728,7 +732,7 @@ void Internal::block_literal (Blocker &blocker, int lit) { // have to try blocked clause elimination on it again until irredundant // clauses with its negation are removed. // - assert (!frozen (lit)); // just to be sure ... + CADICAL_assert (!frozen (lit)); // just to be sure ... unmark_block (lit); } @@ -752,7 +756,7 @@ bool Internal::block () { if (!propagate ()) { LOG ("propagating units results in empty clause"); learn_empty_clause (); - assert (unsat); + CADICAL_assert (unsat); } clear_watches (); reset_watches (); @@ -766,9 +770,9 @@ bool Internal::block () { LOG ("block-%" PRId64 "", stats.blockings); - assert (!level); - assert (!watching ()); - assert (!occurring ()); + CADICAL_assert (!level); + CADICAL_assert (!watching ()); + CADICAL_assert (!occurring ()); mark_satisfied_clauses_as_garbage (); @@ -822,3 +826,5 @@ bool Internal::block () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/ccadical.cpp b/src/sat/cadical/cadical_ccadical.cpp similarity index 93% rename from src/sat/cadical/ccadical.cpp rename to src/sat/cadical/cadical_ccadical.cpp index 91438932b..ae5c1093f 100644 --- a/src/sat/cadical/ccadical.cpp +++ b/src/sat/cadical/cadical_ccadical.cpp @@ -1,8 +1,12 @@ +#include "global.h" + #include "cadical.hpp" #include <cstdlib> #include <cstring> +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { struct Wrapper : Learner, Terminator { @@ -63,12 +67,14 @@ struct Wrapper : Learner, Terminator { } // namespace CaDiCaL -using namespace CaDiCaL; - -extern "C" { +ABC_NAMESPACE_IMPL_END #include "ccadical.h" +ABC_NAMESPACE_IMPL_START + +using namespace CaDiCaL; + const char *ccadical_signature (void) { return Solver::signature (); } CCaDiCaL *ccadical_init (void) { return (CCaDiCaL *) new Wrapper (); } @@ -193,4 +199,13 @@ int ccadical_vars (CCaDiCaL *ptr) { int ccadical_reserve_difference (CCaDiCaL *ptr, int number_of_vars) { return ((Wrapper *) ptr)->solver->reserve_difference (number_of_vars); } + +void ccadical_reserve(CCaDiCaL *ptr, int min_max_var) { + ((Wrapper *) ptr)->solver->reserve(min_max_var); } + +int ccadical_is_inconsistent(CCaDiCaL *ptr) { + return ((Wrapper *) ptr)->solver->inconsistent (); +} + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/checker.cpp b/src/sat/cadical/cadical_checker.cpp similarity index 91% rename from src/sat/cadical/checker.cpp rename to src/sat/cadical/cadical_checker.cpp index cfb60f0f7..63a5f2b10 100644 --- a/src/sat/cadical/checker.cpp +++ b/src/sat/cadical/cadical_checker.cpp @@ -1,12 +1,16 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ inline unsigned Checker::l2u (int lit) { - assert (lit); - assert (lit != INT_MIN); + CADICAL_assert (lit); + CADICAL_assert (lit != INT_MIN); unsigned res = 2 * (abs (lit) - 1); if (lit < 0) res++; @@ -14,22 +18,22 @@ inline unsigned Checker::l2u (int lit) { } inline signed char Checker::val (int lit) { - assert (lit); - assert (lit != INT_MIN); - assert (abs (lit) < size_vars); - assert (vals[lit] == -vals[-lit]); + CADICAL_assert (lit); + CADICAL_assert (lit != INT_MIN); + CADICAL_assert (abs (lit) < size_vars); + CADICAL_assert (vals[lit] == -vals[-lit]); return vals[lit]; } signed char &Checker::mark (int lit) { const unsigned u = l2u (lit); - assert (u < marks.size ()); + CADICAL_assert (u < marks.size ()); return marks[u]; } inline CheckerWatcher &Checker::watcher (int lit) { const unsigned u = l2u (lit); - assert (u < watchers.size ()); + CADICAL_assert (u < watchers.size ()); return watchers[u]; } @@ -37,7 +41,7 @@ inline CheckerWatcher &Checker::watcher (int lit) { CheckerClause *Checker::new_clause () { const size_t size = simplified.size (); - assert (size > 1), assert (size <= UINT_MAX); + CADICAL_assert (size > 1), CADICAL_assert (size <= UINT_MAX); const size_t bytes = sizeof (CheckerClause) + (size - 2) * sizeof (int); CheckerClause *res = (CheckerClause *) new char[bytes]; DeferDeleteArray<char> delete_res ((char *) res); @@ -63,8 +67,8 @@ CheckerClause *Checker::new_clause () { break; } } - assert (!val (literals[0])); - assert (!val (literals[1])); + CADICAL_assert (!val (literals[0])); + CADICAL_assert (!val (literals[1])); watcher (literals[0]).push_back (CheckerWatch (literals[1], res)); watcher (literals[1]).push_back (CheckerWatch (literals[0], res)); @@ -74,18 +78,18 @@ CheckerClause *Checker::new_clause () { void Checker::delete_clause (CheckerClause *c) { if (c->size) { - assert (c->size > 1); - assert (num_clauses); + CADICAL_assert (c->size > 1); + CADICAL_assert (num_clauses); num_clauses--; } else { - assert (num_garbage); + CADICAL_assert (num_garbage); num_garbage--; } delete[] (char *) c; } void Checker::enlarge_clauses () { - assert (num_clauses == size_clauses); + CADICAL_assert (num_clauses == size_clauses); const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1; LOG ("CHECKER enlarging clauses of checker from %" PRIu64 " to %" PRIu64, (uint64_t) size_clauses, (uint64_t) new_size_clauses); @@ -131,7 +135,7 @@ void Checker::collect_garbage_clauses () { c->next = garbage; garbage = c; num_garbage++; - assert (num_clauses); + CADICAL_assert (num_clauses); num_clauses--; } else p = &c->next; @@ -163,7 +167,7 @@ void Checker::collect_garbage_clauses () { for (CheckerClause *c = garbage, *next; c; c = next) next = c->next, delete_clause (c); - assert (!num_garbage); + CADICAL_assert (!num_garbage); garbage = 0; } @@ -181,7 +185,7 @@ Checker::Checker (Internal *i) uint64_t nonce = random.next (); if (!(nonce & 1)) nonce++; - assert (nonce), assert (nonce & 1); + CADICAL_assert (nonce), CADICAL_assert (nonce & 1); nonces[n] = nonce; } @@ -213,7 +217,7 @@ Checker::~Checker () { void Checker::enlarge_vars (int64_t idx) { - assert (0 < idx), assert (idx <= INT_MAX); + CADICAL_assert (0 < idx), CADICAL_assert (idx <= INT_MAX); int64_t new_size_vars = size_vars ? 2 * size_vars : 2; while (idx >= new_size_vars) @@ -237,12 +241,12 @@ void Checker::enlarge_vars (int64_t idx) { watchers.resize (2 * new_size_vars); marks.resize (2 * new_size_vars); - assert (idx < new_size_vars); + CADICAL_assert (idx < new_size_vars); } inline void Checker::import_literal (int lit) { - assert (lit); - assert (lit != INT_MIN); + CADICAL_assert (lit); + CADICAL_assert (lit != INT_MIN); int idx = abs (lit); if (idx >= size_vars) enlarge_vars (idx); @@ -289,7 +293,7 @@ bool Checker::tautological () { /*------------------------------------------------------------------------*/ uint64_t Checker::reduce_hash (uint64_t hash, uint64_t size) { - assert (size > 0); + CADICAL_assert (size > 0); unsigned shift = 32; uint64_t res = hash; while ((((uint64_t) 1) << shift) > size) { @@ -297,7 +301,7 @@ uint64_t Checker::reduce_hash (uint64_t hash, uint64_t size) { shift >>= 1; } res &= size - 1; - assert (res < size); + CADICAL_assert (res < size); return res; } @@ -344,7 +348,7 @@ void Checker::insert () { /*------------------------------------------------------------------------*/ inline void Checker::assign (int lit) { - assert (!val (lit)); + CADICAL_assert (!val (lit)); vals[lit] = 1; vals[-lit] = -1; trail.push_back (lit); @@ -354,26 +358,26 @@ inline void Checker::assume (int lit) { signed char tmp = val (lit); if (tmp > 0) return; - assert (!tmp); + CADICAL_assert (!tmp); stats.assumptions++; assign (lit); } void Checker::backtrack (unsigned previously_propagated) { - assert (previously_propagated <= trail.size ()); + CADICAL_assert (previously_propagated <= trail.size ()); while (trail.size () > previously_propagated) { int lit = trail.back (); - assert (val (lit) > 0); - assert (val (-lit) < 0); + CADICAL_assert (val (lit) > 0); + CADICAL_assert (val (-lit) < 0); vals[lit] = vals[-lit] = 0; trail.pop_back (); } trail.resize (previously_propagated); next_to_propagate = previously_propagated; - assert (trail.size () == next_to_propagate); + CADICAL_assert (trail.size () == next_to_propagate); } /*------------------------------------------------------------------------*/ @@ -386,15 +390,15 @@ bool Checker::propagate () { while (res && next_to_propagate < trail.size ()) { int lit = trail[next_to_propagate++]; stats.propagations++; - assert (val (lit) > 0); - assert (abs (lit) < size_vars); + CADICAL_assert (val (lit) > 0); + CADICAL_assert (abs (lit) < size_vars); CheckerWatcher &ws = watcher (-lit); const auto end = ws.end (); auto j = ws.begin (), i = j; for (; res && i != end; i++) { CheckerWatch &w = *j++ = *i; const int blit = w.blit; - assert (blit != -lit); + CADICAL_assert (blit != -lit); const signed char blit_val = val (blit); if (blit_val > 0) continue; @@ -405,16 +409,16 @@ bool Checker::propagate () { else assign (w.blit); // but still sound } else { - assert (size > 2); + CADICAL_assert (size > 2); CheckerClause *c = w.clause; if (!c->size) { j--; continue; } // skip garbage clauses - assert (size == c->size); + CADICAL_assert (size == c->size); int *lits = c->literals; int other = lits[0] ^ lits[1] ^ (-lit); - assert (other != -lit); + CADICAL_assert (other != -lit); signed char other_val = val (other); if (other_val > 0) { j[-1].blit = other; @@ -511,7 +515,7 @@ void Checker::add_clause (const char *type) { const signed char tmp = val (lit); if (tmp < 0) continue; - assert (!tmp); + CADICAL_assert (!tmp); if (unit) { unit = INT_MIN; break; @@ -598,10 +602,10 @@ void Checker::delete_clause (int64_t id, bool, const vector<int> &c) { if (!tautological ()) { CheckerClause **p = find (), *d = *p; if (d) { - assert (d->size > 1); + CADICAL_assert (d->size > 1); // Remove from hash table, mark as garbage, connect to garbage list. num_garbage++; - assert (num_clauses); + CADICAL_assert (num_clauses); num_clauses--; *p = d->next; d->next = garbage; @@ -646,3 +650,5 @@ void Checker::dump () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/clause.cpp b/src/sat/cadical/cadical_clause.cpp similarity index 88% rename from src/sat/cadical/clause.cpp rename to src/sat/cadical/cadical_clause.cpp index aa011bedd..1c84994a6 100644 --- a/src/sat/cadical/clause.cpp +++ b/src/sat/cadical/cadical_clause.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -41,7 +45,7 @@ void Internal::unmark_clause () { void Internal::mark_removed (Clause *c, int except) { LOG (c, "marking removed"); - assert (!c->redundant); + CADICAL_assert (!c->redundant); for (const auto &lit : *c) if (lit != except) mark_removed (lit); @@ -68,7 +72,7 @@ inline void Internal::mark_added (int lit, int size, bool redundant) { void Internal::mark_added (Clause *c) { LOG (c, "marking added"); - assert (likely_to_be_kept_clause (c)); + CADICAL_assert (likely_to_be_kept_clause (c)); for (const auto &lit : *c) mark_added (lit, c->size, c->redundant); } @@ -77,9 +81,9 @@ void Internal::mark_added (Clause *c) { Clause *Internal::new_clause (bool red, int glue) { - assert (clause.size () <= (size_t) INT_MAX); + CADICAL_assert (clause.size () <= (size_t) INT_MAX); const int size = (int) clause.size (); - assert (size >= 2); + CADICAL_assert (size >= 2); if (glue > size) glue = size; @@ -119,7 +123,7 @@ Clause *Internal::new_clause (bool red, int glue) { // Just checking that we did not mess up our sophisticated memory layout. // This might be compiler dependent though. Crucial for correctness. // - assert (c->bytes () == bytes); + CADICAL_assert (c->bytes () == bytes); stats.current.total++; stats.added.total++; @@ -146,7 +150,7 @@ Clause *Internal::new_clause (bool red, int glue) { /*------------------------------------------------------------------------*/ void Internal::promote_clause (Clause *c, int new_glue) { - assert (c->redundant); + CADICAL_assert (c->redundant); const int tier1limit = tier1[false]; const int tier2limit = max (tier1limit, tier2[false]); if (!c->redundant) @@ -173,7 +177,7 @@ void Internal::promote_clause (Clause *c, int new_glue) { /*------------------------------------------------------------------------*/ void Internal::promote_clause_glue_only (Clause *c, int new_glue) { - assert (c->redundant); + CADICAL_assert (c->redundant); if (c->hyper) return; int old_glue = c->glue; @@ -209,10 +213,10 @@ void Internal::promote_clause_glue_only (Clause *c, int new_glue) { size_t Internal::shrink_clause (Clause *c, int new_size) { if (opts.check && is_external_forgettable (c->id)) mark_garbage_external_forgettable (c->id); - assert (new_size >= 2); + CADICAL_assert (new_size >= 2); int old_size = c->size; - assert (new_size < old_size); -#ifndef NDEBUG + CADICAL_assert (new_size < old_size); +#ifndef CADICAL_NDEBUG for (int i = c->size; i < new_size; i++) c->literals[i] = 0; #endif @@ -229,7 +233,7 @@ size_t Internal::shrink_clause (Clause *c, int new_size) { promote_clause_glue_only (c, min (c->size - 1, c->glue)); else { int delta_size = old_size - new_size; - assert (stats.irrlits >= delta_size); + CADICAL_assert (stats.irrlits >= delta_size); stats.irrlits -= delta_size; } @@ -256,11 +260,11 @@ void Internal::delete_clause (Clause *c) { size_t bytes = c->bytes (); stats.collected += bytes; if (c->garbage) { - assert (stats.garbage.bytes >= (int64_t) bytes); + CADICAL_assert (stats.garbage.bytes >= (int64_t) bytes); stats.garbage.bytes -= bytes; - assert (stats.garbage.clauses > 0); + CADICAL_assert (stats.garbage.clauses > 0); stats.garbage.clauses--; - assert (stats.garbage.literals >= c->size); + CADICAL_assert (stats.garbage.literals >= c->size); stats.garbage.literals -= c->size; // See the discussion in 'propagate' on avoiding to eagerly trace binary @@ -296,7 +300,7 @@ void Internal::delete_clause (Clause *c) { // void Internal::mark_garbage (Clause *c) { - assert (!c->garbage); + CADICAL_assert (!c->garbage); // Delay tracing deletion of binary clauses. See the discussion above in // 'delete_clause' and also in 'propagate'. @@ -312,17 +316,17 @@ void Internal::mark_garbage (Clause *c) { if (opts.check && is_external_forgettable (c->id)) mark_garbage_external_forgettable (c->id); - assert (stats.current.total > 0); + CADICAL_assert (stats.current.total > 0); stats.current.total--; size_t bytes = c->bytes (); if (c->redundant) { - assert (stats.current.redundant > 0); + CADICAL_assert (stats.current.redundant > 0); stats.current.redundant--; } else { - assert (stats.current.irredundant > 0); + CADICAL_assert (stats.current.irredundant > 0); stats.current.irredundant--; - assert (stats.irrlits >= c->size); + CADICAL_assert (stats.irrlits >= c->size); stats.irrlits -= c->size; mark_removed (c); } @@ -341,11 +345,11 @@ void Internal::mark_garbage (Clause *c) { // to learn a new unit clause (which was confusing in log files). void Internal::assign_original_unit (int64_t id, int lit) { - assert (!level || opts.chrono); - assert (!unsat); + CADICAL_assert (!level || opts.chrono); + CADICAL_assert (!unsat); const int idx = vidx (lit); - assert (!vals[idx]); - assert (!flags (idx).eliminated ()); + CADICAL_assert (!vals[idx]); + CADICAL_assert (!flags (idx).eliminated ()); Var &v = var (idx); v.level = 0; v.trail = (int) trail.size (); @@ -358,13 +362,13 @@ void Internal::assign_original_unit (int64_t id, int lit) { if (lrat || frat) unit_clauses (uidx) = id; LOG ("original unit assign %d", lit); - assert (num_assigned == trail.size () || level); + CADICAL_assert (num_assigned == trail.size () || level); mark_fixed (lit); if (level) return; if (propagate ()) return; - assert (conflict); + CADICAL_assert (conflict); LOG ("propagation of original unit results in conflict"); learn_empty_clause (); } @@ -383,14 +387,14 @@ void Internal::add_new_original_clause (int64_t id) { if (!from_propagator && level && !opts.ilb) { backtrack (); } else if (tainted_literal) { - assert (val (tainted_literal)); + CADICAL_assert (val (tainted_literal)); int new_level = var (tainted_literal).level - 1; - assert (new_level >= 0); + CADICAL_assert (new_level >= 0); backtrack (new_level); } - assert (!tainted_literal); + CADICAL_assert (!tainted_literal); LOG (original, "original clause"); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); bool skip = false; unordered_set<int> learned_levels; size_t unassigned = 0; @@ -399,7 +403,7 @@ void Internal::add_new_original_clause (int64_t id) { LOG ("skipping clause since formula is already inconsistent"); skip = true; } else { - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); for (const auto &lit : original) { int tmp = marked (lit); if (tmp > 0) { @@ -425,7 +429,7 @@ void Internal::add_new_original_clause (int64_t id) { skip = true; } else { clause.push_back (lit); - assert (flags (lit).status != Flags::UNUSED); + CADICAL_assert (flags (lit).status != Flags::UNUSED); tmp = val (lit); if (tmp) learned_levels.insert (var (lit).level); @@ -477,7 +481,7 @@ void Internal::add_new_original_clause (int64_t id) { if (!size) { if (from_propagator) stats.ext_prop.elearn_conf++; - assert (!unsat); + CADICAL_assert (!unsat); if (!original.size ()) VERBOSE (1, "found empty original clause"); else @@ -488,12 +492,12 @@ void Internal::add_new_original_clause (int64_t id) { conclusion.push_back (new_id); } else if (size == 1) { if (force_no_backtrack) { - assert (level); + CADICAL_assert (level); const int idx = vidx (clause[0]); - assert (val (clause[0]) >= 0); - assert (!flags (idx).eliminated ()); + CADICAL_assert (val (clause[0]) >= 0); + CADICAL_assert (!flags (idx).eliminated ()); Var &v = var (idx); - assert (val (clause[0])); + CADICAL_assert (val (clause[0])); v.level = 0; v.reason = 0; const unsigned uidx = vlit (clause[0]); @@ -502,20 +506,20 @@ void Internal::add_new_original_clause (int64_t id) { mark_fixed (clause[0]); } else { const int lit = clause[0]; - assert (!val (lit) || var (lit).level); + CADICAL_assert (!val (lit) || var (lit).level); if (val (lit) < 0) backtrack (var (lit).level - 1); - assert (val (lit) >= 0); + CADICAL_assert (val (lit) >= 0); handle_external_clause (0); assign_original_unit (new_id, lit); } } else { move_literals_to_watch (); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG check_watched_literal_invariants (); #endif int glue = (int) (learned_levels.size () + unassigned); - assert (glue <= (int) clause.size ()); + CADICAL_assert (glue <= (int) clause.size ()); bool clause_redundancy = from_propagator && ext_clause_forgettable; Clause *c = new_clause (clause_redundancy, glue); c->id = new_id; @@ -536,18 +540,18 @@ void Internal::add_new_original_clause (int64_t id) { // are assigned at the highest decision level. // Clause *Internal::new_learned_redundant_clause (int glue) { - assert (clause.size () > 1); -#ifndef NDEBUG + CADICAL_assert (clause.size () > 1); +#ifndef CADICAL_NDEBUG for (size_t i = 2; i < clause.size (); i++) - assert (var (clause[0]).level >= var (clause[i]).level), - assert (var (clause[1]).level >= var (clause[i]).level); + CADICAL_assert (var (clause[0]).level >= var (clause[i]).level), + CADICAL_assert (var (clause[1]).level >= var (clause[i]).level); #endif external->check_learned_clause (); Clause *res = new_clause (true, glue); if (proof) { proof->add_derived_clause (res, lrat_chain); } - assert (watching ()); + CADICAL_assert (watching ()); watch_clause (res); return res; } @@ -560,7 +564,7 @@ Clause *Internal::new_hyper_binary_resolved_clause (bool red, int glue) { if (proof) { proof->add_derived_clause (res, lrat_chain); } - assert (watching ()); + CADICAL_assert (watching ()); watch_clause (res); return res; } @@ -574,7 +578,7 @@ Clause *Internal::new_hyper_ternary_resolved_clause (bool red) { if (proof) { proof->add_derived_clause (res, lrat_chain); } - assert (!watching ()); + CADICAL_assert (!watching ()); return res; } @@ -586,8 +590,8 @@ Clause *Internal::new_factor_clause () { if (proof) { proof->add_derived_clause (res, lrat_chain); } - assert (!watching ()); - assert (occurring ()); + CADICAL_assert (!watching ()); + CADICAL_assert (occurring ()); for (const auto &lit : *res) { occs (lit).push_back (res); } @@ -606,7 +610,7 @@ Internal::new_hyper_ternary_resolved_clause_and_watch (bool red, proof->add_derived_clause (res, lrat_chain); } if (full_watching) { - assert (watching ()); + CADICAL_assert (watching ()); watch_clause (res); } return res; @@ -622,7 +626,7 @@ Clause *Internal::new_clause_as (const Clause *orig) { if (proof) { proof->add_derived_clause (res, lrat_chain); } - assert (watching ()); + CADICAL_assert (watching ()); watch_clause (res); return res; } @@ -636,8 +640,10 @@ Clause *Internal::new_resolved_irredundant_clause () { proof->add_derived_clause (clause_id + 1, false, clause, lrat_chain); } Clause *res = new_clause (false); - assert (!watching ()); + CADICAL_assert (!watching ()); return res; } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/collect.cpp b/src/sat/cadical/cadical_collect.cpp similarity index 93% rename from src/sat/cadical/collect.cpp rename to src/sat/cadical/cadical_collect.cpp index 2e583c9c2..c36e063b2 100644 --- a/src/sat/cadical/collect.cpp +++ b/src/sat/cadical/cadical_collect.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -56,7 +60,7 @@ void Internal::remove_falsified_literals (Clause *c) { literal_iterator j = c->begin (); for (i = j; i != end; i++) { const int lit = *j++ = *i, tmp = fixed (lit); - assert (tmp <= 0); + CADICAL_assert (tmp <= 0); if (tmp >= 0) continue; LOG ("flushing %d", lit); @@ -102,23 +106,23 @@ void Internal::mark_satisfied_clauses_as_garbage () { void Internal::protect_reasons () { LOG ("protecting reason clauses of all assigned variables on trail"); - assert (!protected_reasons); + CADICAL_assert (!protected_reasons); #ifdef LOGGING size_t count = 0; #endif for (const auto &lit : trail) { if (!active (lit)) continue; - assert (val (lit)); + CADICAL_assert (val (lit)); Var &v = var (lit); - assert (v.level > 0); + CADICAL_assert (v.level > 0); Clause *reason = v.reason; if (!reason) continue; if (reason == external_reason) continue; LOG (reason, "protecting assigned %d reason %p", lit, (void *) reason); - assert (!reason->reason); + CADICAL_assert (!reason->reason); reason->reason = true; #ifdef LOGGING count++; @@ -135,16 +139,16 @@ void Internal::protect_reasons () { void Internal::unprotect_reasons () { LOG ("unprotecting reasons clauses of all assigned variables on trail"); - assert (protected_reasons); + CADICAL_assert (protected_reasons); #ifdef LOGGING size_t count = 0; #endif for (const auto &lit : trail) { if (!active (lit)) continue; - assert (val (lit)); + CADICAL_assert (val (lit)); Var &v = var (lit); - assert (v.level > 0); + CADICAL_assert (v.level > 0); Clause *reason = v.reason; if (!reason) continue; @@ -152,7 +156,7 @@ void Internal::unprotect_reasons () { continue; LOG (reason, "unprotecting assigned %d reason %p", lit, (void *) reason); - assert (reason->reason); + CADICAL_assert (reason->reason); reason->reason = false; #ifdef LOGGING count++; @@ -181,7 +185,7 @@ size_t Internal::flush_occs (int lit) { if (c->collect ()) continue; *j++ = c->moved ? c->copy : c; - // assert (!c->redundant); // -> not true in sweeping + // CADICAL_assert (!c->redundant); // -> not true in sweeping res++; } os.resize (j - os.begin ()); @@ -196,7 +200,7 @@ size_t Internal::flush_occs (int lit) { // preprocessing is actually redundant. inline void Internal::flush_watches (int lit, Watches &saved) { - assert (saved.empty ()); + CADICAL_assert (saved.empty ()); Watches &ws = watches (lit); const const_watch_iterator end = ws.end (); watch_iterator j = ws.begin (); @@ -211,7 +215,7 @@ inline void Internal::flush_watches (int lit, Watches &saved) { w.size = c->size; const int new_blit_pos = (c->literals[0] == lit); LOG (c, "clause in flush_watch starting from %d", lit); - assert (c->literals[!new_blit_pos] == lit); /*FW1*/ + CADICAL_assert (c->literals[!new_blit_pos] == lit); /*FW1*/ w.blit = c->literals[new_blit_pos]; if (w.binary ()) *j++ = w; @@ -254,8 +258,8 @@ void Internal::update_reason_references () { if (c == external_reason) continue; LOG (c, "updating assigned %d reason", lit); - assert (c->reason); - assert (c->moved); + CADICAL_assert (c->reason); + CADICAL_assert (c->moved); Clause *d = c->copy; v.reason = d; #ifdef LOGGING @@ -277,7 +281,7 @@ void Internal::delete_garbage_clauses () { flush_all_occs_and_watches (); LOG ("deleting garbage clauses"); -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t collected_bytes = 0, collected_clauses = 0; #endif const auto end = clauses.end (); @@ -286,7 +290,7 @@ void Internal::delete_garbage_clauses () { Clause *c = *j++ = *i++; if (!c->collect ()) continue; -#ifndef QUIET +#ifndef CADICAL_QUIET collected_bytes += c->bytes (); collected_clauses++; #endif @@ -310,7 +314,7 @@ void Internal::delete_garbage_clauses () { // void Internal::copy_clause (Clause *c) { LOG (c, "moving"); - assert (!c->moved); + CADICAL_assert (!c->moved); char *p = (char *) c; char *q = arena.copy (p, c->bytes ()); c->copy = (Clause *) q; @@ -390,7 +394,7 @@ void Internal::copy_non_garbage_clauses () { // the decision queue and also uses saved phases. It seems faster than // the MiniSAT version and thus we keep 'opts.arenatype == 3'. - assert (opts.arenatype == 3); + CADICAL_assert (opts.arenatype == 3); for (int sign = -1; sign <= 1; sign += 2) for (int idx = queue.last; idx; idx = link (idx).prev) @@ -418,7 +422,7 @@ void Internal::copy_non_garbage_clauses () { if (c->collect ()) delete_clause (c); else - assert (c->moved), *j++ = c->copy, deallocate_clause (c); + CADICAL_assert (c->moved), *j++ = c->copy, deallocate_clause (c); } clauses.resize (j - clauses.begin ()); if (clauses.size () < clauses.capacity () / 2) @@ -446,7 +450,7 @@ void Internal::copy_non_garbage_clauses () { // statistics are updated correctly. void Internal::check_clause_stats () { -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG int64_t irredundant = 0, redundant = 0, total = 0, irrlits = 0; for (const auto &c : clauses) { if (c->garbage) @@ -459,10 +463,10 @@ void Internal::check_clause_stats () { irrlits += c->size; total++; } - assert (stats.current.irredundant == irredundant); - assert (stats.current.redundant == redundant); - assert (stats.current.total == total); - assert (stats.irrlits == irrlits); + CADICAL_assert (stats.current.irredundant == irredundant); + CADICAL_assert (stats.current.redundant == redundant); + CADICAL_assert (stats.current.total == total); + CADICAL_assert (stats.irrlits == irrlits); #endif } @@ -481,7 +485,7 @@ void Internal::remove_garbage_binaries () { Watches saved; for (auto v : vars) { for (auto lit : {-v, v}) { - assert (saved.empty ()); + CADICAL_assert (saved.empty ()); Watches &ws = watches (lit); const const_watch_iterator end = ws.end (); watch_iterator j = ws.begin (); @@ -495,7 +499,7 @@ void Internal::remove_garbage_binaries () { continue; if (c->reason && c->garbage) { COVER (true); - assert (c->size == 2); + CADICAL_assert (c->size == 2); backtrack_level = min (backtrack_level, var (c->literals[0]).level); LOG ("need to backtrack to before level %d", backtrack_level); @@ -543,3 +547,5 @@ void Internal::garbage_collection () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/compact.cpp b/src/sat/cadical/cadical_compact.cpp similarity index 87% rename from src/sat/cadical/compact.cpp rename to src/sat/cadical/cadical_compact.cpp index 1bf423993..6747218cb 100644 --- a/src/sat/cadical/compact.cpp +++ b/src/sat/cadical/cadical_compact.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -18,7 +22,7 @@ bool Internal::compacting () { if (stats.conflicts < lim.compact) return false; int inactive = max_var - active (); - assert (inactive >= 0); + CADICAL_assert (inactive >= 0); if (!inactive) return false; if (inactive < opts.compactmin) @@ -52,7 +56,7 @@ struct Mapper { table = new int[internal->max_var + 1u]; clear_n (table, internal->max_var + 1u); - assert (!internal->level); + CADICAL_assert (!internal->level); for (auto src : internal->vars) { const Flags &f = internal->flags (src); @@ -72,10 +76,10 @@ struct Mapper { // Map old variable indices. A result of zero means not mapped. // int map_idx (int src) { - assert (0 < src); - assert (src <= internal->max_var); + CADICAL_assert (0 < src); + CADICAL_assert (src <= internal->max_var); const int res = table[src]; - assert (res <= new_max_var); + CADICAL_assert (res <= new_max_var); return res; } @@ -89,14 +93,14 @@ struct Mapper { if (!res) { const signed char tmp = internal->val (src); if (tmp) { - assert (first_fixed); + CADICAL_assert (first_fixed); res = map_first_fixed; if (tmp != first_fixed_val) res = -res; } } else if ((src) < 0) res = -res; - assert (abs (res) <= new_max_var); + CADICAL_assert (abs (res) <= new_max_var); return res; } @@ -108,8 +112,8 @@ struct Mapper { const int dst = map_idx (src); if (!dst) continue; - assert (0 < dst); - assert (dst <= src); + CADICAL_assert (0 < dst); + CADICAL_assert (dst <= src); v[dst] = v[src]; } v.resize (new_vsize); @@ -124,8 +128,8 @@ struct Mapper { const int dst = map_idx (src); if (!dst) continue; - assert (0 < dst); - assert (dst <= src); + CADICAL_assert (0 < dst); + CADICAL_assert (dst <= src); v[2 * dst] = v[2 * src]; v[2 * dst + 1] = v[2 * src + 1]; } @@ -143,7 +147,7 @@ struct Mapper { for (; i != end; i++) { const int src = *i; int dst = map_idx (abs (src)); - assert (abs (dst) <= abs (src)); + CADICAL_assert (abs (dst) <= abs (src)); if (!dst) continue; if (src < 0) @@ -163,19 +167,19 @@ void Internal::compact () { START (compact); - assert (active () < max_var); + CADICAL_assert (active () < max_var); stats.compacts++; - assert (!level); - assert (!unsat); - assert (!conflict); - assert (clause.empty ()); - assert (levels.empty ()); - assert (analyzed.empty ()); - assert (minimized.empty ()); - assert (control.size () == 1); - assert (propagated == trail.size ()); + CADICAL_assert (!level); + CADICAL_assert (!unsat); + CADICAL_assert (!conflict); + CADICAL_assert (clause.empty ()); + CADICAL_assert (levels.empty ()); + CADICAL_assert (analyzed.empty ()); + CADICAL_assert (minimized.empty ()); + CADICAL_assert (control.size () == 1); + CADICAL_assert (propagated == trail.size ()); garbage_collection (); @@ -188,14 +192,14 @@ void Internal::compact () { LOG ("no variable fixed"); if (!assumptions.empty ()) { - assert (!external->assumptions.empty ()); + CADICAL_assert (!external->assumptions.empty ()); LOG ("temporarily reset internal assumptions"); reset_assumptions (); } const bool is_constraint = !constraint.empty (); if (is_constraint) { - assert (!external->constraint.empty ()); + CADICAL_assert (!external->constraint.empty ()); LOG ("temporarily reset internal constraint"); reset_constraint (); } @@ -213,11 +217,11 @@ void Internal::compact () { continue; } if (lrat || frat) { - assert (eidx > 0); - assert (external->ext_units.size () >= (size_t) 2 * eidx + 1); + CADICAL_assert (eidx > 0); + CADICAL_assert (external->ext_units.size () >= (size_t) 2 * eidx + 1); int64_t id1 = external->ext_units[2 * eidx]; int64_t id2 = external->ext_units[2 * eidx + 1]; - assert (!id1 || !id2); + CADICAL_assert (!id1 || !id2); if (!id1 && !id2) { int64_t new_id1 = unit_clauses (2 * src); int64_t new_id2 = unit_clauses (2 * src + 1); @@ -237,7 +241,7 @@ void Internal::compact () { if (lrat || frat) { for (auto src : internal->vars) { const int dst = mapper.map_idx (src); - assert (dst <= src); + CADICAL_assert (dst <= src); const signed char tmp = internal->val (src); if (!dst && !tmp) { unit_clauses (2 * src) = 0; @@ -245,10 +249,10 @@ void Internal::compact () { continue; } if (!tmp || src == mapper.first_fixed) { - assert (0 < dst); + CADICAL_assert (0 < dst); if (dst == src) continue; - assert (!unit_clauses (2 * dst) && !unit_clauses (2 * dst + 1)); + CADICAL_assert (!unit_clauses (2 * dst) && !unit_clauses (2 * dst + 1)); unit_clauses (2 * dst) = unit_clauses (2 * src); unit_clauses (2 * dst + 1) = unit_clauses (2 * src + 1); unit_clauses (2 * src) = 0; @@ -263,7 +267,7 @@ void Internal::compact () { } unit_clauses (2 * src) = 0; unit_clauses (2 * src + 1) = 0; - assert (id); + CADICAL_assert (id); } unit_clauses_idx.resize (2 * mapper.new_vsize); shrink_vector (unit_clauses_idx); @@ -271,12 +275,12 @@ void Internal::compact () { // Map the literals in all clauses. // for (const auto &c : clauses) { - assert (!c->garbage); + CADICAL_assert (!c->garbage); for (auto &src : *c) { - assert (!val (src)); + CADICAL_assert (!val (src)); int dst; dst = mapper.map_lit (src); - assert (dst || c->garbage); + CADICAL_assert (dst || c->garbage); src = dst; } } @@ -299,7 +303,7 @@ void Internal::compact () { const int dst = mapper.map_idx (idx); if (!dst) continue; - assert (active (idx)); + CADICAL_assert (active (idx)); if (prev) links[prev].next = dst; else @@ -319,15 +323,15 @@ void Internal::compact () { // In the second part we map, flush and shrink arrays. /*======================================================================*/ - assert (trail.size () == num_assigned); + CADICAL_assert (trail.size () == num_assigned); mapper.map_flush_and_shrink_lits (trail); propagated = trail.size (); num_assigned = trail.size (); if (mapper.first_fixed) { - assert (trail.size () == 1); + CADICAL_assert (trail.size () == 1); var (mapper.first_fixed).trail = 0; // before mapping 'vtab' } else - assert (trail.empty ()); + CADICAL_assert (trail.empty ()); if (!probes.empty ()) mapper.map_flush_and_shrink_lits (probes); @@ -360,7 +364,7 @@ void Internal::compact () { continue; if (src == dst) continue; - assert (dst < src); + CADICAL_assert (dst < src); if ((size_t) src >= frozentab.size ()) break; if ((size_t) dst >= frozentab.size ()) @@ -380,7 +384,7 @@ void Internal::compact () { continue; if (src == dst) continue; - assert (dst < src); + CADICAL_assert (dst < src); relevanttab[dst] += relevanttab[src]; relevanttab[src] = 0; @@ -394,12 +398,12 @@ void Internal::compact () { if (!external->assumptions.empty ()) { for (const auto &elit : external->assumptions) { - assert (elit); - assert (elit != INT_MIN); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); int eidx = abs (elit); - assert (eidx <= external->max_var); + CADICAL_assert (eidx <= external->max_var); int ilit = external->e2i[eidx]; - assert (ilit); // Because we froze all!!! + CADICAL_assert (ilit); // Because we froze all!!! if (elit < 0) ilit = -ilit; assume (ilit); @@ -428,14 +432,14 @@ void Internal::compact () { // 'constrain' uses 'val', so this code has to be after remapping that if (is_constraint) { - assert (!level); - assert (!external->constraint.back ()); + CADICAL_assert (!level); + CADICAL_assert (!external->constraint.back ()); for (auto elit : external->constraint) { - assert (elit != INT_MIN); + CADICAL_assert (elit != INT_MIN); int eidx = abs (elit); - assert (eidx <= external->max_var); + CADICAL_assert (eidx <= external->max_var); int ilit = external->e2i[eidx]; - assert (!ilit == !elit); + CADICAL_assert (!ilit == !elit); if (elit < 0) ilit = -ilit; LOG ("re adding lit external %d internal %d to constraint", elit, @@ -475,7 +479,7 @@ void Internal::compact () { // elements with 'zero' destination should be flushed. vector<int> saved; - assert (saved.empty ()); + CADICAL_assert (saved.empty ()); if (!scores.empty ()) { while (!scores.empty ()) { const int src = scores.front (); @@ -549,3 +553,5 @@ void Internal::compact () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/condition.cpp b/src/sat/cadical/cadical_condition.cpp similarity index 91% rename from src/sat/cadical/condition.cpp rename to src/sat/cadical/cadical_condition.cpp index 7fd70a465..8c2f72cf7 100644 --- a/src/sat/cadical/condition.cpp +++ b/src/sat/cadical/cadical_condition.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -39,7 +43,7 @@ bool Internal::conditioning () { if (!preprocessing && !opts.inprocessing) return false; if (preprocessing) - assert (lim.preprocessing); + CADICAL_assert (lim.preprocessing); // Triggered in regular 'opts.conditionint' conflict intervals. // @@ -79,13 +83,13 @@ bool Internal::conditioning () { void Internal::condition_unassign (int lit) { LOG ("condition unassign %d", lit); - assert (val (lit) > 0); + CADICAL_assert (val (lit) > 0); set_val (lit, 0); } void Internal::condition_assign (int lit) { LOG ("condition assign %d", lit); - assert (!val (lit)); + CADICAL_assert (!val (lit)); set_val (lit, 1); } @@ -104,15 +108,15 @@ inline bool Internal::is_autarky_literal (int lit) const { inline void Internal::mark_as_conditional_literal (int lit) { LOG ("marking %d as conditional literal", lit); - assert (val (lit) > 0); + CADICAL_assert (val (lit) > 0); setbit (lit, 0); - assert (is_conditional_literal (lit)); - assert (!is_autarky_literal (lit)); + CADICAL_assert (is_conditional_literal (lit)); + CADICAL_assert (!is_autarky_literal (lit)); } inline void Internal::unmark_as_conditional_literal (int lit) { LOG ("unmarking %d as conditional literal", lit); - assert (is_conditional_literal (lit)); + CADICAL_assert (is_conditional_literal (lit)); unsetbit (lit, 0); } @@ -130,13 +134,13 @@ inline bool Internal::is_in_candidate_clause (int lit) const { inline void Internal::mark_in_candidate_clause (int lit) { LOG ("marking %d as literal of the candidate clause", lit); mark67 (lit); - assert (is_in_candidate_clause (lit)); - assert (!is_in_candidate_clause (-lit)); + CADICAL_assert (is_in_candidate_clause (lit)); + CADICAL_assert (!is_in_candidate_clause (-lit)); } inline void Internal::unmark_in_candidate_clause (int lit) { LOG ("unmarking %d as literal of the candidate clause", lit); - assert (is_in_candidate_clause (lit)); + CADICAL_assert (is_in_candidate_clause (lit)); unmark67 (lit); } @@ -155,7 +159,7 @@ struct less_conditioned { long Internal::condition_round (long delta) { long limit; -#ifndef QUIET +#ifndef CADICAL_QUIET long props = 0; #endif if (LONG_MAX - delta < stats.condprops) @@ -170,7 +174,7 @@ long Internal::condition_round (long delta) { protect_reasons (); -#if defined(LOGGING) || !defined(NDEBUG) +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) int additionally_assigned = 0; #endif @@ -202,7 +206,7 @@ long Internal::condition_round (long delta) { condition_assign (lit); v.level = level; trail.push_back (lit); -#if defined(LOGGING) || !defined(NDEBUG) +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) additionally_assigned++; #endif } @@ -260,7 +264,7 @@ long Internal::condition_round (long delta) { vector<int> conditional; vector<Clause *> candidates; // Gather candidate clauses. -#ifndef QUIET +#ifndef CADICAL_QUIET size_t watched = 0; // Number of watched clauses. #endif @@ -350,12 +354,12 @@ long Internal::condition_round (long delta) { // if (negative > 0 && positive > 0) { LOG (c, "found %d negative literals in candidate", negative); - assert (watch); - assert (val (watch) > 0); + CADICAL_assert (watch); + CADICAL_assert (val (watch) > 0); Occs &os = occs (watch); - assert (os.size () == minsize); + CADICAL_assert (os.size () == minsize); os.push_back (c); -#ifndef QUIET +#ifndef CADICAL_QUIET watched++; #endif LOG (c, "watching %d with %zd occurrences in", watch, minsize); @@ -375,7 +379,7 @@ long Internal::condition_round (long delta) { signed char tmp = val (lit); if (!tmp) continue; - assert (tmp < 0); + CADICAL_assert (tmp < 0); if (!var (lit).level) continue; // Not unassigned yet! if (is_conditional_literal (-lit)) @@ -389,7 +393,7 @@ long Internal::condition_round (long delta) { new_conditionals); initial.conditional += new_conditionals; - assert (initial.autarky >= new_conditionals); + CADICAL_assert (initial.autarky >= new_conditionals); initial.autarky -= new_conditionals; } @@ -408,14 +412,14 @@ long Internal::condition_round (long delta) { #ifdef LOGGING for (size_t i = 0; i < conditional.size (); i++) { LOG ("initial conditional %d", conditional[i]); - assert (is_conditional_literal (conditional[i])); + CADICAL_assert (is_conditional_literal (conditional[i])); } for (size_t i = 0; i < trail.size (); i++) if (is_autarky_literal (trail[i])) LOG ("initial autarky %d", trail[i]); #endif - assert (initial.conditional == conditional.size ()); - assert (initial.assigned == initial.conditional + initial.autarky); + CADICAL_assert (initial.conditional == conditional.size ()); + CADICAL_assert (initial.assigned == initial.conditional + initial.autarky); stats.condassinit += initial.assigned; stats.condcondinit += initial.conditional; @@ -448,7 +452,7 @@ long Internal::condition_round (long delta) { // // TODO consider computing conditioned and unconditioned over all clauses. // - assert (conditioned + unconditioned == candidates.size ()); + CADICAL_assert (conditioned + unconditioned == candidates.size ()); if (conditioned && unconditioned) { stable_sort (candidates.begin (), candidates.end (), less_conditioned ()); @@ -457,13 +461,13 @@ long Internal::condition_round (long delta) { unconditioned, percent (unconditioned, candidates.size ())); } else if (conditioned && !unconditioned) { for (auto const &c : candidates) { - assert (c->conditioned); + CADICAL_assert (c->conditioned); c->conditioned = false; // Reset 'conditioned' bit. } PHASE ("condition", stats.conditionings, "all %zd candidates tried before", conditioned); } else { - assert (!conditioned); + CADICAL_assert (!conditioned); PHASE ("condition", stats.conditionings, "all %zd candidates are fresh", unconditioned); } @@ -478,7 +482,7 @@ long Internal::condition_round (long delta) { // long blocked = 0; // Number of Successfully blocked clauses. // -#ifndef QUIET +#ifndef CADICAL_QUIET size_t untried = candidates.size (); #endif for (const auto &c : candidates) { @@ -503,11 +507,11 @@ long Internal::condition_round (long delta) { untried, percent (untried, candidates.size ()), props); break; } -#ifndef QUIET +#ifndef CADICAL_QUIET untried--; #endif - assert (!c->garbage); - assert (!c->redundant); + CADICAL_assert (!c->garbage); + CADICAL_assert (!c->redundant); LOG (c, "candidate"); c->conditioned = 1; // Next time later. @@ -559,17 +563,17 @@ long Internal::condition_round (long delta) { size_t conditional, unassigned; } next = {0, 0}; - assert (unassigned.empty ()); - assert (conditional.size () == initial.conditional); + CADICAL_assert (unassigned.empty ()); + CADICAL_assert (conditional.size () == initial.conditional); while (watched_autarky_literal && stats.condprops < limit && next.conditional < conditional.size ()) { - assert (next.unassigned == unassigned.size ()); + CADICAL_assert (next.unassigned == unassigned.size ()); const int conditional_lit = conditional[next.conditional++]; LOG ("processing next conditional %d", conditional_lit); - assert (is_conditional_literal (conditional_lit)); + CADICAL_assert (is_conditional_literal (conditional_lit)); if (is_in_candidate_clause (-conditional_lit)) { LOG ("conditional %d negated in candidate clause", conditional_lit); @@ -580,11 +584,11 @@ long Internal::condition_round (long delta) { conditional_lit); condition_unassign (conditional_lit); - assert (!is_conditional_literal (conditional_lit)); + CADICAL_assert (!is_conditional_literal (conditional_lit)); unassigned.push_back (conditional_lit); - assert (remain.assigned > 0); - assert (remain.conditional > 0); + CADICAL_assert (remain.assigned > 0); + CADICAL_assert (remain.conditional > 0); remain.conditional--; remain.assigned--; @@ -592,8 +596,8 @@ long Internal::condition_round (long delta) { next.unassigned < unassigned.size ()) { const int unassigned_lit = unassigned[next.unassigned++]; LOG ("processing next unassigned %d", unassigned_lit); - assert (!val (unassigned_lit)); -#ifndef QUIET + CADICAL_assert (!val (unassigned_lit)); +#ifndef CADICAL_QUIET props++; #endif stats.condprops++; @@ -635,7 +639,7 @@ long Internal::condition_round (long delta) { i--; // Drop watch! LOG (d, "watching %d in", replacement); - assert (replacement != unassigned_lit); + CADICAL_assert (replacement != unassigned_lit); occs (replacement).push_back (d); continue; // ... with next watched clause 'd'. @@ -661,7 +665,7 @@ long Internal::condition_round (long delta) { conditional.push_back (-lit); remain.conditional++; - assert (remain.autarky > 0); + CADICAL_assert (remain.autarky > 0); remain.autarky--; if (-lit != watched_autarky_literal) @@ -713,9 +717,9 @@ long Internal::condition_round (long delta) { LOG ("remaining conditional part of size %zd", remain.conditional); LOG ("remaining autarky part of size %zd", remain.autarky); // - assert (remain.assigned - remain.conditional == remain.autarky); + CADICAL_assert (remain.assigned - remain.conditional == remain.autarky); // -#if defined(LOGGING) || !defined(NDEBUG) +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) // // This is a sanity check, that the size of our implicit representation // of the autarky part matches our 'remain' counts. We need the same @@ -732,21 +736,21 @@ long Internal::condition_round (long delta) { check.assigned++; if (is_conditional_literal (lit)) { LOG ("remaining conditional %d", lit); - assert (!is_autarky_literal (lit)); + CADICAL_assert (!is_autarky_literal (lit)); check.conditional++; } else { - assert (is_autarky_literal (lit)); + CADICAL_assert (is_autarky_literal (lit)); LOG ("remaining autarky %d", lit); check.autarky++; } } else { - assert (!is_autarky_literal (lit)); - assert (!is_conditional_literal (lit)); + CADICAL_assert (!is_autarky_literal (lit)); + CADICAL_assert (!is_conditional_literal (lit)); } } - assert (remain.assigned == check.assigned); - assert (remain.conditional == check.conditional); - assert (remain.autarky == check.autarky); + CADICAL_assert (remain.assigned == check.assigned); + CADICAL_assert (remain.conditional == check.conditional); + CADICAL_assert (remain.autarky == check.autarky); #endif // Success if an autarky literal is left in the clause and @@ -754,8 +758,8 @@ long Internal::condition_round (long delta) { // limit was hit. // if (watched_autarky_literal && stats.condprops < limit) { - assert (is_autarky_literal (watched_autarky_literal)); - assert (is_in_candidate_clause (watched_autarky_literal)); + CADICAL_assert (is_autarky_literal (watched_autarky_literal)); + CADICAL_assert (is_in_candidate_clause (watched_autarky_literal)); blocked++; stats.conditioned++; @@ -848,20 +852,20 @@ long Internal::condition_round (long delta) { // Unassign additionally assigned literals. // -#if defined(LOGGING) || !defined(NDEBUG) +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) int additionally_unassigned = 0; #endif while (trail.size () > initial_trail_level) { int lit = trail.back (); trail.pop_back (); condition_unassign (lit); -#if defined(LOGGING) || !defined(NDEBUG) +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) additionally_unassigned++; #endif } LOG ("unassigned %d additionally assigned literals", additionally_unassigned); - assert (additionally_unassigned == additionally_assigned); + CADICAL_assert (additionally_unassigned == additionally_assigned); if (level > initial_level) { LOG ("reset condition decision level"); @@ -877,14 +881,14 @@ long Internal::condition_round (long delta) { for (size_t i = 0; i < initial_trail_level; i++) { const int lit = trail[i]; const signed char tmp = val (lit); - assert (tmp >= 0); + CADICAL_assert (tmp >= 0); if (!tmp) condition_assign (lit); } -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG for (const auto &lit : trail) - assert (!marked (lit)); + CADICAL_assert (!marked (lit)); #endif unprotect_reasons (); @@ -913,7 +917,7 @@ void Internal::condition (bool update_limits) { limit = opts.conditionmineff; if (limit > opts.conditionmaxeff) limit = opts.conditionmaxeff; - assert (stats.current.irredundant); + CADICAL_assert (stats.current.irredundant); limit *= 2.0 * active () / (double) stats.current.irredundant; limit = max (limit, 2l * active ()); @@ -938,3 +942,5 @@ void Internal::condition (bool update_limits) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/config.cpp b/src/sat/cadical/cadical_config.cpp similarity index 95% rename from src/sat/cadical/config.cpp rename to src/sat/cadical/cadical_config.cpp index f63d1e4b7..d4268790c 100644 --- a/src/sat/cadical/config.cpp +++ b/src/sat/cadical/cadical_config.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -75,7 +79,7 @@ bool Config::set (Options &opts, const char *name) { const NameVal *BEGIN = N##_config; \ const NameVal *END = BEGIN + sizeof N##_config / sizeof (NameVal); \ for (const NameVal *P = BEGIN; P != END; P++) { \ - assert (Options::has (P->name)); \ + CADICAL_assert (Options::has (P->name)); \ opts.set (P->name, P->val); \ } \ return true; \ @@ -99,3 +103,5 @@ const char **Config::begin () { return configs; } const char **Config::end () { return &configs[num_configs]; } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/congruence.cpp b/src/sat/cadical/cadical_congruence.cpp similarity index 86% rename from src/sat/cadical/congruence.cpp rename to src/sat/cadical/cadical_congruence.cpp index 4c8de9d52..8fbceee24 100644 --- a/src/sat/cadical/congruence.cpp +++ b/src/sat/cadical/cadical_congruence.cpp @@ -1,3 +1,5 @@ +#include "global.h" + #include "congruence.hpp" #include "internal.hpp" #include <algorithm> @@ -6,6 +8,8 @@ #include <utility> #include <vector> +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { Closure::Closure (Internal *i) @@ -45,52 +49,52 @@ void update_ite_flags (Gate *g) { f |= Special_ITE_GATE::NO_NEG_ELSE; } g->degenerated_ite = f; - assert (lhs != -then_lit); - assert (lhs != -else_lit); - assert (cond != then_lit); - assert (cond != else_lit); - assert (cond != -then_lit); - assert (cond != -else_lit); + CADICAL_assert (lhs != -then_lit); + CADICAL_assert (lhs != -else_lit); + CADICAL_assert (cond != then_lit); + CADICAL_assert (cond != else_lit); + CADICAL_assert (cond != -then_lit); + CADICAL_assert (cond != -else_lit); } void check_correct_ite_flags (const Gate *const g) { -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG const int8_t f = g->degenerated_ite; const int lhs = g->lhs; const int cond = g->rhs [0]; const int then_lit = g->rhs[1]; const int else_lit = g->rhs[2]; - assert (g->pos_lhs_ids.size () == 4); + CADICAL_assert (g->pos_lhs_ids.size () == 4); if (g->pos_lhs_ids[0].clause == nullptr) - assert ((f & Special_ITE_GATE::NO_PLUS_THEN)); + CADICAL_assert ((f & Special_ITE_GATE::NO_PLUS_THEN)); if (g->pos_lhs_ids[1].clause == nullptr) - assert (f & Special_ITE_GATE::NO_NEG_THEN); + CADICAL_assert (f & Special_ITE_GATE::NO_NEG_THEN); if (g->pos_lhs_ids[2].clause == nullptr) - assert (f & Special_ITE_GATE::NO_PLUS_ELSE); + CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_ELSE); if (g->pos_lhs_ids[3].clause == nullptr) - assert (f & Special_ITE_GATE::NO_NEG_ELSE); + CADICAL_assert (f & Special_ITE_GATE::NO_NEG_ELSE); if (lhs == cond) { - assert (f & Special_ITE_GATE::NO_NEG_THEN); - assert (f & Special_ITE_GATE::NO_PLUS_ELSE); + CADICAL_assert (f & Special_ITE_GATE::NO_NEG_THEN); + CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_ELSE); } if (lhs == -cond) { - assert (f & Special_ITE_GATE::NO_PLUS_THEN); - assert (f & Special_ITE_GATE::NO_NEG_ELSE); + CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_THEN); + CADICAL_assert (f & Special_ITE_GATE::NO_NEG_ELSE); } if (lhs == then_lit) { - assert (f & Special_ITE_GATE::NO_PLUS_THEN); - assert (f & Special_ITE_GATE::NO_NEG_THEN); + CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_THEN); + CADICAL_assert (f & Special_ITE_GATE::NO_NEG_THEN); } if (lhs == else_lit) { - assert (f & Special_ITE_GATE::NO_PLUS_ELSE); - assert (f & Special_ITE_GATE::NO_NEG_ELSE); + CADICAL_assert (f & Special_ITE_GATE::NO_PLUS_ELSE); + CADICAL_assert (f & Special_ITE_GATE::NO_NEG_ELSE); } - assert (lhs != -then_lit); - assert (lhs != -else_lit); - assert (cond != then_lit); - assert (cond != else_lit); - assert (cond != -then_lit); - assert (cond != -else_lit); + CADICAL_assert (lhs != -then_lit); + CADICAL_assert (lhs != -else_lit); + CADICAL_assert (cond != then_lit); + CADICAL_assert (cond != else_lit); + CADICAL_assert (cond != -then_lit); + CADICAL_assert (cond != -else_lit); #else (void)g; #endif @@ -116,7 +120,7 @@ static size_t hash_lits (std::array<int, 16> &nonces, } size_t Hash::operator() (const Gate *const g) const { - assert (hash_lits (nonces, g->rhs) == g->hash); + CADICAL_assert (hash_lits (nonces, g->rhs) == g->hash); return g->hash; } @@ -144,7 +148,7 @@ bool Closure::find_binary (int lit, int other) const { const auto size = offsize.second; const auto begin = std::begin (binaries) + offset; const auto end = std::begin (binaries) + size; - assert (end <= std::end (binaries)); + CADICAL_assert (end <= std::end (binaries)); const CompactBinary target = CompactBinary (nullptr, 0, lit, other); auto it = std::lower_bound (begin, end, target, compact_binary_order ()); // search_binary only returns a bool @@ -172,7 +176,7 @@ void Closure::extract_binaries () { continue; if (c->size > 2) continue; - assert (c->size == 2); + CADICAL_assert (c->size == 2); const int lit = c->literals[0]; const int other = c->literals[1]; const bool already_sorted = @@ -195,8 +199,8 @@ void Closure::extract_binaries () { while (j < size && binaries[j].lit1 == lit) { ++j; } - assert (j >= i); - assert (j <= size); + CADICAL_assert (j >= i); + CADICAL_assert (j <= size); offsetsize[internal->vlit (lit)] = make_pair (i, j); i = j; } @@ -257,7 +261,7 @@ void Closure::extract_binaries () { { size_t i = 0; for (size_t j = 1; j < new_size; ++j) { - assert (i < j); + CADICAL_assert (i < j); if (binaries[i].lit1 == binaries[j].lit1 && binaries[i].lit2 == binaries[j].lit2) { // subsuming later clause @@ -268,7 +272,7 @@ void Closure::extract_binaries () { binaries[++i] = binaries[j]; } } - assert (i <= new_size); + CADICAL_assert (i <= new_size); binaries.resize (i); } binaries.clear (); @@ -281,26 +285,26 @@ void Closure::extract_binaries () { /*------------------------------------------------------------------------*/ // marking structure for congruence closure, by reference signed char &Closure::marked (int lit) { - assert (internal->vlit (lit) < marks.size ()); + CADICAL_assert (internal->vlit (lit) < marks.size ()); return marks[internal->vlit (lit)]; } void Closure::unmark_all () { for (auto lit : internal->analyzed) { - assert (marked (lit)); + CADICAL_assert (marked (lit)); marked (lit) = 0; } internal->analyzed.clear (); } void Closure::set_mu1_reason (int lit, Clause *c) { - assert (marked (lit) & 1); + CADICAL_assert (marked (lit) & 1); LOG (c, "mu1 %d -> %zd", lit, c->id); mu1_ids[internal->vlit (lit)] = LitClausePair (lit, c); } void Closure::set_mu2_reason (int lit, Clause *c) { - assert (marked (lit) & 2); + CADICAL_assert (marked (lit) & 2); if (!internal->lrat) return; LOG (c, "mu2 %d -> %zd", lit, c->id); @@ -308,7 +312,7 @@ void Closure::set_mu2_reason (int lit, Clause *c) { } void Closure::set_mu4_reason (int lit, Clause *c) { - assert (marked (lit) & 4); + CADICAL_assert (marked (lit) & 4); if (!internal->lrat) return; LOG (c, "mu4 %d -> %zd", lit, c->id); @@ -401,21 +405,21 @@ void Closure::sort_literals_by_var (vector<int> &rhs) { } /*------------------------------------------------------------------------*/ int &Closure::representative (int lit) { - assert (internal->vlit (lit) < representant.size ()); + CADICAL_assert (internal->vlit (lit) < representant.size ()); return representant[internal->vlit (lit)]; } int Closure::representative (int lit) const { - assert (internal->vlit (lit) < representant.size ()); + CADICAL_assert (internal->vlit (lit) < representant.size ()); return representant[internal->vlit (lit)]; } int &Closure::eager_representative (int lit) { - assert (internal->vlit (lit) < eager_representant.size ()); + CADICAL_assert (internal->vlit (lit) < eager_representant.size ()); return eager_representant[internal->vlit (lit)]; } int Closure::eager_representative (int lit) const { - assert (internal->vlit (lit) < eager_representant.size ()); + CADICAL_assert (internal->vlit (lit) < eager_representant.size ()); return eager_representant[internal->vlit (lit)]; } @@ -479,7 +483,7 @@ int Closure::find_representative_and_compress (int lit, bool update_eager) { eager_representative (lit) = res; if (internal->lrat) eager_representative_id (lit) = representative_id (lit); - assert (!internal->lrat || eager_representative_id (lit)); + CADICAL_assert (!internal->lrat || eager_representative_id (lit)); } } @@ -493,7 +497,7 @@ int Closure::find_representative_and_compress (int lit, bool update_eager) { void Closure::push_lrat_unit (int lit) { if (!internal->lrat) return; - assert (internal->val (lit) > 0); + CADICAL_assert (internal->val (lit) > 0); LRAT_ID id = internal->unit_id (lit); lrat_chain.push_back (id); } @@ -521,7 +525,7 @@ int Closure::find_eager_representative_and_compress (int lit) { ++path_length; } while (nxt != res); - // assert (res == find_representative (lit)); + // CADICAL_assert (res == find_representative (lit)); // we have to do path compression to support LRAT proofs if (path_length > 2) { LOG ("learning new rewriting from %d to %d (current path length: %d)", @@ -545,10 +549,10 @@ int Closure::find_eager_representative_and_compress (int lit) { } else if (path_length == 2) { LOG ("duplicated information %d -> %d to eager with clause %" PRIu64, lit, res, eager_representative_id (lit)); - assert (eager_representative (lit) == res); - assert (!internal->lrat || eager_representative_id (lit)); + CADICAL_assert (eager_representative (lit) == res); + CADICAL_assert (!internal->lrat || eager_representative_id (lit)); } - assert (internal->clause.empty ()); + CADICAL_assert (internal->clause.empty ()); return res; } @@ -566,14 +570,14 @@ void Closure::import_lazy_and_find_eager_representative_and_compress_both ( } void Closure::produce_representative_lrat (int lit) { - assert (internal->lrat); + CADICAL_assert (internal->lrat); LOG ("production of LRAT chain for %d with representative %" PRIu64, lit, representative_id (lit)); - assert (internal->lrat); - assert (lrat_chain.empty ()); + CADICAL_assert (internal->lrat); + CADICAL_assert (lrat_chain.empty ()); int res = lit; int nxt = lit; - assert (nxt != representative (nxt)); + CADICAL_assert (nxt != representative (nxt)); do { res = nxt; nxt = representative (nxt); @@ -585,14 +589,14 @@ void Closure::produce_representative_lrat (int lit) { } void Closure::produce_eager_representative_lrat (int lit) { - assert (internal->lrat); + CADICAL_assert (internal->lrat); LOG ("production of LRAT chain for %d with representative %" PRIu64, lit, eager_representative_id (lit)); - assert (internal->lrat); - assert (lrat_chain.empty ()); + CADICAL_assert (internal->lrat); + CADICAL_assert (lrat_chain.empty ()); int res = lit; int nxt = lit; - assert (nxt != eager_representative (nxt)); + CADICAL_assert (nxt != eager_representative (nxt)); do { res = nxt; nxt = eager_representative (nxt); @@ -607,13 +611,13 @@ LRAT_ID Closure::find_representative_lrat (int lit) { if (!internal->lrat) return 0; int res = lit; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG int nxt = representative (res); - assert (nxt == representative (res)); + CADICAL_assert (nxt == representative (res)); #endif LOG ("checking for existing LRAT chain for %d with clause %" PRIu64, lit, eager_representative_id (res)); - assert (representative_id (res)); + CADICAL_assert (representative_id (res)); return representative_id (res); } @@ -621,44 +625,44 @@ LRAT_ID Closure::find_eager_representative_lrat (int lit) { if (!internal->lrat) return 0; int res = lit; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG int nxt = eager_representative (res); - assert (nxt == eager_representative (res)); + CADICAL_assert (nxt == eager_representative (res)); #endif LOG ("checking for existing LRAT chain for %d with clause %" PRIu64, lit, eager_representative_id (res)); - assert (eager_representative_id (res)); + CADICAL_assert (eager_representative_id (res)); return eager_representative_id (res); } LRAT_ID &Closure::eager_representative_id (int lit) { - assert (internal->vlit (lit) < eager_representant_id.size ()); + CADICAL_assert (internal->vlit (lit) < eager_representant_id.size ()); return eager_representant_id[internal->vlit (lit)]; } LRAT_ID Closure::eager_representative_id (int lit) const { - assert (internal->vlit (lit) < eager_representant_id.size ()); + CADICAL_assert (internal->vlit (lit) < eager_representant_id.size ()); return eager_representant_id[internal->vlit (lit)]; } LRAT_ID &Closure::representative_id (int lit) { - assert (internal->vlit (lit) < representant_id.size ()); + CADICAL_assert (internal->vlit (lit) < representant_id.size ()); return representant_id[internal->vlit (lit)]; } LRAT_ID Closure::representative_id (int lit) const { - assert (internal->vlit (lit) < representant_id.size ()); + CADICAL_assert (internal->vlit (lit) < representant_id.size ()); return representant_id[internal->vlit (lit)]; } void Closure::mark_garbage (Gate *g) { LOG (g, "marking as garbage"); - assert (!g->garbage); + CADICAL_assert (!g->garbage); g->garbage = true; garbage.push_back (g); } bool Closure::remove_gate (GatesTable::iterator git) { - assert (git != end (table)); - assert (!internal->unsat); + CADICAL_assert (git != end (table)); + CADICAL_assert (!internal->unsat); (*git)->indexed = false; LOG ((*git), "removing from hash table"); table.erase (git); @@ -668,8 +672,8 @@ bool Closure::remove_gate (GatesTable::iterator git) { bool Closure::remove_gate (Gate *g) { if (!g->indexed) return false; - assert (!internal->unsat); - assert (table.find (g) != end (table)); + CADICAL_assert (!internal->unsat); + CADICAL_assert (table.find (g) != end (table)); table.erase (table.find (g)); g->indexed = false; LOG (g, "removing from hash table"); @@ -677,10 +681,10 @@ bool Closure::remove_gate (Gate *g) { } void Closure::index_gate (Gate *g) { - assert (!g->indexed); - assert (!internal->unsat); - assert (g->arity () > 1); - assert (g->hash == hash_lits (nonces, g->rhs)); + 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; @@ -688,9 +692,9 @@ void Closure::index_gate (Gate *g) { void Closure::produce_rewritten_clause_lrat_and_clean ( std::vector<LitClausePair> &litIds, int except_lhs, bool remove_units) { - assert (internal->lrat_chain.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); for (auto &litId : litIds) { - assert (litId.clause); + 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); @@ -704,11 +708,11 @@ void Closure::produce_rewritten_clause_lrat_and_clean ( void Closure::produce_rewritten_clause_lrat_and_clean ( std::vector<LitClausePair> &litIds, int except_lhs, size_t &old_position1, size_t &old_position2, bool remove_units) { - assert (internal->lrat_chain.empty ()); - assert (old_position1 != old_position2); + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (old_position1 != old_position2); size_t j = 0; for (size_t i = 0; i < litIds.size (); ++i) { - assert (j <= i); + CADICAL_assert (j <= i); litIds[j].clause = produce_rewritten_clause_lrat ( litIds[i].clause, except_lhs, remove_units); litIds[j].current_lit = @@ -732,10 +736,10 @@ void Closure::produce_rewritten_clause_lrat_and_clean ( } void Closure::compute_rewritten_clause_lrat_simple (Clause *c, int except) { - assert (internal->clause.empty ()); - assert (internal->lrat_chain.empty ()); - assert (clause.empty ()); - assert (lrat_chain.empty ()); + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (lrat_chain.empty ()); bool changed = false; bool tautology = false; for (auto lit : *c) { @@ -804,23 +808,23 @@ void Closure::compute_rewritten_clause_lrat_simple (Clause *c, int except) { LOG (lrat_chain, "LRAT chain"); } else { LOG (c, "oops this should not happen"); - assert (false); + CADICAL_assert (false); } } Clause *Closure::new_tmp_clause (std::vector<int> &clause) { - assert (internal->lrat); - assert (!clause.empty ()); - assert (!lrat_chain.empty ()); + CADICAL_assert (internal->lrat); + CADICAL_assert (!clause.empty ()); + CADICAL_assert (!lrat_chain.empty ()); bool clear = false; LOG (clause, "learn new tmp clause"); - assert (clause.size () >= 2); + CADICAL_assert (clause.size () >= 2); internal->external->check_learned_clause (); - assert (internal->clause.size () <= (size_t) INT_MAX); + CADICAL_assert (internal->clause.size () <= (size_t) INT_MAX); const int size = (int) clause.size (); - assert (size >= 2); + CADICAL_assert (size >= 2); size_t bytes = Clause::bytes (size); Clause *c = (Clause *) new char[bytes]; @@ -857,7 +861,7 @@ Clause *Closure::new_tmp_clause (std::vector<int> &clause) { // Just checking that we did not mess up our sophisticated memory layout. // This might be compiler dependent though. Crucial for correctness. // - assert (c->bytes () == bytes); + CADICAL_assert (c->bytes () == bytes); clause_delete.release (); LOG (c, "new pointer %p", (void *) c); @@ -869,12 +873,12 @@ Clause *Closure::new_tmp_clause (std::vector<int> &clause) { internal->proof->add_derived_clause (c, lrat_chain); } extra_clauses.push_back (c); - assert (internal->lrat_chain.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); return c; } Clause *Closure::new_clause () { - assert (internal->clause.empty () || clause.empty ()); + CADICAL_assert (internal->clause.empty () || clause.empty ()); bool clear = false; if (internal->clause.empty ()) { swap (internal->clause, clause); @@ -897,7 +901,7 @@ Clause *Closure::new_clause () { // probably do not need that. void Closure::produce_rewritten_clause_lrat ( std::vector<LitClausePair> &litIds, int except_lhs, bool remove_units) { - assert (internal->lrat_chain.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); for (auto &litId : litIds) { if (!litId.clause) continue; @@ -909,14 +913,14 @@ void Closure::produce_rewritten_clause_lrat ( Clause *Closure::produce_rewritten_clause_lrat (Clause *c, int except_lhs, bool remove_units, bool fail_on_unit) { - assert (internal->clause.empty ()); - assert (internal->lrat_chain.empty ()); + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); auto tmp_lrat (std::move (lrat_chain)); lrat_chain.clear (); LOG (c, "rewriting clause for LRAT proof, except for rewriting %d", except_lhs); - assert (internal->clause.empty ()); - assert (lrat_chain.empty ()); + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (lrat_chain.empty ()); bool changed = false; bool tautology = false; for (auto lit : *c) { @@ -978,7 +982,7 @@ Clause *Closure::produce_rewritten_clause_lrat (Clause *c, int except_lhs, lrat_chain.push_back (c->id); Clause *d; - assert (internal->clause.empty ()); + CADICAL_assert (internal->clause.empty ()); if (tautology) { LOG ("generated clause is a tautology"); d = nullptr; @@ -987,7 +991,7 @@ Clause *Closure::produce_rewritten_clause_lrat (Clause *c, int except_lhs, LOG (lrat_chain, "LRAT chain"); if (fail_on_unit) { d = nullptr; - assert (false && "rewriting produced a unit clause"); + CADICAL_assert (false && "rewriting produced a unit clause"); } else { d = c; } @@ -1002,7 +1006,7 @@ Clause *Closure::produce_rewritten_clause_lrat (Clause *c, int except_lhs, } clause.clear (); lrat_chain = std::move (tmp_lrat); - assert (internal->clause.empty ()); + CADICAL_assert (internal->clause.empty ()); return d; } @@ -1025,11 +1029,11 @@ void Closure::push_id_and_rewriting_lrat_unit (Clause *c, Rewrite rewrite1, ", %" PRIu64 ") and skipping %d and %d", rewrite1.src, rewrite1.id1, rewrite1.id2, rewrite2.src, rewrite2.id1, rewrite2.id2, except_lhs, except_lhs2); - assert (c); + CADICAL_assert (c); std::vector<LRAT_ID> units, rewriting; for (auto other : *c) { // unclear how to achieve this in the simplify context where other == - // g->lhs might be set assert (internal->val (other) <= 0 || other == + // g->lhs might be set CADICAL_assert (internal->val (other) <= 0 || other == // except); if (other == except_lhs || other == -except_lhs) { // do nothing; @@ -1048,9 +1052,9 @@ void Closure::push_id_and_rewriting_lrat_unit (Clause *c, Rewrite rewrite1, } else if (other == -rewrite2.src && rewrite2.id2) { push_id_on_chain (rewriting, rewrite2, other); } else if (other != find_eager_representative_and_compress (other)) { -#if defined(LOGGING) || !defined(NDEBUG) +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) const int rewritten_other = eager_representative (other); - assert (other != rewritten_other); + CADICAL_assert (other != rewritten_other); LOG ("reason for representative of %d %d is %" PRIu64 "", other, rewritten_other, find_eager_representative_lrat (other)); #endif @@ -1086,12 +1090,12 @@ void Closure::push_id_and_rewriting_lrat_full (Clause *c, Rewrite rewrite1, ") and skipping %d and %d", rewrite1.src, rewrite1.id1, rewrite1.id2, rewrite2.src, rewrite2.id1, rewrite2.id2, except_lhs, except_lhs2); - assert (c); + CADICAL_assert (c); if (!insert_id_after) chain.push_back (c->id); for (auto other : *c) { // unclear how to achieve this in the simplify context where other == - // g->lhs might be set assert (internal->val (other) <= 0 || other == + // g->lhs might be set CADICAL_assert (internal->val (other) <= 0 || other == // except); if (other == except_lhs) { // do nothing; @@ -1100,7 +1104,7 @@ void Closure::push_id_and_rewriting_lrat_full (Clause *c, Rewrite rewrite1, } else if (internal->val (other) < 0) { LOG ("found unit %d", -other); LRAT_ID id = internal->unit_id (-other); - assert (id); + CADICAL_assert (id); chain.push_back (id); } else if (other == rewrite1.src && rewrite1.id1) { push_id_on_chain (chain, rewrite1, other); @@ -1111,7 +1115,7 @@ void Closure::push_id_and_rewriting_lrat_full (Clause *c, Rewrite rewrite1, } else if (other == -rewrite2.src && rewrite2.id2) { push_id_on_chain (chain, rewrite2, other); } else { - assert (other == find_eager_representative (other)); + CADICAL_assert (other == find_eager_representative (other)); LOG ("no rewriting needed for %d", other); } } @@ -1123,7 +1127,7 @@ void Closure::push_id_and_rewriting_lrat_full (Clause *c, Rewrite rewrite1, // because we can force rewriting that way that have not been done eagerly // yet. void Closure::push_id_on_chain (std::vector<LRAT_ID> &chain, Clause *c) { - assert (c); + CADICAL_assert (c); chain.push_back (c->id); LOG (lrat_chain, "chain"); } @@ -1144,9 +1148,9 @@ void Closure::learn_congruence_unit_when_lhs_set (Gate *g, int src, if (!internal->lrat) return; LOG ("calculating LRAT chain learn_congruence_unit_when_lhs_set"); - assert (!g->pos_lhs_ids.empty ()); - assert (internal->analyzed.empty ()); - assert (internal->val (g->lhs) < 0); + CADICAL_assert (!g->pos_lhs_ids.empty ()); + CADICAL_assert (internal->analyzed.empty ()); + CADICAL_assert (internal->val (g->lhs) < 0); switch (g->tag) { case Gate_Type::And_Gate: LOG (lrat_chain, "lrat"); @@ -1157,7 +1161,7 @@ void Closure::learn_congruence_unit_when_lhs_set (Gate *g, int src, LOG (lrat_chain, "lrat"); break; default: - assert (false); + CADICAL_assert (false); } } @@ -1167,9 +1171,9 @@ void Closure::learn_congruence_unit_falsifies_lrat_chain ( Gate *g, int src, int dst, int clashing, int falsified, int unit) { if (!internal->lrat) return; - assert (!g->pos_lhs_ids.empty ()); - assert (internal->analyzed.empty ()); - assert (lrat_chain.empty ()); + CADICAL_assert (!g->pos_lhs_ids.empty ()); + CADICAL_assert (internal->analyzed.empty ()); + CADICAL_assert (lrat_chain.empty ()); std::vector<LRAT_ID> proof_chain; switch (g->tag) { case Gate_Type::And_Gate: @@ -1211,7 +1215,7 @@ void Closure::learn_congruence_unit_falsifies_lrat_chain ( LOG (proof_chain, "produced lrat chain so far"); } } - assert (!proof_chain.empty ()); + CADICAL_assert (!proof_chain.empty ()); } else { LOG ("degenerated AND gate with conflict without LHS"); for (const auto &litId : g->pos_lhs_ids) { @@ -1251,7 +1255,7 @@ void Closure::learn_congruence_unit_falsifies_lrat_chain ( } } } else { - assert (unit); + 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 (), @@ -1263,7 +1267,7 @@ void Closure::learn_congruence_unit_falsifies_lrat_chain ( lrat_chain = std::move (proof_chain); break; default: - assert (false); + CADICAL_assert (false); } (void) unit; } @@ -1272,8 +1276,8 @@ bool Closure::fully_propagate () { if (internal->unsat) return false; LOG ("fully propagating"); - assert (internal->watching ()); - assert (full_watching); + CADICAL_assert (internal->watching ()); + CADICAL_assert (full_watching); bool no_conflict = internal->propagate (); if (no_conflict) @@ -1298,10 +1302,10 @@ bool Closure::learn_congruence_unit (int lit, bool delay_propagation, bool force } LOG ("adding unit %s", LOGLIT (lit)); ++internal->stats.congruence.units; - assert (!internal->lrat || !lrat_chain.empty ()); + CADICAL_assert (!internal->lrat || !lrat_chain.empty ()); if (val_lit < 0) { if (internal->lrat) { - assert (internal->lrat_chain.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); LRAT_ID id = internal->unit_id (-lit); internal->lrat_chain.push_back (id); for (auto id : lrat_chain) @@ -1315,8 +1319,8 @@ bool Closure::learn_congruence_unit (int lit, bool delay_propagation, bool force LOG (lrat_chain, "assigning due to LRAT chain"); swap (lrat_chain, internal->lrat_chain); internal->assign_unit (lit); - assert (lrat_chain.empty ()); - assert (internal->lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); if (delay_propagation) return false; else return fully_propagate (); @@ -1329,9 +1333,9 @@ bool Closure::merge_literals_lrat ( Gate *g, Gate *h, int lit, int other, const std::vector<LRAT_ID> &extra_reasons_lit, const std::vector<LRAT_ID> &extra_reasons_ulit) { - assert (!internal->unsat); - assert (g->lhs == lit); - assert (g == h || h->lhs == other); + CADICAL_assert (!internal->unsat); + CADICAL_assert (g->lhs == lit); + CADICAL_assert (g == h || h->lhs == other); (void) g, (void) h; LOG ("merging literals %s and %s", LOGLIT(lit), LOGLIT(other)); // TODO: this should not update_eager but still calculate the LRAT chain @@ -1392,10 +1396,10 @@ bool Closure::merge_literals_lrat ( swap (smaller_chain, larger_chain); } - assert (find_representative (smaller_repr) == smaller_repr); - assert (find_representative (larger_repr) == larger_repr); + CADICAL_assert (find_representative (smaller_repr) == smaller_repr); + CADICAL_assert (find_representative (larger_repr) == larger_repr); if (lit == -other) { - assert (chain.empty ()); + CADICAL_assert (chain.empty ()); LOG ("merging clashing %d and %d", lit, other); if (internal->proof) { if (internal->lrat) { @@ -1425,7 +1429,7 @@ bool Closure::merge_literals_lrat ( lrat_chain = *smaller_chain; eq1_tmp = add_tmp_binary_clause (-larger, smaller); } - assert (!internal->lrat || eq1_tmp); + CADICAL_assert (!internal->lrat || eq1_tmp); Clause *eq2_tmp = nullptr; if (internal->lrat) { @@ -1436,7 +1440,7 @@ bool Closure::merge_literals_lrat ( // the order in the clause is important for the // repr_lit == -repr_other to get the right chain } - assert (!internal->lrat || eq2_tmp); + CADICAL_assert (!internal->lrat || eq2_tmp); if (internal->lrat) lrat_chain.clear (); @@ -1484,7 +1488,7 @@ bool Closure::merge_literals_lrat ( if (val_lit == -val_other) { LOG ("merging lits %d and %d assigned to inconsistent value", lit, other); - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); if (internal->lrat) { Clause *c = val_lit ? eq2_tmp : eq1_tmp; int pos = val_lit ? other : lit; @@ -1499,9 +1503,9 @@ bool Closure::merge_literals_lrat ( return false; } - assert (!val_other); + CADICAL_assert (!val_other); LOG ("merging assigned %d and unassigned %d", lit, other); - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); const int unit = (val_lit < 0) ? -other : other; if (internal->lrat) { Clause *c; @@ -1527,7 +1531,7 @@ bool Closure::merge_literals_lrat ( if (!val_lit && val_other) { LOG ("merging assigned %d and unassigned %d", lit, other); - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); const int unit = (val_other < 0) ? -lit : lit; if (internal->lrat) { Clause *c; @@ -1611,10 +1615,10 @@ bool Closure::merge_literals_lrat ( if (internal->lrat) { representative_id (larger_repr) = eq1_repr->id; - assert (std::find (begin (*eq1_repr), end (*eq1_repr), -larger_repr) != + CADICAL_assert (std::find (begin (*eq1_repr), end (*eq1_repr), -larger_repr) != end (*eq1_repr)); representative_id (-larger_repr) = eq2_repr->id; - assert (std::find (begin (*eq2_repr), end (*eq2_repr), larger_repr) != + CADICAL_assert (std::find (begin (*eq2_repr), end (*eq2_repr), larger_repr) != end (*eq2_repr)); } LOG ("updating %d -> %d", larger_repr, smaller_repr); @@ -1622,7 +1626,7 @@ bool Closure::merge_literals_lrat ( representative (-larger_repr) = -smaller_repr; schedule_literal (larger_repr); ++internal->stats.congruence.congruent; - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); return true; } @@ -1632,7 +1636,7 @@ bool Closure::merge_literals_lrat ( bool Closure::merge_literals_lrat ( int lit, int other, const std::vector<LRAT_ID> &extra_reasons_lit, const std::vector<LRAT_ID> &extra_reasons_ulit) { - assert (!internal->unsat); + CADICAL_assert (!internal->unsat); LOG ("merging literals %s and %s", LOGLIT (lit), LOGLIT (other)); // TODO: this should not update_eager but still calculate the LRAT chain // below! @@ -1688,8 +1692,8 @@ bool Closure::merge_literals_lrat ( swap (val_smaller, val_larger); } - assert (find_representative (smaller_repr) == smaller_repr); - assert (find_representative (larger_repr) == larger_repr); + 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) { @@ -1701,7 +1705,7 @@ bool Closure::merge_literals_lrat ( push_lrat_unit (smaller); if (internal->lrat) { - assert (internal->lrat_chain.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); swap (internal->lrat_chain, lrat_chain); for (auto id : *larger_chain) internal->lrat_chain.push_back (id); @@ -1747,9 +1751,9 @@ bool Closure::merge_literals_lrat ( if (internal->lrat) { lrat_chain = *smaller_chain; eq1_tmp = add_tmp_binary_clause (-larger, smaller); - assert (eq1_tmp); + CADICAL_assert (eq1_tmp); } - assert (!internal->lrat || eq1_tmp); + CADICAL_assert (!internal->lrat || eq1_tmp); Clause *eq2_tmp = nullptr; if (internal->lrat) { @@ -1758,10 +1762,10 @@ bool Closure::merge_literals_lrat ( // 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); - assert (eq2_tmp); + CADICAL_assert (eq2_tmp); } - assert (!internal->lrat || eq2_tmp); + CADICAL_assert (!internal->lrat || eq2_tmp); if (internal->lrat) lrat_chain.clear (); @@ -1781,7 +1785,7 @@ bool Closure::merge_literals_lrat ( rew2); swap (lrat_chain, internal->lrat_chain); } - assert (val_larger == internal->val (larger_repr)); + CADICAL_assert (val_larger == internal->val (larger_repr)); if (!val_larger) { // not assigned, first assign one internal->assign_unit (-larger_repr); @@ -1819,7 +1823,7 @@ bool Closure::merge_literals_lrat ( if (val_lit == -val_other) { LOG ("merging lits %d and %d assigned to inconsistent value", lit, other); - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); if (internal->lrat) { Clause *c = val_lit ? eq2_tmp : eq1_tmp; int pos = val_lit ? other : lit; @@ -1834,9 +1838,9 @@ bool Closure::merge_literals_lrat ( return false; } - assert (!val_other); + CADICAL_assert (!val_other); LOG ("merging assigned %d and unassigned %d", lit, other); - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); const int unit = (val_lit < 0) ? -other : other; if (internal->lrat) { Clause *c; @@ -1862,12 +1866,12 @@ bool Closure::merge_literals_lrat ( if (!val_lit && val_other) { LOG ("merging assigned %d and unassigned %d", lit, other); - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); const int unit = (val_other < 0) ? -lit : lit; if (internal->lrat) { Clause *c; if (lit == smaller) { - assert (other == larger); + CADICAL_assert (other == larger); if (val_other > 0) c = eq1_tmp; else @@ -1945,12 +1949,12 @@ bool Closure::merge_literals_lrat ( if (internal->lrat) { representative_id (larger_repr) = eq1_repr->id; - assert (std::find (begin (*eq1_repr), end (*eq1_repr), -larger_repr) != + 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); - assert (std::find (begin (*eq2_repr), end (*eq2_repr), larger_repr) != + CADICAL_assert (std::find (begin (*eq2_repr), end (*eq2_repr), larger_repr) != end (*eq2_repr)); } LOG ("updating %d -> %d", larger_repr, smaller_repr); @@ -1958,7 +1962,7 @@ bool Closure::merge_literals_lrat ( representative (-larger_repr) = -smaller_repr; schedule_literal (larger_repr); ++internal->stats.congruence.congruent; - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); return true; } @@ -1976,9 +1980,9 @@ inline void Closure::promote_clause (Clause *c) { internal->stats.current.irredundant++; internal->stats.added.irredundant++; internal->stats.irrlits += c->size; - assert (internal->stats.current.redundant > 0); + CADICAL_assert (internal->stats.current.redundant > 0); internal->stats.current.redundant--; - assert (internal->stats.added.redundant > 0); + CADICAL_assert (internal->stats.added.redundant > 0); internal->stats.added.redundant--; // ... and keep 'stats.added.total'. } @@ -1989,18 +1993,18 @@ inline void Closure::promote_clause (Clause *c) { // vice-versa bool Closure::merge_literals_equivalence (int lit, int other, Clause *c1, Clause *c2) { - assert (!internal->unsat); + CADICAL_assert (!internal->unsat); LRAT_ID id1 = c1 ? c1->id : 0; LRAT_ID id2 = c2 ? c2->id : 0; if (internal->lrat) { - assert (c1); - assert (c2); - assert (c1->size == 2); - assert (c2->size == 2); - assert (c1->literals[0] == lit || c1->literals[1] == lit); - assert (c2->literals[0] == other || c2->literals[1] == other); - assert (c1->literals[0] == -other || c1->literals[1] == -other); - assert (c2->literals[0] == -lit || c2->literals[1] == -lit); + CADICAL_assert (c1); + CADICAL_assert (c2); + CADICAL_assert (c1->size == 2); + CADICAL_assert (c2->size == 2); + CADICAL_assert (c1->literals[0] == lit || c1->literals[1] == lit); + CADICAL_assert (c2->literals[0] == other || c2->literals[1] == other); + CADICAL_assert (c1->literals[0] == -other || c1->literals[1] == -other); + CADICAL_assert (c2->literals[0] == -lit || c2->literals[1] == -lit); check_not_tmp_binary_clause (c1); check_not_tmp_binary_clause (c2); } @@ -2033,7 +2037,7 @@ bool Closure::merge_literals_equivalence (int lit, int other, Clause *c1, return false; } - assert (!val_other); + CADICAL_assert (!val_other); LOG ("merging assigned %d and unassigned %d", lit, other); const int unit = (val_lit < 0) ? -other : other; if (internal->lrat) @@ -2070,8 +2074,8 @@ bool Closure::merge_literals_equivalence (int lit, int other, Clause *c1, swap (smaller, larger); } - assert (find_representative (smaller_repr) == smaller_repr); - assert (find_representative (larger_repr) == larger_repr); + CADICAL_assert (find_representative (smaller_repr) == smaller_repr); + CADICAL_assert (find_representative (larger_repr) == larger_repr); if (repr_lit == -repr_other) { LOG ("merging clashing %d [=%d] and %d[=%d], smaller: %d", lit, @@ -2093,7 +2097,7 @@ bool Closure::merge_literals_equivalence (int lit, int other, Clause *c1, if (internal->lrat) { lrat_chain.clear (); LRAT_ID id = internal->unit_id (repr_lit); - assert (id); + CADICAL_assert (id); lrat_chain.push_back (id); if (lit != repr_lit) { const LRAT_ID repr_id2 = find_representative_lrat (-lit); @@ -2140,20 +2144,20 @@ bool Closure::merge_literals_equivalence (int lit, int other, Clause *c1, if (internal->lrat) { lrat_chain.clear (); if (smaller_repr == repr_lit) { - assert (larger_repr == repr_other); + CADICAL_assert (larger_repr == repr_other); representative_id (-larger_repr) = eq2->id; - assert (std::find (eq2->begin (), eq2->end (), larger_repr) != + CADICAL_assert (std::find (eq2->begin (), eq2->end (), larger_repr) != eq2->end ()); representative_id (larger_repr) = eq1->id; - assert (std::find (eq1->begin (), eq1->end (), -larger_repr) != + CADICAL_assert (std::find (eq1->begin (), eq1->end (), -larger_repr) != eq1->end ()); } else { - assert (larger_repr == repr_lit); + CADICAL_assert (larger_repr == repr_lit); representative_id (-larger_repr) = eq1->id; - assert (std::find (eq1->begin (), eq1->end (), larger_repr) != + CADICAL_assert (std::find (eq1->begin (), eq1->end (), larger_repr) != eq1->end ()); representative_id (larger_repr) = eq2->id; - assert (std::find (eq2->begin (), eq2->end (), -larger_repr) != + CADICAL_assert (std::find (eq2->begin (), eq2->end (), -larger_repr) != eq2->end ()); } } @@ -2187,13 +2191,13 @@ GOccs &Closure::goccs (int lit) { return gtab[internal->vlit (lit)]; } void Closure::connect_goccs (Gate *g, int lit) { LOG (g, "connect %d to", lit); // incorrect for ITE - // assert (std::find(begin (goccs (lit)), end (goccs (lit)), g) == + // CADICAL_assert (std::find(begin (goccs (lit)), end (goccs (lit)), g) == // std::end (goccs (lit))); goccs (lit).push_back (g); } uint64_t &Closure::largecount (int lit) { - assert (internal->vlit (lit) < glargecounts.size ()); + CADICAL_assert (internal->vlit (lit) < glargecounts.size ()); return glargecounts[internal->vlit (lit)]; } @@ -2216,7 +2220,7 @@ void Closure::init_closure () { mu2_ids.resize (2 * internal->max_var + 3); mu4_ids.resize (2 * internal->max_var + 3); } -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG for (auto &it : mu1_ids) it.current_lit = 0, it.clause = nullptr; for (auto &it : mu2_ids) @@ -2253,7 +2257,7 @@ void Closure::init_and_gate_extraction () { continue; if (c->size > 2) continue; - assert (c->size == 2); + CADICAL_assert (c->size == 2); const int lit = c->literals[0]; const int other = c->literals[1]; internal->noccs (lit)++; @@ -2264,8 +2268,8 @@ void Closure::init_and_gate_extraction () { /*------------------------------------------------------------------------*/ void Closure::check_and_gate_implied (Gate *g) { - assert (internal->clause.empty ()); - assert (g->tag == Gate_Type::And_Gate); + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (g->tag == Gate_Type::And_Gate); if (internal->lrat) return; LOG (g, "checking implied"); @@ -2280,14 +2284,14 @@ void Closure::check_and_gate_implied (Gate *g) { void Closure::delete_proof_chain () { if (!internal->proof) { - assert (chain.empty ()); + CADICAL_assert (chain.empty ()); return; } if (chain.empty ()) return; LOG ("starting deletion of proof chain"); auto &clause = internal->clause; - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); uint32_t id1 = UINT32_MAX, id2 = UINT32_MAX; LRAT_ID id = 0; @@ -2308,13 +2312,13 @@ void Closure::delete_proof_chain () { LOG ("found %d as literal in chain", lit); clause.push_back (lit); } else { - assert (id); + CADICAL_assert (id); internal->proof->delete_clause (id, false, clause); clause.clear (); id = 0, id1 = UINT32_MAX, id2 = UINT32_MAX; } } - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); chain.clear (); LOG ("finished deletion of proof chain"); } @@ -2322,7 +2326,7 @@ void Closure::delete_proof_chain () { /*------------------------------------------------------------------------*/ // Simplification bool Closure::skip_and_gate (Gate *g) { - assert (g->tag == Gate_Type::And_Gate); + CADICAL_assert (g->tag == Gate_Type::And_Gate); if (g->garbage) return true; const int lhs = g->lhs; @@ -2331,15 +2335,15 @@ bool Closure::skip_and_gate (Gate *g) { return true; } - assert (g->arity () > 1); + CADICAL_assert (g->arity () > 1); return false; } bool Closure::skip_xor_gate (Gate *g) { - assert (g->tag == Gate_Type::XOr_Gate); + CADICAL_assert (g->tag == Gate_Type::XOr_Gate); if (g->garbage) return true; - assert (g->arity () > 1); + CADICAL_assert (g->arity () > 1); return false; } @@ -2371,17 +2375,17 @@ void Closure::update_and_gate_unit_build_lrat_chain ( // 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 - assert (g->pos_lhs_ids.size () == 1); + CADICAL_assert (g->pos_lhs_ids.size () == 1); push_id_and_rewriting_lrat_unit (g->pos_lhs_ids[0].clause, Rewrite (), extra_reasons_ulit, true, Rewrite ()); return; } - assert (g->lhs == g->rhs[0] || (g->lhs == src && g->rhs[0] == dst)); - assert (g->pos_lhs_ids.size () <= 1); // either degenerated or empty A = A + CADICAL_assert (g->lhs == g->rhs[0] || (g->lhs == src && g->rhs[0] == dst)); + CADICAL_assert (g->pos_lhs_ids.size () <= 1); // either degenerated or empty A = A return; } - assert (g->neg_lhs_ids.size () == 1); - assert (!g->pos_lhs_ids.empty ()); + CADICAL_assert (g->neg_lhs_ids.size () == 1); + CADICAL_assert (!g->pos_lhs_ids.empty ()); const int repr_lit = find_representative (g->lhs); const int repr_other = find_representative (g->rhs[0]); @@ -2397,7 +2401,7 @@ void Closure::update_and_gate_unit_build_lrat_chain ( lrat_chain.clear (); - assert (!g->pos_lhs_ids.empty ()); + CADICAL_assert (!g->pos_lhs_ids.empty ()); for (auto litId : g->pos_lhs_ids) push_id_and_rewriting_lrat_unit ( litId.clause, Rewrite (src, dst, id1, id2), extra_reasons_lit, true, @@ -2408,7 +2412,7 @@ void Closure::update_and_gate_unit_build_lrat_chain ( void Closure::update_and_gate_build_lrat_chain ( Gate *g, Gate *h, std::vector<LRAT_ID> &extra_reasons_lit, std::vector<LRAT_ID> &extra_reasons_ulit, bool remove_units) { - assert (g != h); + CADICAL_assert (g != h); LOG (g, "merging"); LOG (h, "with"); // If the LHS are identical, do not even attempt to build the LRAT chain @@ -2425,41 +2429,41 @@ void Closure::update_and_gate_build_lrat_chain ( // and all gate clauses (except one binary) are actually tautologies for (auto &litId : g->pos_lhs_ids) { if (litId.current_lit == h->lhs) { - assert (extra_reasons_lit.empty ()); + CADICAL_assert (extra_reasons_lit.empty ()); LOG (litId.clause, "binary clause to push into the reason"); litId.clause = produce_rewritten_clause_lrat (litId.clause, g->lhs, remove_units); - assert (litId.clause); + CADICAL_assert (litId.clause); extra_reasons_lit.push_back (litId.clause->id); } } - assert (!extra_reasons_lit.empty ()); - assert (extra_reasons_lit.size () == 1); + CADICAL_assert (!extra_reasons_lit.empty ()); + CADICAL_assert (extra_reasons_lit.size () == 1); for (auto &litId : h->pos_lhs_ids) { if (litId.current_lit == g->lhs) { - assert (extra_reasons_ulit.empty ()); - assert (litId.clause); + CADICAL_assert (extra_reasons_ulit.empty ()); + CADICAL_assert (litId.clause); LOG (litId.clause, "binary clause to push into the reason"); litId.clause = produce_rewritten_clause_lrat (litId.clause, h->lhs, remove_units); - assert (litId.clause); + CADICAL_assert (litId.clause); extra_reasons_ulit.push_back (litId.clause->id); } } - assert (!extra_reasons_ulit.empty ()); - assert (extra_reasons_ulit.size () == 1); + CADICAL_assert (!extra_reasons_ulit.empty ()); + CADICAL_assert (extra_reasons_ulit.size () == 1); return; } if (g_tautology || h_tautology) { // special case: actually we have an equivalence due to binary clauses // and some of the clauses from the gate are actually tautologies - assert (g_tautology != h_tautology); + CADICAL_assert (g_tautology != h_tautology); Gate *tauto = (g_tautology ? g : h); Gate *other = (g_tautology ? h : g); LOG (tauto, "one gate is a tautology"); - assert (tauto != other); - assert (tauto == h || tauto == g); + CADICAL_assert (tauto != other); + CADICAL_assert (tauto == h || tauto == g); auto &extra_reasons_tauto = (!g_tautology ? extra_reasons_lit : extra_reasons_ulit); @@ -2469,32 +2473,32 @@ 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) { - assert (litId.clause); - assert (extra_reasons_tauto.empty ()); + CADICAL_assert (litId.clause); + CADICAL_assert (extra_reasons_tauto.empty ()); LOG (litId.clause, "binary clause to push into the reason"); litId.clause = produce_rewritten_clause_lrat ( litId.clause, other->lhs, remove_units); - assert (litId.clause); + CADICAL_assert (litId.clause); extra_reasons_tauto.push_back (litId.clause->id); } } - assert (!extra_reasons_tauto.empty ()); + CADICAL_assert (!extra_reasons_tauto.empty ()); // other direction, we have to resolve LOG (tauto, "now the other direction"); for (auto &litId : tauto->pos_lhs_ids) { - assert (litId.clause); + CADICAL_assert (litId.clause); LOG (litId.clause, "binary clause from %d to push into the reason [avoiding %d]", litId.current_lit, tauto->lhs); if (litId.current_lit != tauto->lhs) { LOG (litId.clause, "binary clause to push into the reason"); - 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; - assert (litId.clause); + 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), @@ -2504,12 +2508,12 @@ void Closure::update_and_gate_build_lrat_chain ( }), end (tauto->pos_lhs_ids)); } - assert (!extra_reasons_other.empty ()); + 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); - assert (!extra_reasons_tauto.empty ()); - assert (!extra_reasons_other.empty ()); + CADICAL_assert (!extra_reasons_tauto.empty ()); + CADICAL_assert (!extra_reasons_other.empty ()); return; } // default: resolve all clauses @@ -2517,16 +2521,16 @@ void Closure::update_and_gate_build_lrat_chain ( // TODO: do we really need dest as second exclusion? produce_rewritten_clause_lrat_and_clean (h->pos_lhs_ids, -h->lhs, remove_units); - assert (internal->clause.empty ()); + CADICAL_assert (internal->clause.empty ()); produce_rewritten_clause_lrat_and_clean (h->neg_lhs_ids, -h->lhs, remove_units); - assert (internal->clause.empty ()); + CADICAL_assert (internal->clause.empty ()); produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, -g->lhs, remove_units); - assert (internal->clause.empty ()); + CADICAL_assert (internal->clause.empty ()); produce_rewritten_clause_lrat_and_clean (g->neg_lhs_ids, -g->lhs, remove_units); - assert (internal->clause.empty ()); + 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); @@ -2538,15 +2542,15 @@ void Closure::update_and_gate_build_lrat_chain ( lrat_chain.clear (); LOG (extra_reasons_lit, "lrat chain for positive side"); - assert (!extra_reasons_lit.empty ()); - assert (!extra_reasons_ulit.empty ()); + CADICAL_assert (!extra_reasons_lit.empty ()); + CADICAL_assert (!extra_reasons_ulit.empty ()); } void Closure::update_and_gate (Gate *g, GatesTable::iterator it, int src, int dst, LRAT_ID id1, LRAT_ID id2, int falsifies, int clashing) { LOG (g, "update and gate of arity %ld", g->arity ()); - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); bool garbage = true; if (falsifies || clashing) { if (internal->lrat) @@ -2589,12 +2593,12 @@ void Closure::update_and_gate (Gate *g, GatesTable::iterator it, int src, } } } else { - assert (g->arity () > 1); + CADICAL_assert (g->arity () > 1); sort_literals_by_var (g->rhs); Gate *h = find_and_lits (g->rhs, g); - assert (g != h); + CADICAL_assert (g != h); if (h) { - assert (garbage); + CADICAL_assert (garbage); std::vector<LRAT_ID> extra_reasons_lit2; std::vector<LRAT_ID> extra_reasons_ulit2; if (internal->lrat) @@ -2610,7 +2614,7 @@ void Closure::update_and_gate (Gate *g, GatesTable::iterator it, int src, } g->hash = hash_lits (nonces, g->rhs); LOG (g, "inserting gate into table"); - assert (table.count (g) == 0); + CADICAL_assert (table.count (g) == 0); table.insert (g); g->indexed = true; garbage = false; @@ -2624,23 +2628,23 @@ void Closure::update_and_gate (Gate *g, GatesTable::iterator it, int src, } void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { - assert (g->tag == Gate_Type::XOr_Gate); - assert (!internal->unsat && chain.empty ()); + CADICAL_assert (g->tag == Gate_Type::XOr_Gate); + CADICAL_assert (!internal->unsat && chain.empty ()); LOG (g, "updating"); bool garbage = true; // TODO Florian LRAT for learn_congruence_unit - assert (g->arity () == 0 || internal->clause.empty ()); - assert (clause.empty ()); + CADICAL_assert (g->arity () == 0 || internal->clause.empty ()); + CADICAL_assert (clause.empty ()); if (g->arity () == 0) { if (internal->clause.size ()) { - assert (!internal->proof || (internal->clause.size () == 1 && + CADICAL_assert (!internal->proof || (internal->clause.size () == 1 && internal->clause.back () == -g->lhs)); - assert (!internal->lrat || lrat_chain.size ()); + CADICAL_assert (!internal->lrat || lrat_chain.size ()); internal->clause.clear (); } else if (internal->lrat) { simplify_unit_xor_lrat_clauses (g->pos_lhs_ids, g->lhs); - assert (clause.size () && clause.back () == -g->lhs); + CADICAL_assert (clause.size () && clause.back () == -g->lhs); clause.clear (); } @@ -2653,14 +2657,14 @@ void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { } else learn_congruence_unit (-g->lhs); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); } else if (g->arity () == 1) { std::vector<LRAT_ID> reasons_implication, reasons_back; if (internal->lrat) { vector<LitClausePair> first; simplify_and_sort_xor_lrat_clauses (g->pos_lhs_ids, first, g->lhs); g->pos_lhs_ids = first; - assert (g->pos_lhs_ids.size () == 2); + CADICAL_assert (g->pos_lhs_ids.size () == 2); reasons_implication.push_back (g->pos_lhs_ids[0].clause->id); reasons_back.push_back (g->pos_lhs_ids[1].clause->id); } @@ -2683,11 +2687,11 @@ void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { ++internal->stats.congruence.unaries; ++internal->stats.congruence.unary_and; } - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); } else { Gate *h = find_xor_gate (g); if (h) { - assert (garbage); + CADICAL_assert (garbage); std::vector<LRAT_ID> reasons_implication, reasons_back; add_xor_matching_proof_chain (g, g->lhs, h->pos_lhs_ids, h->lhs, reasons_implication, reasons_back); @@ -2703,21 +2707,21 @@ void Closure::update_xor_gate (Gate *g, GatesTable::iterator git) { LOG (g, "reinserting in table"); table.insert (g); g->indexed = true; - assert (table.find (g) != end (table)); + CADICAL_assert (table.find (g) != end (table)); garbage = false; } - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); } if (garbage && !internal->unsat) mark_garbage (g); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); } void Closure::simplify_and_gate (Gate *g) { if (skip_and_gate (g)) return; GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); - assert (!g->indexed || git != end (table)); + CADICAL_assert (!g->indexed || git != end (table)); LOG (g, "simplifying"); int falsifies = 0; std::vector<int>::iterator it = begin (g->rhs); @@ -2756,9 +2760,9 @@ void Closure::simplify_and_gate (Gate *g) { g->pos_lhs_ids.resize (i); } - assert (it <= end (g->rhs)); // can be equal when ITE are converted to + CADICAL_assert (it <= end (g->rhs)); // can be equal when ITE are converted to // ands leading to - assert (it >= begin (g->rhs)); + CADICAL_assert (it >= begin (g->rhs)); LOG (g, "shrunken"); g->shrunken = true; @@ -2774,7 +2778,7 @@ void Closure::simplify_and_gate (Gate *g) { ++internal->stats.congruence.simplified; if (ulhs_in_rhs) { // missing in Kissat, TODO: port back - assert (gate_contains (g, -g->lhs)); + CADICAL_assert (gate_contains (g, -g->lhs)); if (internal->lrat) { for (auto litId : g->pos_lhs_ids) { if (litId.current_lit == g->lhs) { @@ -2799,18 +2803,18 @@ bool Closure::simplify_gate (Gate *g) { simplify_ite_gate (g); break; default: - assert (false); + CADICAL_assert (false); break; } - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); return !internal->unsat; } bool Closure::simplify_gates (int lit) { const auto &occs = goccs (lit); for (Gate *g : occs) { - assert (lrat_chain.empty ()); - assert (clause.empty ()); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (clause.empty ()); if (!simplify_gate (g)) return false; } @@ -2820,7 +2824,7 @@ bool Closure::simplify_gates (int lit) { // AND gates Gate *Closure::find_and_lits (const vector<int> &rhs, Gate *except) { - assert (is_sorted (begin (rhs), end (rhs), + CADICAL_assert (is_sorted (begin (rhs), end (rhs), sort_literals_by_var_smaller (internal))); return find_gate_lits (rhs, Gate_Type::And_Gate, except); } @@ -2845,7 +2849,7 @@ Gate *Closure::find_gate_lits (const vector<int> &rhs, Gate_Type typ, LOG ((*it), "checking gate in the table"); if (*it == except) continue; - assert ((*it)->lhs != g->lhs); + CADICAL_assert ((*it)->lhs != g->lhs); if ((*it)->tag != g->tag) continue; if ((*it)->rhs != g->rhs) @@ -2874,12 +2878,12 @@ Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { for (auto lit : lits) { if (lhs != lit) { - assert (lhs != -lit); + CADICAL_assert (lhs != -lit); rhs.push_back (-lit); } } - assert (rhs.size () + 1 == lits.size ()); + CADICAL_assert (rhs.size () + 1 == lits.size ()); sort_literals_by_var (this->rhs); Gate *h = find_and_lits (this->rhs); @@ -2920,7 +2924,7 @@ Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { return nullptr; } else { g->rhs = {rhs}; - assert (!internal->lrat || + CADICAL_assert (!internal->lrat || g->pos_lhs_ids.size () == g->arity ()); // otherwise we need intermediate clauses g->garbage = false; @@ -2942,7 +2946,7 @@ Gate *Closure::new_and_gate (Clause *base_clause, int lhs) { } Gate *Closure::find_first_and_gate (Clause *base_clause, int lhs) { - assert (internal->analyzed.empty ()); + CADICAL_assert (internal->analyzed.empty ()); const int not_lhs = -lhs; LOG ("trying to find AND gate with first LHS %d", (lhs)); LOG ("negated LHS %d occurs in %zd binary clauses", (not_lhs), @@ -2953,15 +2957,15 @@ Gate *Closure::find_first_and_gate (Clause *base_clause, int lhs) { for (auto w : internal->watches (not_lhs)) { LOG (w.clause, "checking clause for candidates"); - assert (w.binary ()); - assert (w.clause->size == 2); - assert (w.clause->literals[0] == -lhs || w.clause->literals[1] == -lhs); + CADICAL_assert (w.binary ()); + CADICAL_assert (w.clause->size == 2); + CADICAL_assert (w.clause->literals[0] == -lhs || w.clause->literals[1] == -lhs); const int other = w.blit; signed char &mark = marked (other); if (mark) { LOG ("marking %d mu2", other); ++matched; - assert (~(mark & 2)); + CADICAL_assert (~(mark & 2)); mark |= 2; internal->analyzed.push_back (other); set_mu2_reason (other, w.clause); @@ -2996,10 +3000,10 @@ Clause *Closure::learn_binary_tmp_or_full_clause (int a, int b) { } Clause *Closure::maybe_add_binary_clause (int a, int b) { - assert (internal->clause.empty ()); - assert (internal->lrat_chain.empty ()); - assert (!internal->lrat); - assert (lrat_chain.empty ()); + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (!internal->lrat); + CADICAL_assert (lrat_chain.empty ()); LOG ("learning binary clause %d %d", a, b); if (internal->unsat) return nullptr; @@ -3024,18 +3028,18 @@ Clause *Closure::maybe_add_binary_clause (int a, int b) { learn_congruence_unit (unit); return nullptr; } - assert (!a_value), assert (!b_value); + CADICAL_assert (!a_value), CADICAL_assert (!b_value); } return add_binary_clause (a, b); } Clause *Closure::add_binary_clause (int a, int b) { - assert (internal->clause.empty ()); + CADICAL_assert (internal->clause.empty ()); internal->clause.push_back (a); internal->clause.push_back (b); if (internal->lrat) { - assert (lrat_chain.size () >= 1); - assert (internal->lrat_chain.empty ()); + CADICAL_assert (lrat_chain.size () >= 1); + CADICAL_assert (internal->lrat_chain.empty ()); swap (internal->lrat_chain, lrat_chain); } LOG (internal->lrat_chain, "chain"); @@ -3051,19 +3055,19 @@ Clause *Closure::add_binary_clause (int a, int b) { if (internal->lrat) { internal->lrat_chain.clear (); } - assert (internal->clause.empty ()); - assert (internal->lrat_chain.empty ()); + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); return res; } void Closure::check_not_tmp_binary_clause (Clause *c) { -#ifndef NDEBUG - assert (internal->lrat); - assert (internal->lrat_chain.empty ()); - assert (c->size == 2); +#ifndef CADICAL_NDEBUG + CADICAL_assert (internal->lrat); + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (c->size == 2); if (internal->val (c->literals[0]) || internal->val (c->literals[1])) return; - assert (std::find (begin (extra_clauses), end (extra_clauses), c) == + CADICAL_assert (std::find (begin (extra_clauses), end (extra_clauses), c) == end (extra_clauses)); #else (void) c; @@ -3071,12 +3075,12 @@ void Closure::check_not_tmp_binary_clause (Clause *c) { }; Clause *Closure::maybe_promote_tmp_binary_clause (Clause *c) { - assert (internal->lrat); - assert (internal->lrat_chain.empty ()); - assert (c->size == 2); + CADICAL_assert (internal->lrat); + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (c->size == 2); LOG (c, "promoting tmp"); -#ifndef NDEBUG - assert (std::find (begin (extra_clauses), end (extra_clauses), c) != +#ifndef CADICAL_NDEBUG + CADICAL_assert (std::find (begin (extra_clauses), end (extra_clauses), c) != end (extra_clauses)); #endif if (internal->val (c->literals[0]) || internal->val (c->literals[1])) @@ -3088,20 +3092,20 @@ Clause *Closure::maybe_promote_tmp_binary_clause (Clause *c) { }; Clause *Closure::add_tmp_binary_clause (int a, int b) { - assert (internal->clause.empty ()); - assert (internal->lrat_chain.empty ()); - assert (internal->lrat); + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (internal->lrat); LOG ("learning tmp binary clause %d %d", a, b); if (internal->unsat) return nullptr; if (a == -b) return nullptr; - assert (internal->clause.empty ()); + CADICAL_assert (internal->clause.empty ()); internal->clause.push_back (a); internal->clause.push_back (b); if (internal->lrat) { - assert (lrat_chain.size () >= 1); - assert (internal->lrat_chain.empty ()); + CADICAL_assert (lrat_chain.size () >= 1); + CADICAL_assert (internal->lrat_chain.empty ()); } LOG (lrat_chain, "chain"); Clause *res = new_tmp_clause (internal->clause); @@ -3109,8 +3113,8 @@ Clause *Closure::add_tmp_binary_clause (int a, int b) { if (internal->lrat) { lrat_chain.clear (); } - assert (internal->clause.empty ()); - assert (internal->lrat_chain.empty ()); + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); LOG (res, "promoted to"); return res; } @@ -3129,15 +3133,15 @@ Gate *Closure::find_remaining_and_gate (Clause *base_clause, int lhs) { const size_t arity = lits.size () - 1; size_t matched = 0; - assert (1 < arity); + CADICAL_assert (1 < arity); for (auto w : internal->watches (not_lhs)) { - assert (w.binary ()); + CADICAL_assert (w.binary ()); #ifdef LOGGING Clause *c = w.clause; LOG (c, "checking"); - assert (c->size == 2); - assert (c->literals[0] == not_lhs || c->literals[1] == not_lhs); + CADICAL_assert (c->size == 2); + CADICAL_assert (c->literals[0] == not_lhs || c->literals[1] == not_lhs); #endif const int other = w.blit; signed char &mark = marked (other); @@ -3150,7 +3154,7 @@ Gate *Closure::find_remaining_and_gate (Clause *base_clause, int lhs) { continue; } LOG ("marking %d mu4", other); - assert (!(mark & 4)); + CADICAL_assert (!(mark & 4)); mark |= 4; lrat_chain_and_gate.push_back (LitClausePair (other, w.clause)); if (internal->lrat) @@ -3159,8 +3163,8 @@ Gate *Closure::find_remaining_and_gate (Clause *base_clause, int lhs) { { auto q = begin (internal->analyzed); - assert (!internal->analyzed.empty ()); - assert (marked (not_lhs) == 3); + CADICAL_assert (!internal->analyzed.empty ()); + CADICAL_assert (marked (not_lhs) == 3); for (auto lit : internal->analyzed) { signed char &mark = marked (lit); if (lit == not_lhs) { @@ -3168,7 +3172,7 @@ Gate *Closure::find_remaining_and_gate (Clause *base_clause, int lhs) { continue; } - assert ((mark & 3) == 3); + CADICAL_assert ((mark & 3) == 3); if (mark & 4) { mark = 3; *q = lit; @@ -3179,8 +3183,8 @@ Gate *Closure::find_remaining_and_gate (Clause *base_clause, int lhs) { mark = 1; } } - assert (q != end (internal->analyzed)); - assert (marked (not_lhs) == 1); + CADICAL_assert (q != end (internal->analyzed)); + CADICAL_assert (marked (not_lhs) == 1); internal->analyzed.resize (q - begin (internal->analyzed)); LOG ("after filtering %zu LHS candidate remain", internal->analyzed.size ()); @@ -3219,8 +3223,8 @@ struct congruence_occurrences_larger { }; void Closure::extract_and_gates_with_base_clause (Clause *c) { - assert (!c->garbage); - assert (lrat_chain.empty ()); + CADICAL_assert (!c->garbage); + CADICAL_assert (lrat_chain.empty ()); LOG (c, "extracting and gates with clause"); unsigned size = 0; const unsigned arity_limit = @@ -3236,7 +3240,7 @@ void Closure::extract_and_gates_with_base_clause (Clause *c) { continue; } if (v > 0) { - assert (!internal->level); + CADICAL_assert (!internal->level); LOG (c, "found satisfied clause"); internal->mark_garbage (c); if (internal->lrat) @@ -3268,7 +3272,7 @@ void Closure::extract_and_gates_with_base_clause (Clause *c) { LOG (c, "is actually too small, thus skipping"); if (internal->lrat) lrat_chain.clear (); - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); return; } @@ -3300,9 +3304,9 @@ void Closure::extract_and_gates_with_base_clause (Clause *c) { } } const size_t reduced_size = clause_size - reduced; - assert (reduced_size); + CADICAL_assert (reduced_size); LOG (c, "trying as base arity %lu AND gate", arity); - assert (begin (lits) + reduced_size <= end (lits)); + CADICAL_assert (begin (lits) + reduced_size <= end (lits)); MSORT (internal->opts.radixsortlim, begin (lits), begin (lits) + reduced_size, congruence_occurrences_rank (internal), @@ -3311,7 +3315,7 @@ void Closure::extract_and_gates_with_base_clause (Clause *c) { unsigned extracted = 0; for (size_t i = 0; i < clause_size; ++i) { - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); if (internal->unsat) break; if (c->garbage) @@ -3322,16 +3326,16 @@ void Closure::extract_and_gates_with_base_clause (Clause *c) { if (first) { first = false; - assert (internal->analyzed.empty ()); + CADICAL_assert (internal->analyzed.empty ()); if (find_first_and_gate (c, lhs) != nullptr) { - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); ++extracted; } } else if (internal->analyzed.empty ()) { LOG ("early abort AND gate search"); break; } else if (find_remaining_and_gate (c, lhs)) { - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); ++extracted; } } @@ -3352,7 +3356,7 @@ void Closure::reset_and_gate_extraction () { } void Closure::extract_and_gates () { - assert (!full_watching); + CADICAL_assert (!full_watching); if (!internal->opts.congruenceand) return; START (extractands); @@ -3362,7 +3366,7 @@ void Closure::extract_and_gates () { 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 - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); Clause *c = internal->clauses[i]; if (c->garbage) continue; @@ -3373,7 +3377,7 @@ void Closure::extract_and_gates () { if (c->redundant) continue; extract_and_gates_with_base_clause (c); - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); } reset_and_gate_extraction (); @@ -3384,12 +3388,12 @@ void Closure::extract_and_gates () { // XOR gates uint64_t &Closure::new_largecounts (int lit) { - assert (internal->vlit (lit) < gnew_largecounts.size ()); + CADICAL_assert (internal->vlit (lit) < gnew_largecounts.size ()); return gnew_largecounts[internal->vlit (lit)]; } uint64_t &Closure::largecounts (int lit) { - assert (internal->vlit (lit) < glargecounts.size ()); + CADICAL_assert (internal->vlit (lit) < glargecounts.size ()); return glargecounts[internal->vlit (lit)]; } @@ -3410,11 +3414,11 @@ void inc_lits (vector<int> &lits) { } void Closure::check_ternary (int a, int b, int c) { - assert (internal->clause.empty ()); + CADICAL_assert (internal->clause.empty ()); if (internal->lrat) return; auto &clause = internal->clause; - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); clause.push_back (a); clause.push_back (b); clause.push_back (c); @@ -3429,11 +3433,11 @@ void Closure::check_ternary (int a, int b, int c) { } void Closure::check_binary_implied (int a, int b) { - assert (internal->clause.empty ()); + CADICAL_assert (internal->clause.empty ()); if (internal->lrat) return; auto &clause = internal->clause; - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); clause.push_back (a); clause.push_back (b); check_implied (); @@ -3447,8 +3451,8 @@ void Closure::check_implied () { } void Closure::add_xor_shrinking_proof_chain (Gate *g, int pivot) { - assert (internal->clause.empty ()); - assert (clause.empty ()); + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (clause.empty ()); if (!internal->proof) return; LOG (g, "starting XOR shrinking proof chain"); @@ -3468,10 +3472,10 @@ void Closure::add_xor_shrinking_proof_chain (Gate *g, int pivot) { clause.push_back (lit); const bool parity = (lhs > 0); - assert (parity == parity_lits (clause)); + CADICAL_assert (parity == parity_lits (clause)); const size_t size = clause.size (); const unsigned end = 1u << (size - 1); - assert (!internal->lrat || first.size () == 2 * end); + CADICAL_assert (!internal->lrat || first.size () == 2 * end); #ifdef LOGGING for (auto pair : first) { LOG (pair.clause, "key %d", pair.current_lit); @@ -3492,7 +3496,7 @@ void Closure::add_xor_shrinking_proof_chain (Gate *g, int pivot) { clause.pop_back (); } if (internal->lrat) { - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); lrat_chain.push_back (first[2 * i].clause->id); lrat_chain.push_back (first[2 * i + 1].clause->id); } @@ -3515,17 +3519,17 @@ void Closure::add_xor_shrinking_proof_chain (Gate *g, int pivot) { } void Closure::check_xor_gate_implied (Gate const *const g) { - assert (internal->clause.empty ()); - assert (g->tag == Gate_Type::XOr_Gate); + CADICAL_assert (internal->clause.empty ()); + CADICAL_assert (g->tag == Gate_Type::XOr_Gate); if (internal->lrat) { return; } const int lhs = g->lhs; LOG (g, "checking implied"); auto &clause = internal->clause; - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); for (auto other : g->rhs) { - assert (other > 0); + CADICAL_assert (other > 0); clause.push_back (other); } clause.push_back (-lhs); @@ -3548,14 +3552,14 @@ void Closure::check_xor_gate_implied (Gate const *const g) { } Gate *Closure::find_xor_lits (const vector<int> &rhs) { - assert (is_sorted (begin (rhs), end (rhs), + CADICAL_assert (is_sorted (begin (rhs), end (rhs), sort_literals_by_var_smaller (internal))); return find_gate_lits (rhs, Gate_Type::XOr_Gate); } Gate *Closure::find_xor_gate (Gate *g) { - assert (g->tag == Gate_Type::XOr_Gate); - assert (is_sorted (begin (g->rhs), end (g->rhs), + CADICAL_assert (g->tag == Gate_Type::XOr_Gate); + CADICAL_assert (is_sorted (begin (g->rhs), end (g->rhs), sort_literals_by_var_smaller (internal))); return find_gate_lits (g->rhs, Gate_Type::XOr_Gate); } @@ -3564,7 +3568,7 @@ void Closure::reset_xor_gate_extraction () { internal->clear_occs (); } bool Closure::normalize_ite_lits_gate (Gate *g) { auto &rhs = g->rhs; - assert (rhs.size () == 3); + CADICAL_assert (rhs.size () == 3); if (internal->lrat) check_correct_ite_flags (g); LOG (rhs, "RHS = "); @@ -3572,7 +3576,7 @@ bool Closure::normalize_ite_lits_gate (Gate *g) { rhs[0] = -rhs[0]; std::swap (rhs[1], rhs[2]); if (internal->lrat) { - assert (g->pos_lhs_ids.size () == 4); + 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; @@ -3588,8 +3592,8 @@ bool Closure::normalize_ite_lits_gate (Gate *g) { : Special_ITE_GATE::NORMAL) | (neg_else ? Special_ITE_GATE::NO_NEG_THEN : Special_ITE_GATE::NORMAL); - assert (g->pos_lhs_ids[0].current_lit == rhs[1]); - assert (g->pos_lhs_ids[2].current_lit == rhs[2]); + CADICAL_assert (g->pos_lhs_ids[0].current_lit == rhs[1]); + CADICAL_assert (g->pos_lhs_ids[2].current_lit == rhs[2]); if (internal->lrat) check_correct_ite_flags (g); } @@ -3602,7 +3606,7 @@ bool Closure::normalize_ite_lits_gate (Gate *g) { rhs[2] = -rhs[2]; LOG (rhs, "RHS = "); if (internal->lrat) { - assert (g->pos_lhs_ids.size () == 4); + 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; @@ -3618,8 +3622,8 @@ bool Closure::normalize_ite_lits_gate (Gate *g) { : Special_ITE_GATE::NORMAL) | (neg_else ? Special_ITE_GATE::NO_PLUS_ELSE : Special_ITE_GATE::NORMAL); - assert (g->pos_lhs_ids[0].current_lit == rhs[1]); - assert (g->pos_lhs_ids[2].current_lit == rhs[2]); + CADICAL_assert (g->pos_lhs_ids[0].current_lit == rhs[1]); + CADICAL_assert (g->pos_lhs_ids[2].current_lit == rhs[2]); // incorrect as we have not negated the LHS yet! // check_correct_ite_flags (g); } @@ -3628,10 +3632,10 @@ bool Closure::normalize_ite_lits_gate (Gate *g) { return true; } -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG bool is_tautological_ite_gate (Gate *g) { - assert (g->tag == Gate_Type::ITE_Gate); - assert (g->rhs.size () == 3); + CADICAL_assert (g->tag == Gate_Type::ITE_Gate); + CADICAL_assert (g->rhs.size () == 3); const int cond_lit = g->rhs[0]; const int then_lit = g->rhs[1]; const int else_lit = g->rhs[2]; @@ -3650,8 +3654,8 @@ LRAT_ID Closure::check_and_add_to_proof_chain (vector<int> &clause) { const LRAT_ID id = ++internal->clause_id; if (internal->proof) { if (internal->lrat) { - assert (internal->lrat_chain.empty ()); - assert (lrat_chain.size () >= 1); + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (lrat_chain.size () >= 1); } internal->proof->add_derived_clause (id, true, clause, lrat_chain); lrat_chain.clear (); @@ -3663,7 +3667,7 @@ void Closure::add_clause_to_chain (std::vector<int> unsimplified, LRAT_ID id) { const uint32_t id2_higher = (id >> 32); const uint32_t id2_lower = (uint32_t) (id & (LRAT_ID) (uint32_t) (-1)); - assert (id == ((LRAT_ID) id2_higher << 32) + (LRAT_ID) id2_lower); + CADICAL_assert (id == ((LRAT_ID) id2_higher << 32) + (LRAT_ID) id2_lower); chain.push_back (id2_higher); chain.push_back (id2_lower); LOG (unsimplified, "pushing to chain"); @@ -3674,10 +3678,10 @@ void Closure::add_clause_to_chain (std::vector<int> unsimplified, LRAT_ID Closure::simplify_and_add_to_proof_chain (vector<int> &unsimplified, LRAT_ID delete_id) { vector<int> &clause = internal->clause; - assert (clause.empty ()); -#ifndef NDEBUG + CADICAL_assert (clause.empty ()); +#ifndef CADICAL_NDEBUG for (auto lit : unsimplified) { - assert (!(marked (lit) & 4)); + CADICAL_assert (!(marked (lit) & 4)); } #endif @@ -3696,7 +3700,7 @@ LRAT_ID Closure::simplify_and_add_to_proof_chain (vector<int> &unsimplified, } for (auto lit : clause) { signed char &mark = marked (lit); - assert (mark & 4); + CADICAL_assert (mark & 4); mark &= ~4u; } @@ -3725,8 +3729,8 @@ void Closure::add_ite_turned_and_binary_clauses (Gate *g) { if (internal->lrat) return; LOG ("starting ITE turned AND supporting binary clauses"); - assert (unsimplified.empty ()); - assert (chain.empty ()); + CADICAL_assert (unsimplified.empty ()); + CADICAL_assert (chain.empty ()); int not_lhs = -g->lhs; unsimplified.push_back (not_lhs); unsimplified.push_back (g->rhs[0]); @@ -3738,18 +3742,18 @@ void Closure::add_ite_turned_and_binary_clauses (Gate *g) { } void Closure::simplify_unit_xor_lrat_clauses ( const vector<LitClausePair> &source, int lhs) { - assert (internal->lrat); + CADICAL_assert (internal->lrat); for (auto pair : source) { compute_rewritten_clause_lrat_simple (pair.clause, lhs); if (lrat_chain.size ()) break; } - assert (clause.size () == 1); + CADICAL_assert (clause.size () == 1); } void Closure::simplify_and_sort_xor_lrat_clauses ( const vector<LitClausePair> &source, vector<LitClausePair> &target, int lhs, int except2, bool flip) { - assert (internal->lrat); + CADICAL_assert (internal->lrat); for (auto pair : source) { Clause *c = produce_rewritten_clause_lrat (pair.clause, lhs); if (c) { @@ -3765,7 +3769,7 @@ void Closure::add_xor_matching_proof_chain ( return; if (!internal->proof) return; - assert (unsimplified.empty ()); + CADICAL_assert (unsimplified.empty ()); unsimplified = g->rhs; vector<LitClausePair> first; vector<LitClausePair> second; @@ -3804,21 +3808,21 @@ void Closure::add_xor_matching_proof_chain ( do { vector<LitIdPair> first_tmp; vector<LitIdPair> second_tmp; - assert (!unsimplified.empty ()); + CADICAL_assert (!unsimplified.empty ()); unsimplified.pop_back (); const size_t size = unsimplified.size (); - assert (size < 32); + CADICAL_assert (size < 32); const size_t off = 1u << size; for (size_t i = 0; i != off; ++i) { int32_t n = 0; if (internal->lrat) { n = number_from_xor_reason_reversed (unsimplified); - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); for (auto pair : first_ids) { if (pair.lit == n) lrat_chain.push_back (pair.id); } - assert (lrat_chain.size () == 2); + CADICAL_assert (lrat_chain.size () == 2); } unsimplified.push_back (-lhs1); unsimplified.push_back (lhs2); @@ -3833,7 +3837,7 @@ void Closure::add_xor_matching_proof_chain ( if (pair.lit == n) lrat_chain.push_back (pair.id); } - assert (lrat_chain.size () == 2); + CADICAL_assert (lrat_chain.size () == 2); } unsimplified.push_back (lhs1); unsimplified.push_back (-lhs2); @@ -3852,15 +3856,15 @@ void Closure::add_xor_matching_proof_chain ( } } while (!unsimplified.empty ()); if (internal->lrat) { - assert (first_ids.size () == 1); - assert (second_ids.size () == 1); + CADICAL_assert (first_ids.size () == 1); + CADICAL_assert (second_ids.size () == 1); to_lrat.push_back (first_ids.back ().id); back_lrat.push_back (second_ids.back ().id); } - assert (!internal->lrat || to_lrat.size () == 1); - assert (!internal->lrat || back_lrat.size () == 1); + CADICAL_assert (!internal->lrat || to_lrat.size () == 1); + CADICAL_assert (!internal->lrat || back_lrat.size () == 1); LOG ("finished XOR matching proof"); - assert (unsimplified.empty ()); + CADICAL_assert (unsimplified.empty ()); } // this function needs to either put the clauses from @@ -3872,11 +3876,11 @@ Gate *Closure::new_xor_gate (const vector<LitClausePair> &glauses, for (auto lit : lits) { if (lhs != lit && -lhs != lit) { - assert (lit > 0); + CADICAL_assert (lit > 0); rhs.push_back (lit); } } - assert (rhs.size () + 1 == lits.size ()); + CADICAL_assert (rhs.size () + 1 == lits.size ()); sort_literals_by_var (rhs); Gate *g = find_xor_lits (this->rhs); if (g) { @@ -3889,7 +3893,7 @@ Gate *Closure::new_xor_gate (const vector<LitClausePair> &glauses, ++internal->stats.congruence.xors; } delete_proof_chain (); - assert (internal->unsat || chain.empty ()); + CADICAL_assert (internal->unsat || chain.empty ()); } else { g = new Gate; g->lhs = lhs; @@ -3917,9 +3921,9 @@ Gate *Closure::new_xor_gate (const vector<LitClausePair> &glauses, uint32_t Closure::number_from_xor_reason_reversed (const std::vector<int> &rhs) { uint32_t n = 0; - assert (is_sorted (rhs.rbegin (), rhs.rend (), + CADICAL_assert (is_sorted (rhs.rbegin (), rhs.rend (), sort_literals_by_var_smaller_except (internal, 0, 0))); - assert (rhs.size () <= 32); + CADICAL_assert (rhs.size () <= 32); for (auto r = rhs.rbegin (); r != rhs.rend (); r++) { int lit = *r; n *= 2; @@ -3931,11 +3935,11 @@ Closure::number_from_xor_reason_reversed (const std::vector<int> &rhs) { uint32_t Closure::number_from_xor_reason (const std::vector<int> &rhs, int lhs, int except, bool flip) { uint32_t n = 0; - assert (is_sorted ( + CADICAL_assert (is_sorted ( begin (rhs), end (rhs), sort_literals_by_var_smaller_except (internal, lhs, except))); (void) lhs, (void) except; - assert (rhs.size () <= 32); + CADICAL_assert (rhs.size () <= 32); for (auto lit : rhs) { n *= 2; n += !(lit > 0) ^ flip; @@ -3948,7 +3952,7 @@ uint32_t Closure::number_from_xor_reason (const std::vector<int> &rhs, // Look at this first void Closure::gate_sort_lrat_reasons (LitClausePair &litId, int lhs, int except2, bool flip) { - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); std::copy (begin (*litId.clause), end (*litId.clause), back_inserter (clause)); sort_literals_by_var_except (clause, lhs, except2); @@ -3965,24 +3969,24 @@ struct smaller_pair_first_rank { // Look at this first void Closure::gate_sort_lrat_reasons (std::vector<LitClausePair> &xs, int lhs, int except2, bool flip) { - assert (clause.empty ()); - assert (!xs.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (!xs.empty ()); for (auto &litId : xs) { gate_sort_lrat_reasons (litId, lhs, except2, flip); } rsort (begin (xs), end (xs), smaller_pair_first_rank ()); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG std::for_each (begin (xs), end (xs), [&xs] (const LitClausePair &x) { - assert (x.clause->size == xs[1].clause->size); + CADICAL_assert (x.clause->size == xs[1].clause->size); }); #endif } void Closure::init_xor_gate_extraction (std::vector<Clause *> &candidates) { const unsigned arity_limit = internal->opts.congruencexorarity; - assert (arity_limit < 32); // we use unsigned int. + CADICAL_assert (arity_limit < 32); // we use unsigned int. const unsigned size_limit = arity_limit + 1; glargecounts.resize (2 * internal->vsize, 0); @@ -4043,8 +4047,8 @@ void Closure::init_xor_gate_extraction (std::vector<Clause *> &candidates) { if (!internal->val (lit)) ++size; } - assert (3 <= size); - assert (size <= size_limit); + CADICAL_assert (3 <= size); + CADICAL_assert (size <= size_limit); const unsigned arity = size - 1; const unsigned needed_clauses = 1u << (arity - 1); for (auto lit : *c) { @@ -4082,31 +4086,31 @@ Clause *Closure::find_large_xor_side_clause (std::vector<int> &lits) { unsigned least_occurring_literal = 0; unsigned count_least_occurring = UINT_MAX; const size_t size_lits = lits.size (); -#if defined(LOGGING) || !defined(NDEBUG) +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) const unsigned arity = size_lits - 1; #endif -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG const unsigned count_limit = 1u << (arity - 1); #endif LOG (lits, "trying to find arity %u XOR side clause", arity); for (auto lit : lits) { - assert (!internal->val (lit)); + CADICAL_assert (!internal->val (lit)); marked (lit) = 1; unsigned count = largecount (lit); - assert (count_limit <= count); + CADICAL_assert (count_limit <= count); if (count >= count_least_occurring) continue; count_least_occurring = count; least_occurring_literal = lit; } Clause *res = 0; - assert (least_occurring_literal); + CADICAL_assert (least_occurring_literal); LOG ("searching XOR side clause watched by %d#%u", least_occurring_literal, count_least_occurring); LOG ("searching for size %ld", size_lits); for (auto c : internal->occs (least_occurring_literal)) { LOG (c, "checking"); - assert (c->size != 2); // TODO kissat has break + CADICAL_assert (c->size != 2); // TODO kissat has break if (c->garbage) continue; if ((size_t) c->size < size_lits) @@ -4119,7 +4123,7 @@ Clause *Closure::find_large_xor_side_clause (std::vector<int> &lits) { if (value > 0) { LOG (c, "found satisfied %d in", other); internal->mark_garbage (c); - assert (c->garbage); + CADICAL_assert (c->garbage); break; } if (marked (other)) @@ -4172,8 +4176,8 @@ void Closure::extract_xor_gates_with_base_clause (Clause *c) { largest = smallest = lit; first = false; } else { - assert (smallest); - assert (largest); + CADICAL_assert (smallest); + CADICAL_assert (largest); if (internal->vlit (lit) < internal->vlit (smallest)) { LOG ("new smallest %d", lit); smallest = lit; @@ -4198,7 +4202,7 @@ void Closure::extract_xor_gates_with_base_clause (Clause *c) { lits.push_back (lit); ++size; } - assert (size == lits.size ()); + CADICAL_assert (size == lits.size ()); if (size < 3) { LOG (c, "short XOR base clause"); return; @@ -4221,9 +4225,9 @@ void Closure::extract_xor_gates_with_base_clause (Clause *c) { } LOG ("checking for XOR side clauses"); - assert (smallest && largest); + CADICAL_assert (smallest && largest); const unsigned end = 1u << arity; - assert (negated == parity_lits (lits)); + CADICAL_assert (negated == parity_lits (lits)); unsigned found = 0; vector<LitClausePair> glauses; glauses.push_back (LitClausePair (0, c)); @@ -4236,10 +4240,10 @@ void Closure::extract_xor_gates_with_base_clause (Clause *c) { Clause *d = find_large_xor_side_clause (lits); if (!d) return; - assert (!d->redundant); + CADICAL_assert (!d->redundant); glauses.push_back (LitClausePair (i, d)); } else - assert (!c->redundant); + CADICAL_assert (!c->redundant); inc_lits (lits); ++found; } @@ -4247,7 +4251,7 @@ void Closure::extract_xor_gates_with_base_clause (Clause *c) { while (parity_lits (lits) != negated) inc_lits (lits); LOG (lits, "found all needed %u matching clauses:", found); - assert (found == 1u << arity); + CADICAL_assert (found == 1u << arity); if (negated) { auto p = begin (lits); int lit; @@ -4271,7 +4275,7 @@ void Closure::extract_xor_gates_with_base_clause (Clause *c) { LOG ("no arity %u XOR gate extracted", arity); } void Closure::extract_xor_gates () { - assert (!full_watching); + CADICAL_assert (!full_watching); if (!internal->opts.congruencexor) return; START (extractxors); @@ -4325,13 +4329,13 @@ void Closure::find_units () { } unmark_all (); } - assert (internal->analyzed.empty ()); + CADICAL_assert (internal->analyzed.empty ()); } LOG ("found %zd units", units); } void Closure::find_equivalences () { - assert (!internal->unsat); + CADICAL_assert (!internal->unsat); for (auto v : internal->vars) { RESTART: @@ -4341,7 +4345,7 @@ void Closure::find_equivalences () { for (auto w : internal->watches (lit)) { if (!w.binary ()) break; - assert (w.size == 2); + CADICAL_assert (w.size == 2); const int other = w.blit; if (internal->vlit (lit) > internal->vlit (other)) continue; @@ -4361,7 +4365,7 @@ void Closure::find_equivalences () { const int other = w.blit; if (internal->vlit (-lit) > internal->vlit (other)) continue; - assert (-lit != other); + CADICAL_assert (-lit != other); LOG ("binary clause %d %d", -lit, other); if (marked (-other)) { int lit_repr = find_representative (lit); @@ -4377,7 +4381,7 @@ void Closure::find_equivalences () { // // therefore you actually need to learn the clauses 2->3->4 // and -2->1 and vice-versa eager_representative_id (other) = // marked_mu1 (-other).clause->id; eager_representative_id - // (-other) = w.clause->id; assert (eager_representative_id + // (-other) = w.clause->id; CADICAL_assert (eager_representative_id // (other) != -1); LOG ("lrat: %d (%zd) %d (%zd)", other, // eager_representative_id (other), -other, // eager_representative_id (-other)); @@ -4402,7 +4406,7 @@ void Closure::find_equivalences () { } unmark_all (); } - assert (internal->analyzed.empty ()); + CADICAL_assert (internal->analyzed.empty ()); LOG ("found %zd equivalences", schedule.size ()); } @@ -4421,9 +4425,9 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, simplify_and_gate (g); return; } - assert (src); - assert (dst); - assert (internal->val (src) == internal->val (dst)); + CADICAL_assert (src); + CADICAL_assert (dst); + CADICAL_assert (internal->val (src) == internal->val (dst)); GatesTable::iterator git = (g->indexed ? table.find (g) : end (table)); LOG (g, "rewriting %d into %d in", src, dst); int clashing = 0, falsifies = 0; @@ -4460,12 +4464,12 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, } if (lit == -dst) { if (dst_count) { - assert (!not_dst_count); + CADICAL_assert (!not_dst_count); LOG ("clashing literals %d and %d", (dst), (-dst)); clashing = -dst; break; } - assert (!not_dst_count); + CADICAL_assert (!not_dst_count); ++not_dst_count; } *q++ = lit; @@ -4482,7 +4486,7 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, // that we can produce an LRAT chain size_t i = 0, size = g->pos_lhs_ids.size (); bool found = false; - assert (!falsifies || !clashing); + CADICAL_assert (!falsifies || !clashing); const int orig_falsifies = falsifies == dst ? src : falsifies; const int orig_clashing = clashing == -dst ? -src : (clashing == dst ? src : clashing); @@ -4522,7 +4526,7 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, ++i; } LOG ("resizing to %zd", i); - assert (i); + CADICAL_assert (i); g->pos_lhs_ids.resize (i); } @@ -4530,13 +4534,13 @@ void Closure::rewrite_and_gate (Gate *g, int dst, int src, LRAT_ID id1, g->rhs.resize (q - begin (g->rhs)); g->shrunken = true; } - assert (dst_count <= 2); - assert (not_dst_count <= 1); + CADICAL_assert (dst_count <= 2); + CADICAL_assert (not_dst_count <= 1); std::vector<LRAT_ID> reasons_lrat_src, reasons_lrat_usrc; shrink_and_gate (g, falsifies, clashing); LOG (g, "rewritten as"); - assert (!internal->lrat || !g->pos_lhs_ids.empty ()); + CADICAL_assert (!internal->lrat || !g->pos_lhs_ids.empty ()); // check_and_gate_implied (g); update_and_gate (g, git, src, dst, id1, id2, falsifies, clashing); ++internal->stats.congruence.rewritten_ands; @@ -4555,17 +4559,17 @@ bool Closure::rewrite_gate (Gate *g, int dst, int src, LRAT_ID id1, rewrite_ite_gate (g, dst, src); break; default: - assert (false); + CADICAL_assert (false); break; } - assert (internal->unsat || lrat_chain.empty ()); + CADICAL_assert (internal->unsat || lrat_chain.empty ()); return !internal->unsat; } bool Closure::rewrite_gates (int dst, int src, LRAT_ID id1, LRAT_ID id2) { const auto &occs = goccs (src); for (auto g : occs) { - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); if (!rewrite_gate (g, dst, src, id1, id2)) return false; else if (!g->garbage && gate_contains (g, dst)) @@ -4573,15 +4577,15 @@ bool Closure::rewrite_gates (int dst, int src, LRAT_ID id1, LRAT_ID id2) { } goccs (src).clear (); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG for (const auto &occs : gtab) { for (auto g : occs) { - assert (g); - assert (g->garbage || !gate_contains (g, src)); + CADICAL_assert (g); + CADICAL_assert (g->garbage || !gate_contains (g, src)); } } #endif - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); return true; } @@ -4610,7 +4614,7 @@ void Closure::rewrite_xor_gate (Gate *g, int dst, int src) { const size_t size = g->rhs.size (); for (size_t i = 0; i < size; ++i) { int lit = g->rhs[i]; - assert (lit > 0); + CADICAL_assert (lit > 0); if (lit == src) lit = dst; const signed char v = internal->val (lit); @@ -4628,7 +4632,7 @@ void Closure::rewrite_xor_gate (Gate *g, int dst, int src) { LOG ("flipping LHS %d", g->lhs); g->lhs = -g->lhs; } - assert (dst_count <= 2); + CADICAL_assert (dst_count <= 2); if (dst_count == 2) { LOG ("destination found twice, removing"); size_t k = 0; @@ -4637,10 +4641,10 @@ void Closure::rewrite_xor_gate (Gate *g, int dst, int src) { if (lit != dst) g->rhs[k++] = g->rhs[i]; } - assert (k == j - 2); + CADICAL_assert (k == j - 2); g->rhs.resize (k); g->shrunken = true; - assert (is_sorted (begin (g->rhs), end (g->rhs), + 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) { @@ -4651,17 +4655,17 @@ void Closure::rewrite_xor_gate (Gate *g, int dst, int src) { nonces, g->rhs); // all but one (the dst) is sorted correctly actually } else { - assert (j == size); + CADICAL_assert (j == size); sort_literals_by_var (g->rhs); } - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); // LRAT for add_xor_shrinking_proof_chain // this should be unnecessary... // TODO check if really unnecessary if (dst_count > 1) add_xor_shrinking_proof_chain (g, dst); - assert (internal->clause.size () <= 1); + CADICAL_assert (internal->clause.size () <= 1); update_xor_gate (g, git); if (!g->garbage && !internal->unsat && original_dst_negated && @@ -4685,7 +4689,7 @@ void Closure::simplify_xor_gate (Gate *g) { size_t j = 0; for (size_t i = 0; i < size; ++i) { int lit = g->rhs[i]; - assert (lit > 0); + CADICAL_assert (lit > 0); const signed char v = internal->val (lit); if (v > 0) negate ^= 1; @@ -4701,15 +4705,15 @@ void Closure::simplify_xor_gate (Gate *g) { LOG ("shrunken gate"); g->shrunken = true; g->rhs.resize (j); - assert (is_sorted (begin (g->rhs), end (g->rhs), + CADICAL_assert (is_sorted (begin (g->rhs), end (g->rhs), sort_literals_by_var_smaller (internal))); g->hash = hash_lits (nonces, g->rhs); } else { - assert (g->hash == hash_lits (nonces, g->rhs)); + CADICAL_assert (g->hash == hash_lits (nonces, g->rhs)); } check_xor_gate_implied (g); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); update_xor_gate (g, git); LOG (g, "simplified"); check_xor_gate_implied (g); @@ -4725,7 +4729,7 @@ void Closure::schedule_literal (int lit) { return; scheduled[idx] = true; schedule.push (lit); - assert (lit != find_representative (lit)); + CADICAL_assert (lit != find_representative (lit)); LOG ("scheduled literal %d", lit); } @@ -4757,7 +4761,7 @@ bool Closure::propagate_equivalence (int lit) { const int repr = find_eager_representative_and_compress (lit); const LRAT_ID id1 = find_eager_representative_lrat (lit); const LRAT_ID id2 = find_eager_representative_lrat (-lit); - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); return rewrite_gates (repr, lit, id1, id2) && rewrite_gates (-repr, -lit, id2, id1); } @@ -4766,10 +4770,10 @@ size_t Closure::propagate_units_and_equivalences () { START (congruencemerge); size_t propagated = 0; LOG ("propagating at least %zd units", schedule.size ()); - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); while (propagate_units () && !schedule.empty ()) { - assert (!internal->unsat); - assert (lrat_chain.empty ()); + CADICAL_assert (!internal->unsat); + CADICAL_assert (lrat_chain.empty ()); ++propagated; int lit = schedule.front (); schedule.pop (); @@ -4778,28 +4782,28 @@ size_t Closure::propagate_units_and_equivalences () { break; } - assert (internal->unsat || schedule.empty ()); - assert (internal->unsat || lrat_chain.empty ()); + CADICAL_assert (internal->unsat || schedule.empty ()); + CADICAL_assert (internal->unsat || lrat_chain.empty ()); LOG ("propagated %zu congruence units", units); LOG ("propagated %zu congruence equivalences", propagated); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG if (!internal->unsat) { for (const auto &occs : gtab) { for (auto g : occs) { if (g->garbage) continue; - assert (g->tag == Gate_Type::ITE_Gate || + 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! - // assert (g->tag != Gate_Type::ITE_Gate || (g->rhs.size() == 3 + // CADICAL_assert (g->tag != Gate_Type::ITE_Gate || (g->rhs.size() == 3 // && g->rhs[1] != -g->lhs && g->rhs[2] != -g->lhs)); - // assert (table.count(g) == 1); + // CADICAL_assert (table.count(g) == 1); for (auto lit : g->rhs) { - assert (!internal->val (lit)); - assert (representative (lit) == lit); + CADICAL_assert (!internal->val (lit)); + CADICAL_assert (representative (lit) == lit); } } } @@ -4807,7 +4811,7 @@ size_t Closure::propagate_units_and_equivalences () { if (g->garbage) continue; if (g->tag == Gate_Type::And_Gate) { - // assert (find_and_lits(g->arity, g->rhs)); + // CADICAL_assert (find_and_lits(g->arity, g->rhs)); } } } @@ -4832,7 +4836,7 @@ std::string string_of_gate (Gate_Type t) { void Closure::reset_closure () { scheduled.clear (); for (Gate *g : table) { - assert (g->indexed); + CADICAL_assert (g->indexed); LOG (g, "deleting"); if (!g->garbage) delete g; @@ -4849,15 +4853,15 @@ void Closure::reset_closure () { garbage.clear (); if (internal->lrat) { - assert (internal->proof); + CADICAL_assert (internal->proof); for (auto c : extra_clauses) { - assert (!c->garbage); + CADICAL_assert (!c->garbage); internal->proof->delete_clause (c); delete c; } extra_clauses.clear (); } else { - assert (extra_clauses.empty ()); + CADICAL_assert (extra_clauses.empty ()); } } @@ -4942,7 +4946,7 @@ void Closure::forward_subsume_matching_clauses () { continue; if (c->size == 2) continue; - assert (analyzed.empty ()); + CADICAL_assert (analyzed.empty ()); bool contains_matchable = false; for (auto lit : *c) { const signed char v = internal->val (lit); @@ -4960,7 +4964,7 @@ void Closure::forward_subsume_matching_clauses () { } const int repr = find_representative (lit); - assert (!internal->val (repr)); + CADICAL_assert (!internal->val (repr)); if (marked (repr)) continue; const int not_repr = -repr; @@ -4990,7 +4994,7 @@ void Closure::forward_subsume_matching_clauses () { size_t tried = 0, subsumed = 0; internal->init_occs (); for (auto c : candidates) { - assert (c->size != 2); + CADICAL_assert (c->size != 2); // TODO if terminated ++tried; if (find_subsuming_clause (c)) { @@ -5008,11 +5012,11 @@ void Closure::forward_subsume_matching_clauses () { // from subsume void Closure::subsume_clause (Clause *subsuming, Clause *subsumed) { - // assert (!subsuming->redundant); - // assert (!subsumed->redundant); + // CADICAL_assert (!subsuming->redundant); + // CADICAL_assert (!subsumed->redundant); auto &stats = internal->stats; stats.subsumed++; - assert (subsuming->size <= subsumed->size); + CADICAL_assert (subsuming->size <= subsumed->size); LOG (subsumed, "subsumed"); if (subsumed->redundant) stats.subred++; @@ -5030,26 +5034,26 @@ void Closure::subsume_clause (Clause *subsuming, Clause *subsumed) { stats.current.irredundant++; stats.added.irredundant++; stats.irrlits += subsuming->size; - assert (stats.current.redundant > 0); + CADICAL_assert (stats.current.redundant > 0); stats.current.redundant--; - assert (stats.added.redundant > 0); + CADICAL_assert (stats.added.redundant > 0); stats.added.redundant--; // ... and keep 'stats.added.total'. } bool Closure::find_subsuming_clause (Clause *subsumed) { - assert (!subsumed->garbage); + CADICAL_assert (!subsumed->garbage); Clause *subsuming = nullptr; for (auto lit : *subsumed) { - assert (internal->val (lit) <= 0); + CADICAL_assert (internal->val (lit) <= 0); const int repr_lit = find_representative (lit); const signed char repr_val = internal->val (repr_lit); - assert (repr_val <= 0); + CADICAL_assert (repr_val <= 0); if (repr_val < 0) continue; if (marked (repr_lit)) continue; - assert (!marked (-repr_lit)); + CADICAL_assert (!marked (-repr_lit)); marked (repr_lit) = 1; } int least_occuring_lit = 0; @@ -5059,21 +5063,21 @@ bool Closure::find_subsuming_clause (Clause *subsumed) { for (auto lit : *subsumed) { const int repr_lit = find_representative (lit); const size_t count = internal->occs (lit).size (); - assert (count <= UINT_MAX); + 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)) { - assert (!d->garbage); - assert (subsumed != d); + CADICAL_assert (!d->garbage); + CADICAL_assert (subsumed != d); if (!subsumed->redundant && d->redundant) continue; for (auto other : *d) { const signed char v = internal->val (other); if (v < 0) continue; - assert (!v); + CADICAL_assert (!v); const int repr_other = find_representative (other); if (!marked (repr_other)) goto CONTINUE_WITH_NEXT_CLAUSE; @@ -5085,7 +5089,7 @@ bool Closure::find_subsuming_clause (Clause *subsumed) { CONTINUE_WITH_NEXT_CLAUSE:; } } - assert (least_occuring_lit); + CADICAL_assert (least_occuring_lit); FOUND_SUBSUMING: for (auto lit : *subsumed) { @@ -5108,7 +5112,7 @@ FOUND_SUBSUMING: /*------------------------------------------------------------------------*/ static bool skip_ite_gate (Gate *g) { - assert (g->tag == Gate_Type::ITE_Gate); + CADICAL_assert (g->tag == Gate_Type::ITE_Gate); if (g->garbage) return true; return false; @@ -5117,12 +5121,12 @@ static bool skip_ite_gate (Gate *g) { void Closure::produce_ite_merge_then_else_reasons ( Gate *g, int src, int dst, std::vector<LRAT_ID> &reasons_implication, std::vector<LRAT_ID> &reasons_back) { - assert (!g->garbage); + CADICAL_assert (!g->garbage); if (!internal->lrat) return; check_correct_ite_flags (g); // no merge is happening actually - assert (g->rhs[1] == find_eager_representative(g->rhs[1]) || g->rhs[2] == find_eager_representative(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) || @@ -5131,12 +5135,12 @@ void Closure::produce_ite_merge_then_else_reasons ( (g->rhs[2] == -src && g->lhs == -dst && g->rhs[1] == g->lhs)) return; check_ite_lrat_reasons (g, false); - assert (g->rhs.size () == 3); - assert (src == g->rhs[1] || src == g->rhs[2]); - assert (dst == g->rhs[1] || dst == g->rhs[2]); + CADICAL_assert (g->rhs.size () == 3); + CADICAL_assert (src == g->rhs[1] || src == g->rhs[2]); + CADICAL_assert (dst == g->rhs[1] || dst == g->rhs[2]); const int8_t flag = g->degenerated_ite; - assert (!ite_flags_no_then_clauses (flag)); // e = lhs: already merged - assert (!ite_flags_no_else_clauses (flag)); // t = lhs: already merged + 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"); @@ -5148,8 +5152,8 @@ void Closure::produce_ite_merge_then_else_reasons ( } if (ite_flags_cond_lhs (flag)) { LOG ("degenerated case with lhs = cond"); - assert (g->pos_lhs_ids[0].clause); - assert (g->pos_lhs_ids[3].clause); + CADICAL_assert (g->pos_lhs_ids[0].clause); + CADICAL_assert (g->pos_lhs_ids[3].clause); reasons_back.push_back (g->pos_lhs_ids[3].clause->id); reasons_implication.push_back (g->pos_lhs_ids[0].clause->id); return; @@ -5166,7 +5170,7 @@ 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) { - assert (litId.clause); + CADICAL_assert (litId.clause); if (litId.current_lit == src) litId.current_lit = dst; if (litId.current_lit == -src) @@ -5178,8 +5182,8 @@ void Closure::rewrite_ite_gate_update_lrat_reasons (Gate *g, int src, 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) { - assert (internal->lrat_chain.empty ()); - assert (!g->garbage); + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (!g->garbage); LOG (g, "rewriting to proper AND"); if (internal->val (g->lhs) > 0) { { @@ -5204,7 +5208,7 @@ bool Closure::rewrite_ite_gate_to_and ( if (!internal->unsat) { const int lit = g->rhs[1]; const char v = internal->val (lit); - assert (dst == g->rhs[0] || dst == g->rhs[1] || -dst == g->rhs[0] || + CADICAL_assert (dst == g->rhs[0] || dst == g->rhs[1] || -dst == g->rhs[0] || -dst == g->rhs[1]); const int other = (dst == g->rhs[0] || dst == g->rhs[1]) ? dst ^ g->rhs[0] ^ g->rhs[1] @@ -5235,10 +5239,10 @@ bool Closure::rewrite_ite_gate_to_and ( 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); - assert (g->rhs.size () == 3); - assert (g->pos_lhs_ids.size () == 4); - assert (idx1 < g->pos_lhs_ids.size ()); - assert (idx2 < g->pos_lhs_ids.size ()); + CADICAL_assert (g->rhs.size () == 3); + CADICAL_assert (g->pos_lhs_ids.size () == 4); + CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); + CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); int lit = g->pos_lhs_ids[idx2].current_lit, other = g->lhs; // TODO: remove argument (void) src; @@ -5250,59 +5254,59 @@ bool Closure::rewrite_ite_gate_to_and ( return false; } - assert (idx1 != (size_t) -1); - assert (idx2 != (size_t) -1); - assert (idx1 < g->pos_lhs_ids.size ()); - assert (idx2 < g->pos_lhs_ids.size ()); + CADICAL_assert (idx1 != (size_t) -1); + CADICAL_assert (idx2 != (size_t) -1); + CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); + CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); Clause *c = g->pos_lhs_ids[idx1].clause; - assert (c->size == 2); + CADICAL_assert (c->size == 2); Clause *d = g->pos_lhs_ids[idx2].clause; - assert (c != d); - assert (c); - assert (d); + CADICAL_assert (c != d); + CADICAL_assert (c); + CADICAL_assert (d); g->pos_lhs_ids.erase (std::remove_if (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), [d] (const LitClausePair &p) { return p.clause == d || !p.clause; }), end (g->pos_lhs_ids)); - assert (g->pos_lhs_ids.size () == 2); - assert (lit); - assert (other); - assert (lit != dst); - assert (other != dst); - assert (lit != other); + CADICAL_assert (g->pos_lhs_ids.size () == 2); + CADICAL_assert (lit); + CADICAL_assert (other); + CADICAL_assert (lit != dst); + CADICAL_assert (other != dst); + CADICAL_assert (lit != other); lrat_chain.push_back (c->id); lrat_chain.push_back (d->id); Clause *e = learn_binary_tmp_or_full_clause (lit, -other); - assert (e); + CADICAL_assert (e); auto long_clause = std::find_if (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), [] (LitClausePair l) { return l.clause->size == 3; }); - assert (long_clause != end (g->pos_lhs_ids)); + CADICAL_assert (long_clause != end (g->pos_lhs_ids)); LOG (long_clause->clause, "new long clause"); g->neg_lhs_ids.push_back (*long_clause); g->pos_lhs_ids.erase (long_clause); - assert (g->pos_lhs_ids.size () == 1); + CADICAL_assert (g->pos_lhs_ids.size () == 1); (void) maybe_promote_tmp_binary_clause (g->pos_lhs_ids[0].clause); g->pos_lhs_ids.push_back ({lit, e}); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG for (auto litId : g->pos_lhs_ids) { bool found = false; - assert (litId.clause); + CADICAL_assert (litId.clause); for (auto other : *litId.clause) { found = (find_eager_representative (other) == litId.current_lit); if (found) break; } - assert (found); + CADICAL_assert (found); } for (auto id : g->pos_lhs_ids) { LOG (id.clause, "clause after rewriting:"); - assert (id.clause->size == 2); + CADICAL_assert (id.clause->size == 2); } #endif @@ -5332,13 +5336,13 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( LOG ("cond: %d, merging %d and rewriting to %d", cond_lit, lit_to_merge, other_lit); if (internal->lrat) { - assert (internal->lrat); - assert (g->pos_lhs_ids.size () == 4); + CADICAL_assert (internal->lrat); + CADICAL_assert (g->pos_lhs_ids.size () == 4); if (repr_lhs == -repr_other_lit) { LOG ("special case: %s=%s, checking if other: %s %s", LOGLIT (g->lhs), LOGLIT (-lit_to_merge), LOGLIT (cond_lit), LOGLIT (other_lit)); - 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"); @@ -5353,7 +5357,7 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( reasons_implication.push_back (id_unit); g->pos_lhs_ids[3].clause = produce_rewritten_clause_lrat ( g->pos_lhs_ids[3].clause, g->lhs, false); - assert (g->pos_lhs_ids[3].clause); + CADICAL_assert (g->pos_lhs_ids[3].clause); reasons_implication.push_back (g->pos_lhs_ids[3].clause->id); unsimplified.clear (); return; @@ -5371,7 +5375,7 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( reasons_implication.push_back (id_unit); g->pos_lhs_ids[0].clause = produce_rewritten_clause_lrat ( g->pos_lhs_ids[0].clause, g->lhs, false); - assert (g->pos_lhs_ids[0].clause); + CADICAL_assert (g->pos_lhs_ids[0].clause); reasons_implication.push_back (g->pos_lhs_ids[0].clause->id); unsimplified.clear (); return; @@ -5394,7 +5398,7 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( reasons_unit = {id_unit}; g->pos_lhs_ids[1].clause = produce_rewritten_clause_lrat ( g->pos_lhs_ids[1].clause, g->lhs, false); - assert (g->pos_lhs_ids[1].clause); + CADICAL_assert (g->pos_lhs_ids[1].clause); // don't bother finding out which one is used reasons_implication.push_back (id_unit); @@ -5412,7 +5416,7 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( reasons_unit = {id_unit}; g->pos_lhs_ids[2].clause = produce_rewritten_clause_lrat ( g->pos_lhs_ids[2].clause, g->lhs, false); - assert (g->pos_lhs_ids[2].clause); + CADICAL_assert (g->pos_lhs_ids[2].clause); reasons_implication.push_back (id_unit); reasons_implication.push_back (g->pos_lhs_ids[2].clause->id); @@ -5426,8 +5430,8 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( g->pos_lhs_ids[idx1].clause, g->lhs, false); g->pos_lhs_ids[idx2].clause = produce_rewritten_clause_lrat ( g->pos_lhs_ids[idx2].clause, g->lhs, false); - assert (g->pos_lhs_ids[idx1].clause); - assert (g->pos_lhs_ids[idx2].clause); + CADICAL_assert (g->pos_lhs_ids[idx1].clause); + CADICAL_assert (g->pos_lhs_ids[idx2].clause); lrat_chain.push_back (g->pos_lhs_ids[idx1].clause->id); lrat_chain.push_back (g->pos_lhs_ids[idx2].clause->id); unsimplified.push_back (-cond_lit); @@ -5444,8 +5448,8 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( g->pos_lhs_ids[idx1].clause, g->lhs, false); g->pos_lhs_ids[idx2].clause = produce_rewritten_clause_lrat ( g->pos_lhs_ids[idx2].clause, g->lhs, false); - assert (g->pos_lhs_ids[idx1].clause); - assert (g->pos_lhs_ids[idx2].clause); + CADICAL_assert (g->pos_lhs_ids[idx1].clause); + CADICAL_assert (g->pos_lhs_ids[idx2].clause); lrat_chain.push_back (g->pos_lhs_ids[idx1].clause->id); lrat_chain.push_back (g->pos_lhs_ids[idx2].clause->id); unsimplified.push_back (cond_lit); @@ -5463,23 +5467,23 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( g->pos_lhs_ids[idx1].clause, g->lhs, false); g->pos_lhs_ids[idx2].clause = produce_rewritten_clause_lrat ( g->pos_lhs_ids[idx2].clause, g->lhs, false); - assert (g->pos_lhs_ids[idx1].clause); - assert (g->pos_lhs_ids[idx2].clause); + CADICAL_assert (g->pos_lhs_ids[idx1].clause); + CADICAL_assert (g->pos_lhs_ids[idx2].clause); reasons_unit.push_back (g->pos_lhs_ids[idx1].clause->id); reasons_unit.push_back (g->pos_lhs_ids[idx2].clause->id); return; } // if (other_lit == -g->lhs) { - // assert (false); + // CADICAL_assert (false); // } // if (other_lit == g->lhs) { - // assert (false); + // CADICAL_assert (false); // } } LOG ("normal path"); produce_rewritten_clause_lrat (g->pos_lhs_ids, g->lhs, false); - assert (g->pos_lhs_ids.size () == 4); + CADICAL_assert (g->pos_lhs_ids.size () == 4); reasons_unit.push_back (g->pos_lhs_ids[idx1].clause->id); reasons_unit.push_back (g->pos_lhs_ids[idx2].clause->id); @@ -5520,15 +5524,15 @@ void Closure::produce_ite_merge_lhs_then_else_reasons ( } void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { - assert (unsimplified.empty ()); + CADICAL_assert (unsimplified.empty ()); if (skip_ite_gate (g)) return; if (!gate_contains (g, src)) return; LOG (g, "rewriting %d by %d in", src, dst); - assert (!g->shrunken); - assert (g->rhs.size () == 3); - assert (!internal->lrat || g->pos_lhs_ids.size () == 4); + CADICAL_assert (!g->shrunken); + CADICAL_assert (g->rhs.size () == 3); + CADICAL_assert (!internal->lrat || g->pos_lhs_ids.size () == 4); auto &rhs = g->rhs; const int lhs = g->lhs; const int cond = g->rhs[0]; @@ -5544,16 +5548,16 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { bool garbage = false; bool shrink = true; const auto git = g->indexed ? table.find (g) : end (table); - assert (!g->indexed || git != end (table)); - assert (*git == g); + CADICAL_assert (!g->indexed || git != end (table)); + CADICAL_assert (*git == g); if (internal->val (cond) && internal->val (then_lit) && internal->val (else_lit)) { // propagation has set all value anyway LOG (g, "all values are set"); - assert (internal->val (g->lhs)); + CADICAL_assert (internal->val (g->lhs)); garbage = true; } else if (internal->val (g->lhs) && internal->val (cond)) { LOG (g, "all values are set 2"); - assert (internal->val (g->lhs)); + CADICAL_assert (internal->val (g->lhs)); garbage = true; } // this code is taken one-to-one from kissat @@ -5576,14 +5580,14 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { // !then_lit & then_lit | then_lit & else_lit // then_lit & else_lit rhs[0] = else_lit; - assert (rhs[1] == then_lit); + CADICAL_assert (rhs[1] == then_lit); garbage = rewrite_ite_gate_to_and (g, src, dst, 0, 2, -dst); } else if (dst == else_lit) { // else_list ? then_lit : else_lit // else_list & then_lit | !else_list & else_lit // else_list & then_lit rhs[0] = else_lit; - assert (rhs[1] == then_lit); + CADICAL_assert (rhs[1] == then_lit); garbage = rewrite_ite_gate_to_and (g, src, dst, 2, 0, dst); } else if (not_dst == else_lit) { // !else_list ? then_lit : else_lit @@ -5680,7 +5684,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs, false); if (internal->lrat) { - assert (g->pos_lhs_ids.size () == 2); + 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); } @@ -5689,9 +5693,9 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { } else { LOG ("changing to xor"); new_tag = Gate_Type::XOr_Gate; - assert (rhs[0] == cond); + CADICAL_assert (rhs[0] == cond); rhs[1] = else_lit; - assert (!internal->lrat || !g->pos_lhs_ids.empty ()); + CADICAL_assert (!internal->lrat || !g->pos_lhs_ids.empty ()); { #ifdef LOGGING for (auto litId : g->pos_lhs_ids) { @@ -5713,7 +5717,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { rewrite_ite_gate_update_lrat_reasons (g, src, dst); } } else { - assert (src == else_lit); + CADICAL_assert (src == else_lit); if (not_dst == g->lhs) { // TODO not in kissat rhs[2] = dst; std::vector<LRAT_ID> reasons_implication, reasons_back, reasons_unit; @@ -5749,8 +5753,8 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { // cond ? then_lit : cond // cond & then_lit | !cond & cond // cond & then_lit - assert (rhs[0] == cond); - assert (rhs[1] == then_lit); + CADICAL_assert (rhs[0] == cond); + CADICAL_assert (rhs[1] == then_lit); garbage = rewrite_ite_gate_to_and (g, src, dst, 2, 0, cond); } else if (not_dst == cond) { // cond ? then_lit : !cond @@ -5759,7 +5763,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { // then_lit | !cond // !(!then_lit & cond) g->lhs = not_lhs; - assert (rhs[0] == cond); + CADICAL_assert (rhs[0] == cond); rhs[1] = not_then_lit; if (then_lit == lhs || cond == lhs) garbage = true; @@ -5785,7 +5789,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, false); if (internal->lrat) { - assert (g->pos_lhs_ids.size () == 2); + 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); } @@ -5795,7 +5799,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, false); if (internal->lrat) { - assert (g->pos_lhs_ids.size () == 2); + 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); } @@ -5805,7 +5809,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, false); if (internal->lrat) { - assert (g->pos_lhs_ids.size () == 2); + 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); } @@ -5814,10 +5818,10 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { } else { new_tag = Gate_Type::XOr_Gate; g->lhs = not_lhs; - assert (rhs[0] == cond); - assert (rhs[1] == then_lit); - assert (rhs[0] != g->lhs); - assert (rhs[1] != g->lhs); + CADICAL_assert (rhs[0] == cond); + CADICAL_assert (rhs[1] == then_lit); + CADICAL_assert (rhs[0] != g->lhs); + CADICAL_assert (rhs[1] != g->lhs); produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, false); } @@ -5828,8 +5832,8 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { } } if (!garbage && !internal->unsat) { - assert (new_tag != Gate_Type::ITE_Gate || g->lhs != -rhs[1]); - assert (new_tag != Gate_Type::ITE_Gate || g->lhs != -rhs[2]); + CADICAL_assert (new_tag != Gate_Type::ITE_Gate || g->lhs != -rhs[1]); + CADICAL_assert (new_tag != Gate_Type::ITE_Gate || g->lhs != -rhs[2]); if (shrink) { if (new_tag == Gate_Type::XOr_Gate) { bool negate_lhs = false; @@ -5848,15 +5852,15 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { internal->vlit ( rhs[1])) { // unlike kissat, we need to do it after negating std::swap (rhs[0], rhs[1]); - assert (new_tag != Gate_Type::ITE_Gate); + CADICAL_assert (new_tag != Gate_Type::ITE_Gate); } - assert (internal->vlit (rhs[0]) < internal->vlit (rhs[1])); - assert (!g->shrunken); + CADICAL_assert (internal->vlit (rhs[0]) < internal->vlit (rhs[1])); + CADICAL_assert (!g->shrunken); g->shrunken = true; rhs[2] = 0; g->tag = new_tag; rhs.resize (2); - assert (rhs[0] != -rhs[1]); + CADICAL_assert (rhs[0] != -rhs[1]); if (new_tag == Gate_Type::XOr_Gate) { if (rhs[0] == -g->lhs || rhs[1] == -g->lhs) { LOG (g, "special XOR:"); @@ -5864,7 +5868,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, false); if (internal->lrat) { - assert (g->pos_lhs_ids.size () == 2); + 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); } @@ -5876,7 +5880,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, not_lhs, false); if (internal->lrat) { - assert (g->pos_lhs_ids.size () == 2); + 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); } @@ -5886,7 +5890,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { int i = 0; bool negated = false; for (int j = 0; j < 2; ++i, ++j) { - assert (i <= j); + CADICAL_assert (i <= j); const int lit = rhs[i] = rhs[j]; const char v = internal->val (lit); if (v > 0) { @@ -5896,7 +5900,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { --i; } } - assert (i <= 2); + CADICAL_assert (i <= 2); rhs.resize (i); if (negated) { g->lhs = -g->lhs; @@ -5913,10 +5917,10 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { } #endif produce_rewritten_clause_lrat_and_clean (g->pos_lhs_ids, g->lhs); - assert (!internal->lrat || g->pos_lhs_ids.size () == 2); + CADICAL_assert (!internal->lrat || g->pos_lhs_ids.size () == 2); Clause *c1 = nullptr, *c2 = nullptr; if (internal->lrat) { - assert (g->pos_lhs_ids[0].clause); + 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; @@ -5935,10 +5939,10 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { } } if (!garbage) { - assert (rhs[0] != g->lhs); - assert (rhs[1] != g->lhs); - assert (rhs[0] != -g->lhs); - assert (rhs[1] != -g->lhs); + CADICAL_assert (rhs[0] != g->lhs); + CADICAL_assert (rhs[1] != g->lhs); + CADICAL_assert (rhs[0] != -g->lhs); + CADICAL_assert (rhs[1] != -g->lhs); } } @@ -5948,10 +5952,10 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { if (internal->lrat) { if (new_tag == Gate_Type::XOr_Gate) { -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG std::for_each (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), [g] (LitClausePair l) { - assert ((size_t) l.clause->size == + CADICAL_assert ((size_t) l.clause->size == 1 + g->arity ()); }); #endif @@ -5959,25 +5963,29 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { // we have to get rid of one clause, two become binaries, and // becomes ternary -#if defined(LOGGING) || !defined(NDEBUG) +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) for (auto id : g->pos_lhs_ids) { LOG (id.clause, "clause after rewriting:"); - assert (id.clause->size == 2); + CADICAL_assert (id.clause->size == 2); } #endif - assert (g->pos_lhs_ids.size () == 2 || + CADICAL_assert (g->pos_lhs_ids.size () == 2 || gate_contains (g, g->lhs)); - assert (g->neg_lhs_ids.size () == 1 || + CADICAL_assert (g->neg_lhs_ids.size () == 1 || gate_contains (g, g->lhs)); - assert (g->arity () == 2); -#ifndef NDEBUG + CADICAL_assert (g->arity () == 2); +#ifndef CADICAL_NDEBUG std::for_each ( begin (g->pos_lhs_ids), end (g->pos_lhs_ids), - [] (LitClausePair l) { assert (l.clause->size == 2); }); + [] (LitClausePair l) { CADICAL_assert (l.clause->size == 2); }); #endif } else { - assert (false); + CADICAL_assert (false); +#ifdef WIN32 + __assume(false); +#else __builtin_unreachable (); +#endif } } Gate *h; @@ -5985,7 +5993,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { check_and_gate_implied (g); h = find_and_lits (rhs); } else { - assert (new_tag == Gate_Type::XOr_Gate); + CADICAL_assert (new_tag == Gate_Type::XOr_Gate); check_xor_gate_implied (g); h = find_xor_gate (g); } @@ -6015,7 +6023,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { if (g->indexed) remove_gate (git); index_gate (g); - assert (g->arity () == 2); + CADICAL_assert (g->arity () == 2); for (auto lit : g->rhs) if (lit != dst) if (lit != cond && lit != then_lit && lit != else_lit) @@ -6029,18 +6037,18 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { LOG (g, "rewritten"); if (internal->lrat) update_ite_flags (g), check_correct_ite_flags(g); - assert (rhs[0] != rhs[1]); - assert (rhs[0] != rhs[2]); - assert (rhs[1] != rhs[2]); - assert (rhs[0] != -(rhs[1])); - assert (rhs[0] != -(rhs[2])); - assert (rhs[1] != -(rhs[2])); + CADICAL_assert (rhs[0] != rhs[1]); + CADICAL_assert (rhs[0] != rhs[2]); + CADICAL_assert (rhs[1] != rhs[2]); + CADICAL_assert (rhs[0] != -(rhs[1])); + CADICAL_assert (rhs[0] != -(rhs[2])); + CADICAL_assert (rhs[1] != -(rhs[2])); check_ite_gate_implied (g); check_ite_lrat_reasons (g, false); bool negate_lhs; Gate *h = find_ite_gate (g, negate_lhs); - assert (lhs == g->lhs); - assert (not_lhs == -(g->lhs)); + CADICAL_assert (lhs == g->lhs); + CADICAL_assert (not_lhs == -(g->lhs)); if (negate_lhs) g->lhs = -lhs; check_ite_lrat_reasons (g); @@ -6061,7 +6069,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { extra_reasons_ulit)) ++internal->stats.congruence.ites; delete_proof_chain (); - assert (internal->unsat || chain.empty ()); + CADICAL_assert (internal->unsat || chain.empty ()); } else { garbage = false; if (g->indexed) @@ -6069,7 +6077,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { LOG (g, "normalized"); g->hash = hash_lits (nonces, g->rhs); index_gate (g); - assert (g->arity () == 3); + CADICAL_assert (g->arity () == 3); for (auto lit : g->rhs) if (lit != dst) if (lit != cond && lit != then_lit && lit != else_lit) @@ -6080,7 +6088,7 @@ void Closure::rewrite_ite_gate (Gate *g, int dst, int src) { if (garbage && !internal->unsat) mark_garbage (g); - assert (chain.empty ()); + CADICAL_assert (chain.empty ()); } void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, @@ -6091,13 +6099,13 @@ void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, // TODO if (internal->val (lit) > 0) return; - assert (internal->lrat); - assert (g); - assert (idx1 < g->pos_lhs_ids.size ()); - assert (idx2 < g->pos_lhs_ids.size ()); - assert (g->pos_lhs_ids.size () == 4); + CADICAL_assert (internal->lrat); + CADICAL_assert (g); + CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); + CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); + CADICAL_assert (g->pos_lhs_ids.size () == 4); - assert (idx1 != idx2); + CADICAL_assert (idx1 != idx2); Clause *c = g->pos_lhs_ids[idx1].clause; Clause *d = g->pos_lhs_ids[idx2].clause; @@ -6105,22 +6113,22 @@ void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, 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); - assert (c); + CADICAL_assert (c); // not possible to do this in a single lrat chain push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, Rewrite (), g->lhs); - assert (d); + CADICAL_assert (d); return; } if (g->lhs == g->rhs[0]) { LOG ("special case of LHS=cond where only one clause in LRAT is needed is needed"); size_t idx = (internal->val (g->rhs[1]) > 0 ? idx2 : idx1); c = produce_rewritten_clause_lrat (g->pos_lhs_ids[idx].clause, g->lhs, false, false); - assert (c); + CADICAL_assert (c); // not possible to do this in a single lrat chain push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain, true, Rewrite (), g->lhs); - assert (d); + CADICAL_assert (d); return; } @@ -6133,7 +6141,7 @@ void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, } else if (!c) { push_id_and_rewriting_lrat_unit (d, Rewrite (), lrat_chain, true, Rewrite (), g->lhs); - assert (d); + CADICAL_assert (d); lrat_chain.push_back (d->id); } } @@ -6142,10 +6150,10 @@ void Closure::simplify_ite_gate_produce_unit_lrat (Gate *g, int lit, void Closure::merge_and_gate_lrat_produce_lrat ( Gate *g, Gate *h, std::vector<LRAT_ID> &reasons_lrat_src, std::vector<LRAT_ID> &reasons_lrat_usrc, bool remove_units) { - assert (internal->lrat); - assert (g->tag == Gate_Type::And_Gate); - assert (h->tag == Gate_Type::And_Gate); - assert (g->neg_lhs_ids.size () <= 1); + 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); } @@ -6153,8 +6161,8 @@ void Closure::merge_and_gate_lrat_produce_lrat ( // odd copy of rewrite_ite_gate_lrat_and bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, int removed_lit) { - assert (internal->lrat_chain.empty ()); - assert (g->rhs.size () == 3); + CADICAL_assert (internal->lrat_chain.empty ()); + CADICAL_assert (g->rhs.size () == 3); #ifdef LOGGING for (auto litId : g->pos_lhs_ids) { LOG (litId.clause, "%d ->", litId.current_lit); @@ -6164,7 +6172,7 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, if (internal->lrat) { Clause *c = g->pos_lhs_ids[idx1].clause; push_id_and_rewriting_lrat_unit (c, Rewrite (), lrat_chain); - assert (!lrat_chain.empty ()); + CADICAL_assert (!lrat_chain.empty ()); } learn_congruence_unit (-g->lhs); return true; @@ -6174,9 +6182,9 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, 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); - assert (g->pos_lhs_ids.size () == 4); - assert (idx1 < g->pos_lhs_ids.size ()); - assert (idx2 < g->pos_lhs_ids.size ()); + CADICAL_assert (g->pos_lhs_ids.size () == 4); + CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); + CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); int lit = g->pos_lhs_ids[idx2].current_lit, other = g->lhs; size_t new_idx1 = idx1; size_t new_idx2 = idx2; @@ -6187,7 +6195,7 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, LOG (g, "degenerated AND gate"); const int replacement_lit = (g->rhs[1] == g->lhs ? g->rhs[0] : g->rhs[1]); for (auto &litId : g->pos_lhs_ids) { - assert (litId.clause); + CADICAL_assert (litId.clause); LOG (litId.clause, "%d ->", litId.current_lit); if (litId.current_lit == removed_lit) litId.current_lit = -replacement_lit; @@ -6195,32 +6203,32 @@ 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 - assert (std::find (begin (*litId.clause), end (*litId.clause), litId.current_lit) != + CADICAL_assert (std::find (begin (*litId.clause), end (*litId.clause), litId.current_lit) != end (*litId.clause)); - assert (std::find (begin (g->rhs), end (g->rhs), litId.current_lit) != + CADICAL_assert (std::find (begin (g->rhs), end (g->rhs), litId.current_lit) != end (g->rhs)); } return false; } - assert (new_idx1 < g->pos_lhs_ids.size ()); - assert (new_idx2 < g->pos_lhs_ids.size ()); + 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; - assert (c->size == 2); - assert (new_idx1 != new_idx2); + CADICAL_assert (c->size == 2); + CADICAL_assert (new_idx1 != new_idx2); Clause *d = g->pos_lhs_ids[new_idx2].clause; - assert (c != d); - assert (c); - assert (d); + CADICAL_assert (c != d); + CADICAL_assert (c); + CADICAL_assert (d); g->pos_lhs_ids.erase (std::remove_if (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), [d] (const LitClausePair &p) { return p.clause == d; }), end (g->pos_lhs_ids)); - assert (g->pos_lhs_ids.size () == 2); - assert (lit); - assert (other); - assert (lit != other); + CADICAL_assert (g->pos_lhs_ids.size () == 2); + CADICAL_assert (lit); + CADICAL_assert (other); + CADICAL_assert (lit != other); lrat_chain.push_back (c->id); lrat_chain.push_back (d->id); Clause *e = add_tmp_binary_clause (lit, -other); @@ -6228,20 +6236,20 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, auto long_clause = std::find_if (begin (g->pos_lhs_ids), end (g->pos_lhs_ids), [] (LitClausePair l) { return l.clause->size == 3; }); - assert (long_clause != end (g->pos_lhs_ids)); + CADICAL_assert (long_clause != end (g->pos_lhs_ids)); LOG (long_clause->clause, "new long clause"); g->neg_lhs_ids.push_back (*long_clause); g->pos_lhs_ids.erase (long_clause); - assert (g->pos_lhs_ids.size () == 1); + CADICAL_assert (g->pos_lhs_ids.size () == 1); g->pos_lhs_ids.push_back ({lit, e}); const int first_lit = g->rhs[0]; const int second_lit = g->rhs[1]; for (auto &litId : g->pos_lhs_ids) { - assert (litId.clause); + CADICAL_assert (litId.clause); LOG (litId.clause, "%s ->", LOGLIT (litId.current_lit)); if (internal->val (litId.current_lit)) { - assert (litId.clause->size == 2); + CADICAL_assert (litId.clause->size == 2); int replacement_lit = 0; for (int i = 0; i < 2; ++i) { if (litId.clause->literals[i] == first_lit) { @@ -6252,7 +6260,7 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, break; } } - assert (replacement_lit); + CADICAL_assert (replacement_lit); litId.current_lit = replacement_lit; } else if (litId.current_lit == removed_lit) @@ -6261,9 +6269,9 @@ bool Closure::simplify_ite_gate_to_and (Gate *g, size_t idx1, size_t idx2, litId.current_lit = g->rhs[0]; LOG (litId.clause, "%d ->", litId.current_lit); // TODO we need a replacement index - assert (std::find (begin (g->rhs), end (g->rhs), litId.current_lit) != + CADICAL_assert (std::find (begin (g->rhs), end (g->rhs), litId.current_lit) != end (g->rhs)); - assert (std::find (begin (*litId.clause), end (*litId.clause), + CADICAL_assert (std::find (begin (*litId.clause), end (*litId.clause), litId.current_lit) != end (*litId.clause)); } return false; @@ -6273,9 +6281,9 @@ void Closure::merge_ite_gate_same_then_else_lrat ( std::vector<LitClausePair> &clauses, std::vector<LRAT_ID> &reasons_implication, std::vector<LRAT_ID> &reasons_back) { - assert (clauses.size () == 4); + CADICAL_assert (clauses.size () == 4); produce_rewritten_clause_lrat_and_clean (clauses); - assert (clauses.size () == 4); + CADICAL_assert (clauses.size () == 4); auto then_imp = clauses[0]; auto neg_then_imp = clauses[1]; auto else_imp = clauses[2]; @@ -6289,8 +6297,8 @@ void Closure::merge_ite_gate_same_then_else_lrat ( void Closure::simplify_ite_gate_then_else_set ( Gate *g, std::vector<LRAT_ID> &reasons_implication, std::vector<LRAT_ID> &reasons_back, size_t idx1, size_t idx2) { - assert (idx1 < g->pos_lhs_ids.size ()); - assert (idx2 < g->pos_lhs_ids.size ()); + CADICAL_assert (idx1 < g->pos_lhs_ids.size ()); + CADICAL_assert (idx2 < g->pos_lhs_ids.size ()); Clause *c = g->pos_lhs_ids[idx1].clause; Clause *d = g->pos_lhs_ids[idx2].clause; push_id_and_rewriting_lrat_unit (c, Rewrite (), reasons_back, true, @@ -6300,11 +6308,11 @@ void Closure::simplify_ite_gate_then_else_set ( LOG (reasons_back, "LRAT"); LOG (reasons_implication, "LRAT"); // c = produce_rewritten_clause_lrat (c, g->lhs, false); - // assert (c); + // CADICAL_assert (c); // d = produce_rewritten_clause_lrat (d, g->lhs, false); - // assert (d); + // CADICAL_assert (d); // const int cond = g->rhs[0]; - // assert (internal->val (cond)); + // CADICAL_assert (internal->val (cond)); // reasons_implication.push_back(internal->unit_id (internal->val (cond) > // 0 ? cond : -cond)); reasons_implication.push_back(c->id); // reasons_back.push_back(internal->unit_id (internal->val (cond) > 0 ? @@ -6314,12 +6322,12 @@ void Closure::simplify_ite_gate_then_else_set ( void Closure::simplify_ite_gate_condition_set ( Gate *g, std::vector<LRAT_ID> &reasons_lrat, std::vector<LRAT_ID> &reasons_back_lrat, size_t idx1, size_t idx2) { - assert (internal->lrat); + CADICAL_assert (internal->lrat); Clause *c = g->pos_lhs_ids[idx1].clause; Clause *d = g->pos_lhs_ids[idx2].clause; -#if defined(LOGGING) || !defined(NDEBUG) +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) const int cond = g->rhs[0]; - assert (internal->val (cond)); + CADICAL_assert (internal->val (cond)); LOG ("cond = %d", cond); #endif #ifdef LOGGING @@ -6336,7 +6344,7 @@ void Closure::simplify_ite_gate (Gate *g) { if (skip_ite_gate (g)) return; LOG (g, "simplifying"); - assert (g->arity () == 3); + CADICAL_assert (g->arity () == 3); bool garbage = true; int lhs = g->lhs; auto &rhs = g->rhs; @@ -6349,7 +6357,7 @@ void Closure::simplify_ite_gate (Gate *g) { std::vector<LRAT_ID> reasons_lrat, reasons_back_lrat; if (v_cond && v_else && v_then) { // propagation has set all value anyway LOG (g, "all values are set"); - assert (internal->val (g->lhs)); + CADICAL_assert (internal->val (g->lhs)); garbage = true; } else if (v_cond > 0) { if (internal->lrat) @@ -6372,7 +6380,7 @@ void Closure::simplify_ite_gate (Gate *g) { } else { LOG ("then %d: %d; else %d: %d", then_lit, v_then, else_lit, v_else); std::vector<LRAT_ID> extra_reasons, extra_reasons_back; - assert (v_then || v_else); + CADICAL_assert (v_then || v_else); if (v_then > 0 && v_else > 0) { simplify_ite_gate_produce_unit_lrat (g, lhs, 1, 3); learn_congruence_unit (lhs); @@ -6398,9 +6406,9 @@ void Closure::simplify_ite_gate (Gate *g) { ++internal->stats.congruence.unaries; } } else { - assert (!!v_then + !!v_else == 1); + CADICAL_assert (!!v_then + !!v_else == 1); auto git = g->indexed ? table.find (g) : end (table); - assert (!g->indexed || git != end (table)); + CADICAL_assert (!g->indexed || git != end (table)); if (v_then > 0) { g->lhs = -lhs; rhs[0] = -cond; @@ -6416,7 +6424,7 @@ void Closure::simplify_ite_gate (Gate *g) { rhs[1] = cond; simplify_ite_gate_to_and (g, 3, 1, else_lit); } else { - assert (v_else < 0); + CADICAL_assert (v_else < 0); rhs[0] = then_lit; rhs[1] = cond; simplify_ite_gate_to_and (g, 2, 0, -else_lit); @@ -6428,13 +6436,13 @@ void Closure::simplify_ite_gate (Gate *g) { g->shrunken = true; g->tag = Gate_Type::And_Gate; rhs.resize (2); - assert (is_sorted (begin (rhs), end (rhs), + 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) { - assert (garbage); + CADICAL_assert (garbage); std::vector<LRAT_ID> reasons_lrat, reasons_lrat_back; if (internal->lrat) merge_and_gate_lrat_produce_lrat (g, h, reasons_lrat, @@ -6471,16 +6479,16 @@ void Closure::add_ite_matching_proof_chain ( std::vector<LRAT_ID> &reasons2) { check_ite_lrat_reasons (g); check_ite_lrat_reasons (h, false); - assert (g->lhs == lhs1); - assert (h->lhs == lhs2); + 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"); - assert (unsimplified.empty ()); - assert (chain.empty ()); + CADICAL_assert (unsimplified.empty ()); + CADICAL_assert (chain.empty ()); if (internal->lrat) check_correct_ite_flags (g); const auto &rhs = g->rhs; @@ -6497,11 +6505,11 @@ void Closure::add_ite_matching_proof_chain ( const bool degenerated_g_cond = ite_flags_cond_lhs (flags_g); const bool degenerated_h_cond = ite_flags_cond_lhs (flags_h); - assert (!(degenerated_g_cond && degenerated_h_cond)); + CADICAL_assert (!(degenerated_g_cond && degenerated_h_cond)); const bool degenerated_g_not_cond = ite_flags_neg_cond_lhs (flags_g); const bool degenerated_h_not_cond = ite_flags_neg_cond_lhs (flags_h); - assert (!(degenerated_g_not_cond && degenerated_h_not_cond)); + CADICAL_assert (!(degenerated_g_not_cond && degenerated_h_not_cond)); if (internal->lrat) { // the code can produce tautologies in the case that: @@ -6517,7 +6525,7 @@ void Closure::add_ite_matching_proof_chain ( // b = (c ? t : a) // (no clauses with (then) index a and (else) index -e) LOG (g, "get ids from"); - assert (g->pos_lhs_ids.size () == 4); + 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; @@ -6543,7 +6551,7 @@ void Closure::add_ite_matching_proof_chain ( g_neg_else_id = g_neg_else_clause->id; LOG (h, "now clauses from"); - assert (h->pos_lhs_ids.size () == 4); + 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; @@ -6591,8 +6599,8 @@ void Closure::add_ite_matching_proof_chain ( } id2 = simplify_and_add_to_proof_chain (unsimplified); - assert (!internal->lrat || id1); - assert (!internal->lrat || id2); + CADICAL_assert (!internal->lrat || id1); + CADICAL_assert (!internal->lrat || id2); reasons1.push_back (id1); reasons2.push_back (id2); unsimplified.clear (); @@ -6620,8 +6628,8 @@ void Closure::add_ite_matching_proof_chain ( } id2 = simplify_and_add_to_proof_chain (unsimplified); - assert (!internal->lrat || id1); - assert (!internal->lrat || id2); + CADICAL_assert (!internal->lrat || id1); + CADICAL_assert (!internal->lrat || id2); reasons2.push_back (id1); reasons1.push_back (id2); unsimplified.clear (); @@ -6634,7 +6642,7 @@ void Closure::add_ite_matching_proof_chain ( unsimplified.push_back (-lhs2); LRAT_ID id1 = 0; if (internal->lrat) { - assert (g_neg_then_id && h_then_id && g_else_id && h_neg_else_id); + CADICAL_assert (g_neg_then_id && h_then_id && g_else_id && h_neg_else_id); lrat_chain.push_back (g_neg_then_id); lrat_chain.push_back (h_then_id); } @@ -6650,8 +6658,8 @@ void Closure::add_ite_matching_proof_chain ( lrat_chain.push_back (h_neg_else_id); } id2 = simplify_and_add_to_proof_chain (unsimplified); - assert (!internal->lrat || id1); - assert (!internal->lrat || id2); + CADICAL_assert (!internal->lrat || id1); + CADICAL_assert (!internal->lrat || id2); reasons2.push_back (id1); reasons1.push_back (id2); unsimplified.clear (); @@ -6664,7 +6672,7 @@ void Closure::add_ite_matching_proof_chain ( unsimplified.push_back (-lhs2); LRAT_ID id1 = 0; if (internal->lrat) { - assert (g_neg_then_id && h_then_id && g_else_id && h_neg_else_id); + CADICAL_assert (g_neg_then_id && h_then_id && g_else_id && h_neg_else_id); lrat_chain.push_back (h_neg_then_id); lrat_chain.push_back (g_then_id); } @@ -6680,8 +6688,8 @@ void Closure::add_ite_matching_proof_chain ( lrat_chain.push_back (g_neg_else_id); } id2 = simplify_and_add_to_proof_chain (unsimplified); - assert (!internal->lrat || id1); - assert (!internal->lrat || id2); + CADICAL_assert (!internal->lrat || id1); + CADICAL_assert (!internal->lrat || id2); reasons2.push_back (id1); reasons1.push_back (id2); unsimplified.clear (); @@ -6689,16 +6697,16 @@ void Closure::add_ite_matching_proof_chain ( } LOG ("normal path"); - assert (!internal->lrat || degenerated_g_then || + CADICAL_assert (!internal->lrat || degenerated_g_then || (g_then_id && g_neg_then_id)); - assert (!internal->lrat || degenerated_g_else || + CADICAL_assert (!internal->lrat || degenerated_g_else || (g_else_id && g_neg_else_id)); - assert (!internal->lrat || degenerated_h_then || + CADICAL_assert (!internal->lrat || degenerated_h_then || (h_then_id && h_neg_then_id)); - assert (!internal->lrat || degenerated_h_else || + CADICAL_assert (!internal->lrat || degenerated_h_else || (h_else_id && h_neg_else_id)); - assert (!internal->lrat || g_then_id || h_neg_then_id); - assert (!internal->lrat || g_neg_then_id || h_then_id); + CADICAL_assert (!internal->lrat || g_then_id || h_neg_then_id); + CADICAL_assert (!internal->lrat || g_neg_then_id || h_then_id); unsimplified.push_back (-lhs1); unsimplified.push_back (lhs2); unsimplified.push_back (-cond); @@ -6731,7 +6739,7 @@ void Closure::add_ite_matching_proof_chain ( unsimplified.push_back (lhs1); unsimplified.push_back (-lhs2); unsimplified.push_back (-cond); - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); LRAT_ID id3 = 0; if (degenerated_g_then || degenerated_h_then) { @@ -6747,7 +6755,7 @@ void Closure::add_ite_matching_proof_chain ( } unsimplified.pop_back (); unsimplified.push_back (cond); - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); LRAT_ID id4 = 0; if (degenerated_g_else || degenerated_h_else) { @@ -6762,7 +6770,7 @@ void Closure::add_ite_matching_proof_chain ( unsimplified.pop_back (); if (internal->lrat) { - assert (!internal->lrat || (id1 && id2 && id3 && id4)); + CADICAL_assert (!internal->lrat || (id1 && id2 && id3 && id4)); reasons1.push_back (id1), reasons1.push_back (id2); reasons2.push_back (id3), reasons2.push_back (id4); } @@ -6774,7 +6782,7 @@ void Closure::add_ite_matching_proof_chain ( Gate *Closure::new_ite_gate (int lhs, int cond, int then_lit, int else_lit, std::vector<LitClausePair> &&clauses) { - assert (chain.empty ()); + CADICAL_assert (chain.empty ()); if (else_lit == -then_lit) { if (then_lit < 0) LOG ("skipping ternary XOR %d := %d ^ %d", lhs, cond, -then_lit); @@ -6857,15 +6865,15 @@ Gate *Closure::new_ite_gate (int lhs, int cond, int then_lit, int else_lit, } void check_ite_lits_normalized (const std::vector<int> &lits) { - assert (lits[0] > 0); - assert (lits[1] > 0); - assert (lits[0] != lits[1]); - assert (lits[0] != lits[2]); - assert (lits[1] != lits[2]); - assert (lits[0] != -lits[1]); - assert (lits[0] != -lits[2]); - assert (lits[1] != -lits[2]); -#ifdef NDEBUG + CADICAL_assert (lits[0] > 0); + CADICAL_assert (lits[1] > 0); + CADICAL_assert (lits[0] != lits[1]); + CADICAL_assert (lits[0] != lits[2]); + CADICAL_assert (lits[1] != lits[2]); + CADICAL_assert (lits[0] != -lits[1]); + CADICAL_assert (lits[0] != -lits[2]); + CADICAL_assert (lits[1] != -lits[2]); +#ifdef CADICAL_NDEBUG (void) lits; #endif } @@ -6883,46 +6891,46 @@ void Closure::check_ite_implied (int lhs, int cond, int then_lit, void Closure::check_contained_module_rewriting (Clause *c, int lit, bool normalized, int except) { -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG if (lit == except) // happens for degenerated cases except = 0; const int normalize_lit = (lit == except ? except : find_eager_representative (lit)); - assert (!normalized || lit == -except || normalize_lit == lit); + CADICAL_assert (!normalized || lit == -except || normalize_lit == lit); bool found = false; LOG (c, "looking for (normalized) %d in ", lit); for (auto other : *c) { const int normalize_other = (other == except ? except : find_eager_representative (other)); LOG ("%d -> %d ", other, normalize_other); - assert (!normalized || other == -except || normalize_other == other); + CADICAL_assert (!normalized || other == -except || normalize_other == other); if (normalize_other == normalize_lit) { found = true; break; } } - assert (found); + CADICAL_assert (found); #else (void) c, (void) lit, (void) normalized, (void) except; #endif } void Closure::check_ite_lrat_reasons (Gate *g, bool normalized) { -#ifndef NDEBUG - assert (g->tag == Gate_Type::ITE_Gate); +#ifndef CADICAL_NDEBUG + CADICAL_assert (g->tag == Gate_Type::ITE_Gate); if (!internal->lrat) return; - assert (g->rhs.size () == 3); - assert (is_tautological_ite_gate (g) || g->pos_lhs_ids.size () == 4); - assert (g->neg_lhs_ids.empty ()); - assert (g->pos_lhs_ids.size () == 4); + CADICAL_assert (g->rhs.size () == 3); + CADICAL_assert (is_tautological_ite_gate (g) || g->pos_lhs_ids.size () == 4); + CADICAL_assert (g->neg_lhs_ids.empty ()); + CADICAL_assert (g->pos_lhs_ids.size () == 4); #else (void) g, (void) normalized; #endif } void Closure::check_ite_gate_implied (Gate *g) { - assert (g->tag == Gate_Type::ITE_Gate); + CADICAL_assert (g->tag == Gate_Type::ITE_Gate); if (internal->lrat) return; check_ite_implied (g->lhs, g->rhs[0], g->rhs[1], g->rhs[2]); @@ -6941,7 +6949,7 @@ void Closure::init_ite_gate_extraction ( continue; unsigned size = 0; - assert (!c->garbage); + CADICAL_assert (!c->garbage); for (auto lit : *c) { const signed char v = internal->val (lit); if (v < 0) @@ -6957,7 +6965,7 @@ void Closure::init_ite_gate_extraction ( } if (size < 3) continue; - assert (size == 3); + CADICAL_assert (size == 3); ternary.push_back (c); LOG (c, "counting original ITE gate base"); for (auto lit : *c) { @@ -6968,8 +6976,8 @@ void Closure::init_ite_gate_extraction ( } for (auto c : ternary) { - assert (!c->garbage); - assert (!c->redundant); + CADICAL_assert (!c->garbage); + CADICAL_assert (!c->redundant); unsigned positive = 0, negative = 0, twice = 0; for (auto lit : *c) { if (internal->val (lit)) @@ -6978,7 +6986,7 @@ void Closure::init_ite_gate_extraction ( if (!count_not_lit) goto CONTINUE_WITH_NEXT_TERNARY_CLAUSE; const unsigned count_lit = largecount (lit); - assert (count_lit); + CADICAL_assert (count_lit); if (count_lit > 1 && count_not_lit > 1) ++twice; if (lit < 0) @@ -6988,7 +6996,7 @@ void Closure::init_ite_gate_extraction ( } if (twice < 2) goto CONTINUE_WITH_NEXT_TERNARY_CLAUSE; - assert (c->size != 2); + CADICAL_assert (c->size != 2); for (auto lit : *c) internal->occs (lit).push_back (c); if (positive && negative) @@ -7010,9 +7018,9 @@ void Closure::reset_ite_gate_extraction () { void Closure::copy_conditional_equivalences (int lit, lit_implications &condbin) { - assert (condbin.empty ()); + CADICAL_assert (condbin.empty ()); for (auto c : internal->occs (lit)) { - assert (c->size != 2); + CADICAL_assert (c->size != 2); int first = 0, second = 0; for (auto other : *c) { if (internal->val (other)) @@ -7022,21 +7030,21 @@ void Closure::copy_conditional_equivalences (int lit, if (!first) first = other; else { - assert (!second); + CADICAL_assert (!second); second = other; } } - assert (first), assert (second); + CADICAL_assert (first), CADICAL_assert (second); lit_implication p (first, second, c); if (internal->vlit (first) < internal->vlit (second)) { - assert (p.first == first); - assert (p.second == second); + CADICAL_assert (p.first == first); + CADICAL_assert (p.second == second); } else { - assert (internal->vlit (second) < internal->vlit (first)); + CADICAL_assert (internal->vlit (second) < internal->vlit (first)); p.swap (); - assert (p.first == second); - assert (p.second == first); + CADICAL_assert (p.first == second); + CADICAL_assert (p.second == first); } LOG (c, "literal %d condition binary clause %d %d", lit, first, second); condbin.push_back (p); @@ -7088,7 +7096,7 @@ Closure::find_lit_implication_second_literal ( [] (const lit_implication &a, const lit_implication &b) { return a.second < b.second; }); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG auto found2 = std::binary_search ( begin, end, lit_implication{lit, lit}, [] (const lit_implication &a, const lit_implication &b) { @@ -7097,7 +7105,7 @@ Closure::find_lit_implication_second_literal ( #endif if (found < end && found->second == lit) { - assert (found2 == (found < end)); + CADICAL_assert (found2 == (found < end)); return found; } return end; @@ -7110,24 +7118,24 @@ void Closure::search_condeq (int lit, int pos_lit, lit_implications::const_iterator neg_begin, lit_implications::const_iterator neg_end, lit_equivalences &condeq) { - assert (neg_lit == -pos_lit); - assert (pos_begin < pos_end); - assert (neg_begin < neg_end); - assert (pos_begin->first == pos_lit); - assert (neg_begin->first == neg_lit); - assert (pos_end <= neg_begin || neg_end <= pos_begin); + CADICAL_assert (neg_lit == -pos_lit); + CADICAL_assert (pos_begin < pos_end); + CADICAL_assert (neg_begin < neg_end); + CADICAL_assert (pos_begin->first == pos_lit); + CADICAL_assert (neg_begin->first == neg_lit); + CADICAL_assert (pos_end <= neg_begin || neg_end <= pos_begin); for (lit_implications::const_iterator p = pos_begin; p != pos_end; p++) { const int other = p->second; const int not_other = -other; const lit_implications::const_iterator other_clause = find_lit_implication_second_literal (not_other, neg_begin, neg_end); - assert (std::find (begin (*p->clause), end (*p->clause), lit) != + CADICAL_assert (std::find (begin (*p->clause), end (*p->clause), lit) != end (*p->clause)); if (other_clause != neg_end) { - assert (std::find (begin (*other_clause->clause), + CADICAL_assert (std::find (begin (*other_clause->clause), end (*other_clause->clause), neg_lit) != end (*other_clause->clause)); - assert (std::find (begin (*p->clause), end (*p->clause), other) != + CADICAL_assert (std::find (begin (*p->clause), end (*p->clause), other) != end (*p->clause)); lit_equivalence equivalence (neg_lit, other_clause->clause, other, p->clause); @@ -7138,8 +7146,8 @@ void Closure::search_condeq (int lit, int pos_lit, equivalence.check_invariant (); LOG ("found conditional %d equivalence %d = %d", lit, equivalence.first, equivalence.second); - assert (equivalence.first > 0); - assert (internal->vlit (equivalence.first) < + CADICAL_assert (equivalence.first > 0); + CADICAL_assert (internal->vlit (equivalence.first) < internal->vlit (equivalence.second)); check_ternary (lit, neg_lit, -other); check_ternary (lit, -neg_lit, other); @@ -7187,9 +7195,9 @@ void Closure::extract_condeq_pairs (int lit, lit_implications &condbin, } for (;;) { - assert (pos_begin != end); - assert (next_lit == pos_begin->first); - assert (next_lit > 0); + CADICAL_assert (pos_begin != end); + CADICAL_assert (next_lit == pos_begin->first); + CADICAL_assert (next_lit > 0); const int pos_lit = next_lit; lit_implications::const_iterator pos_end = pos_begin + 1; LOG ("searching for first other literal after finding lit %d", @@ -7202,8 +7210,8 @@ void Closure::extract_condeq_pairs (int lit, lit_implications &condbin, break; pos_end++; } - assert (pos_end != end); - assert (next_lit == pos_end->first); + CADICAL_assert (pos_end != end); + CADICAL_assert (next_lit == pos_end->first); const int neg_lit = -pos_lit; if (next_lit != neg_lit) { if (next_lit < 0) { @@ -7254,7 +7262,7 @@ void Closure::extract_condeq_pairs (int lit, lit_implications &condbin, } if (neg_end == end) return; - assert (next_lit == neg_end->first); + CADICAL_assert (next_lit == neg_end->first); if (next_lit < 0) { pos_begin = neg_end + 1; for (;;) { @@ -7273,9 +7281,9 @@ void Closure::extract_condeq_pairs (int lit, lit_implications &condbin, void Closure::find_conditional_equivalences (int lit, lit_implications &condbin, lit_equivalences &condeq) { - assert (condbin.empty ()); - assert (condeq.empty ()); - assert (internal->occs (lit).size () > 1); + CADICAL_assert (condbin.empty ()); + CADICAL_assert (condeq.empty ()); + CADICAL_assert (internal->occs (lit).size () > 1); copy_conditional_equivalences (lit, condbin); MSORT (internal->opts.radixsortlim, begin (condbin), end (condbin), litpair_rank (this->internal), litpair_smaller (this->internal)); @@ -7303,7 +7311,7 @@ void Closure::merge_condeq (int cond, lit_equivalences &condeq, const int then_lit = p.second; if (internal->lrat) p.check_invariant (); - assert (lhs > 0); + CADICAL_assert (lhs > 0); while (q != end_not_condeq && q->first < lhs) ++q; while (q != end_not_condeq && q->first == lhs) { @@ -7315,8 +7323,8 @@ void Closure::merge_condeq (int cond, lit_equivalences &condeq, if (internal->lrat) { // The then/else literal is the second of the pair, hence the swap // of the reasons - assert (p.first_clause && p.second_clause); - assert (not_cond_pair.first_clause && not_cond_pair.second_clause); + CADICAL_assert (p.first_clause && p.second_clause); + CADICAL_assert (not_cond_pair.first_clause && not_cond_pair.second_clause); LOG (p.second_clause, "pairing %d", then_lit); LOG (p.first_clause, "pairing %d", -then_lit); LOG (not_cond_pair.second_clause, "pairing %d", else_lit); @@ -7374,7 +7382,7 @@ void Closure::extract_ite_gates_of_variable (int idx) { } void Closure::extract_ite_gates () { - assert (!full_watching); + CADICAL_assert (!full_watching); if (!internal->opts.congruenceite) return; START (extractites); @@ -7398,15 +7406,15 @@ void Closure::extract_ite_gates () { void Closure::extract_gates () { START (extract); extract_and_gates (); - assert (internal->unsat || lrat_chain.empty ()); - assert (internal->unsat || chain.empty ()); + CADICAL_assert (internal->unsat || lrat_chain.empty ()); + CADICAL_assert (internal->unsat || chain.empty ()); if (internal->unsat || internal->terminated_asynchronously ()) { STOP (extract); return; } extract_xor_gates (); - assert (internal->unsat || lrat_chain.empty ()); - assert (internal->unsat || chain.empty ()); + CADICAL_assert (internal->unsat || lrat_chain.empty ()); + CADICAL_assert (internal->unsat || chain.empty ()); if (internal->unsat || internal->terminated_asynchronously ()) { STOP (extract); @@ -7459,30 +7467,30 @@ bool Internal::extract_gates () { Closure closure (this); closure.init_closure (); - assert (unsat || closure.chain.empty ()); - assert (unsat || lrat_chain.empty ()); + CADICAL_assert (unsat || closure.chain.empty ()); + CADICAL_assert (unsat || lrat_chain.empty ()); closure.extract_binaries (); - assert (unsat || closure.chain.empty ()); - assert (unsat || lrat_chain.empty ()); + CADICAL_assert (unsat || closure.chain.empty ()); + CADICAL_assert (unsat || lrat_chain.empty ()); closure.extract_gates (); - assert (unsat || closure.chain.empty ()); - assert (unsat || lrat_chain.empty ()); + CADICAL_assert (unsat || closure.chain.empty ()); + CADICAL_assert (unsat || lrat_chain.empty ()); internal->clear_watches (); internal->connect_watches (); closure.reset_extraction (); if (!unsat) { closure.find_units (); - assert (unsat || closure.chain.empty ()); - assert (unsat || lrat_chain.empty ()); + CADICAL_assert (unsat || closure.chain.empty ()); + CADICAL_assert (unsat || lrat_chain.empty ()); if (!internal->unsat) { closure.find_equivalences (); - assert (unsat || closure.chain.empty ()); - assert (unsat || lrat_chain.empty ()); + CADICAL_assert (unsat || closure.chain.empty ()); + CADICAL_assert (unsat || lrat_chain.empty ()); if (!unsat) { const int propagated = closure.propagate_units_and_equivalences (); - assert (unsat || closure.chain.empty ()); + CADICAL_assert (unsat || closure.chain.empty ()); if (!unsat && propagated) closure.forward_subsume_matching_clauses (); } @@ -7496,29 +7504,31 @@ bool Internal::extract_gates () { propagated2 = propagated = 0; propagate (); } - assert (closure.new_unwatched_binary_clauses.empty ()); + CADICAL_assert (closure.new_unwatched_binary_clauses.empty ()); internal->reset_occs (); internal->reset_noccs (); - assert (!internal->occurring ()); - assert (lrat_chain.empty ()); + CADICAL_assert (!internal->occurring ()); + CADICAL_assert (lrat_chain.empty ()); const int64_t new_merged = stats.congruence.congruent; +#ifndef CADICAL_QUIET phase ("congruence-phase", stats.congruence.rounds, "merged %ld literals", new_merged - old_merged); +#endif if (!unsat && !internal->propagate ()) unsat = true; STOP_SIMPLIFIER (congruence, CONGRUENCE); report ('c', !opts.reportall && !(stats.congruence.congruent - old)); -#ifndef NDEBUG +#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 ()) - assert (!w.clause->garbage); + CADICAL_assert (!w.clause->garbage); if (w.clause->garbage) continue; ++watched; @@ -7534,9 +7544,9 @@ bool Internal::extract_gates () { LOG (c, "watched"); ++nb_clauses; } - assert (watched == nb_clauses * 2); + CADICAL_assert (watched == nb_clauses * 2); #endif - assert (!internal->occurring ()); + CADICAL_assert (!internal->occurring ()); if (new_merged == old_merged) { congruence_delay.bumpreasons.interval++; @@ -7553,3 +7563,5 @@ bool Internal::extract_gates () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/constrain.cpp b/src/sat/cadical/cadical_constrain.cpp similarity index 96% rename from src/sat/cadical/constrain.cpp rename to src/sat/cadical/cadical_constrain.cpp index e7f7b7ef9..47f14e3d9 100644 --- a/src/sat/cadical/constrain.cpp +++ b/src/sat/cadical/cadical_constrain.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { void Internal::constrain (int lit) { @@ -62,3 +66,5 @@ void Internal::reset_constraint () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/contract.cpp b/src/sat/cadical/cadical_contract.cpp similarity index 86% rename from src/sat/cadical/contract.cpp rename to src/sat/cadical/cadical_contract.cpp index 8547d0306..7485ff4aa 100644 --- a/src/sat/cadical/contract.cpp +++ b/src/sat/cadical/cadical_contract.cpp @@ -1,7 +1,11 @@ -#ifndef NCONTRACTS +#include "global.h" + +#ifndef CADICAL_NCONTRACTS #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { void fatal_message_start (); @@ -24,4 +28,6 @@ void require_solver_pointer_to_be_non_zero (const void *ptr, } // namespace CaDiCaL +ABC_NAMESPACE_IMPL_END + #endif diff --git a/src/sat/cadical/cover.cpp b/src/sat/cadical/cadical_cover.cpp similarity index 93% rename from src/sat/cadical/cover.cpp rename to src/sat/cadical/cadical_cover.cpp index bd8781630..0d3253e98 100644 --- a/src/sat/cadical/cover.cpp +++ b/src/sat/cadical/cadical_cover.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -52,10 +56,10 @@ inline void Internal::cover_push_extension (int lit, Coveror &coveror) { bool found = false; for (const auto &other : coveror.covered) if (lit == other) - assert (!found), found = true; + CADICAL_assert (!found), found = true; else coveror.extend.push_back (other); - assert (found); + CADICAL_assert (found); (void) found; } @@ -63,11 +67,11 @@ inline void Internal::cover_push_extension (int lit, Coveror &coveror) { inline void Internal::covered_literal_addition (int lit, Coveror &coveror) { require_mode (COVER); - assert (level == 1); + CADICAL_assert (level == 1); cover_push_extension (lit, coveror); for (const auto &other : coveror.intersection) { LOG ("covered literal addition %d", other); - assert (!vals[other]), assert (!vals[-other]); + CADICAL_assert (!vals[other]), CADICAL_assert (!vals[-other]); set_val (other, -1); coveror.covered.push_back (other); coveror.added.push_back (other); @@ -81,9 +85,9 @@ inline void Internal::covered_literal_addition (int lit, Coveror &coveror) { inline void Internal::asymmetric_literal_addition (int lit, Coveror &coveror) { require_mode (COVER); - assert (level == 1); + CADICAL_assert (level == 1); LOG ("initial asymmetric literal addition %d", lit); - assert (!vals[lit]), assert (!vals[-lit]); + CADICAL_assert (!vals[lit]), CADICAL_assert (!vals[-lit]); set_val (lit, -1); coveror.added.push_back (lit); coveror.alas++; @@ -101,7 +105,7 @@ bool Internal::cover_propagate_asymmetric (int lit, Clause *ignore, Coveror &coveror) { require_mode (COVER); stats.propagations.cover++; - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); bool subsumed = false; LOG ("asymmetric literal propagation of %d", lit); Watches &ws = watches (lit); @@ -141,12 +145,12 @@ bool Internal::cover_propagate_asymmetric (int lit, Clause *ignore, k++; if (v < 0) { k = lits + 2; - assert (w.clause->pos <= size); + CADICAL_assert (w.clause->pos <= size); while (k != middle && (v = val (r = *k)) < 0) k++; } w.clause->pos = k - lits; - assert (lits + 2 <= k), assert (k <= w.clause->end ()); + CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ()); if (v > 0) j[-1].blit = r; else if (!v) { @@ -156,10 +160,10 @@ bool Internal::cover_propagate_asymmetric (int lit, Clause *ignore, watch_literal (r, lit, w.clause); j--; } else if (!u) { - assert (v < 0); + CADICAL_assert (v < 0); asymmetric_literal_addition (-other, coveror); } else { - assert (u < 0), assert (v < 0); + CADICAL_assert (u < 0), CADICAL_assert (v < 0); LOG (w.clause, "found subsuming"); subsumed = true; break; @@ -181,7 +185,7 @@ bool Internal::cover_propagate_asymmetric (int lit, Clause *ignore, bool Internal::cover_propagate_covered (int lit, Coveror &coveror) { require_mode (COVER); - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); if (frozen (lit)) { LOG ("no covered propagation on frozen literal %d", lit); return false; @@ -190,7 +194,7 @@ bool Internal::cover_propagate_covered (int lit, Coveror &coveror) { stats.propagations.cover++; LOG ("covered propagation of %d", lit); - assert (coveror.intersection.empty ()); + CADICAL_assert (coveror.intersection.empty ()); Occs &os = occs (-lit); const auto end = os.end (); @@ -237,7 +241,7 @@ bool Internal::cover_propagate_covered (int lit, Coveror &coveror) { const signed char tmp = val (other); if (tmp < 0) continue; - assert (!tmp); + CADICAL_assert (!tmp); coveror.intersection.push_back (other); mark (other); } @@ -254,7 +258,7 @@ bool Internal::cover_propagate_covered (int lit, Coveror &coveror) { signed char tmp = val (other); if (tmp < 0) continue; - assert (!tmp); + CADICAL_assert (!tmp); tmp = marked (other); if (tmp > 0) unmark (other); @@ -267,7 +271,7 @@ bool Internal::cover_propagate_covered (int lit, Coveror &coveror) { for (auto k = j; k != end; k++) { const int other = *j++ = *k; const int tmp = marked (other); - assert (tmp >= 0); + CADICAL_assert (tmp >= 0); if (tmp) j--, unmark (other); // remove marked and unmark it else @@ -298,7 +302,7 @@ bool Internal::cover_propagate_covered (int lit, Coveror &coveror) { bool res = false; if (first) { LOG ("all resolution candidates with %d blocked", -lit); - assert (coveror.intersection.empty ()); + CADICAL_assert (coveror.intersection.empty ()); cover_push_extension (lit, coveror); res = true; } else if (coveror.intersection.empty ()) { @@ -323,7 +327,7 @@ bool Internal::cover_propagate_covered (int lit, Coveror &coveror) { bool Internal::cover_clause (Clause *c, Coveror &coveror) { require_mode (COVER); - assert (!c->garbage); + CADICAL_assert (!c->garbage); LOG (c, "trying covered clauses elimination on"); bool satisfied = false; @@ -337,11 +341,11 @@ bool Internal::cover_clause (Clause *c, Coveror &coveror) { return false; } - assert (coveror.added.empty ()); - assert (coveror.extend.empty ()); - assert (coveror.covered.empty ()); + CADICAL_assert (coveror.added.empty ()); + CADICAL_assert (coveror.extend.empty ()); + CADICAL_assert (coveror.covered.empty ()); - assert (!level); + CADICAL_assert (!level); level = 1; LOG ("assuming literals of candidate clause"); for (const auto &lit : *c) { @@ -379,7 +383,7 @@ bool Internal::cover_clause (Clause *c, Coveror &coveror) { bool already_pushed = false; int64_t last_id = 0; LOG (c, "covered tautological"); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); LOG (coveror.extend, "extension = "); for (const auto &other : coveror.extend) { if (!prev) { @@ -450,7 +454,7 @@ bool Internal::cover_clause (Clause *c, Coveror &coveror) { // Backtrack and 'unassign' all literals. - assert (level == 1); + CADICAL_assert (level == 1); for (const auto &lit : coveror.added) set_val (lit, 0); level = 0; @@ -505,12 +509,12 @@ int64_t Internal::cover_round () { // First connect all clauses and find all not yet tried clauses. // -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t untried = 0; #endif // for (auto c : clauses) { - assert (!c->frozen); + CADICAL_assert (!c->frozen); if (c->garbage) continue; if (c->redundant) @@ -539,7 +543,7 @@ int64_t Internal::cover_round () { if (c->covered) continue; schedule.push_back (c); -#ifndef QUIET +#ifndef CADICAL_QUIET untried++; #endif } @@ -561,7 +565,7 @@ int64_t Internal::cover_round () { continue; if (c->size > opts.covermaxclslim) continue; - assert (c->covered); + CADICAL_assert (c->covered); c->covered = false; schedule.push_back (c); } @@ -589,7 +593,7 @@ int64_t Internal::cover_round () { stable_sort (schedule.begin (), schedule.end (), clause_covered_or_smaller ()); -#ifndef QUIET +#ifndef CADICAL_QUIET const size_t scheduled = schedule.size (); PHASE ("cover", stats.cover.count, "scheduled %zd clauses %.0f%% with %" PRId64 " untried %.0f%%", @@ -621,7 +625,7 @@ int64_t Internal::cover_round () { covered++; } -#ifndef QUIET +#ifndef CADICAL_QUIET const size_t remain = schedule.size (); const size_t tried = scheduled - remain; PHASE ("cover", stats.cover.count, @@ -687,11 +691,11 @@ bool Internal::cover () { LOG ("propagating units before covered clause elimination " "results in empty clause"); learn_empty_clause (); - assert (unsat); + CADICAL_assert (unsat); } reset_watches (); } - assert (unsat || propagated == trail.size ()); + CADICAL_assert (unsat || propagated == trail.size ()); int64_t covered = cover_round (); @@ -702,3 +706,5 @@ bool Internal::cover () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/decide.cpp b/src/sat/cadical/cadical_decide.cpp similarity index 94% rename from src/sat/cadical/decide.cpp rename to src/sat/cadical/cadical_decide.cpp index ca56ccdd7..0874cffd7 100644 --- a/src/sat/cadical/decide.cpp +++ b/src/sat/cadical/cadical_decide.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { // This function determines the next decision variable on the queue, without @@ -65,7 +69,7 @@ int Internal::decide_phase (int idx, bool target) { phase = phases.saved[idx]; // The following should not be necessary and in some version we had even - // a hard 'COVER' assertion here to check for this. Unfortunately it + // a hard 'COVER' CADICAL_assertion here to check for this. Unfortunately it // triggered for some users and we could not get to the root cause of // 'phase' still not being set here. The logic for phase and target // saving is pretty complex, particularly in combination with local @@ -100,7 +104,7 @@ bool Internal::satisfied () { return false; if (num_assigned < (size_t) max_var) return false; - assert (num_assigned == (size_t) max_var); + CADICAL_assert (num_assigned == (size_t) max_var); if (propagated < trail.size ()) return false; size_t assigned = num_assigned; @@ -120,12 +124,12 @@ bool Internal::better_decision (int lit, int other) { // that not all variables are assigned. int Internal::decide () { - assert (!satisfied ()); + CADICAL_assert (!satisfied ()); START (decide); int res = 0; if ((size_t) level < assumptions.size ()) { const int lit = assumptions[level]; - assert (assumed (lit)); + CADICAL_assert (assumed (lit)); const signed char tmp = val (lit); if (tmp < 0) { LOG ("assumption %d falsified", lit); @@ -147,7 +151,7 @@ int Internal::decide () { const size_t size_constraint = constraint.size (); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG unsigned sum = 0; for (auto lit : constraint) sum += lit; @@ -172,7 +176,7 @@ int Internal::decide () { break; } - assert (!tmp); + CADICAL_assert (!tmp); LOG ("constraint literal %d unassigned", lit); if (!unassigned_lit || better_decision (lit, unassigned_lit)) @@ -218,10 +222,10 @@ int Internal::decide () { } } -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG for (auto lit : constraint) sum -= lit; - assert (!sum); // Checksum of literal should not change! + CADICAL_assert (!sum); // Checksum of literal should not change! #endif } else { @@ -250,3 +254,5 @@ int Internal::decide () { return res; } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/decompose.cpp b/src/sat/cadical/cadical_decompose.cpp similarity index 89% rename from src/sat/cadical/decompose.cpp rename to src/sat/cadical/cadical_decompose.cpp index 7e44ab742..9081321b4 100644 --- a/src/sat/cadical/decompose.cpp +++ b/src/sat/cadical/cadical_decompose.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { void Internal::decompose_analyze_binary_chain (DFS *dfs, int from) { @@ -10,7 +14,7 @@ void Internal::decompose_analyze_binary_chain (DFS *dfs, int from) { Clause *reason = from_dfs.parent; if (!reason) return; - assert (reason->size == 2); + CADICAL_assert (reason->size == 2); mini_chain.push_back (reason->id); int other = reason->literals[0]; other = other == from ? -reason->literals[1] : -other; @@ -30,7 +34,7 @@ vector<Clause *> Internal::decompose_analyze_binary_clauses (DFS *dfs, Clause *reason = from_dfs.parent; while (reason) { result.push_back (reason); - assert (reason->size == 2); + CADICAL_assert (reason->size == 2); int other = reason->literals[0]; other = other == from ? -reason->literals[1] : -other; Flags &f = flags (other); @@ -48,8 +52,8 @@ vector<Clause *> Internal::decompose_analyze_binary_clauses (DFS *dfs, void Internal::decompose_conflicting_scc_lrat (DFS *dfs, vector<int> &scc) { if (!lrat) return; - assert (lrat_chain.empty ()); - assert (mini_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (mini_chain.empty ()); for (auto &lit : scc) { Flags &f = flags (lit); if (f.seen) @@ -67,10 +71,10 @@ void Internal::decompose_conflicting_scc_lrat (DFS *dfs, vector<int> &scc) { void Internal::build_lrat_for_clause ( const vector<vector<Clause *>> &dfs_chains, bool invert) { - assert (lrat); + CADICAL_assert (lrat); LOG ("building chain for not subsumed clause"); - assert (lrat_chain.empty ()); - assert (sign_marked.empty ()); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (sign_marked.empty ()); // build chain for each replaced literal for (const auto lit : clause) { auto other = lit; @@ -82,7 +86,7 @@ void Internal::build_lrat_for_clause ( lrat_chain.push_back (id); continue; } - assert (mini_chain.empty ()); + CADICAL_assert (mini_chain.empty ()); for (auto p : dfs_chains[vlit (other)]) { if (marked_decomposed (other)) continue; @@ -116,7 +120,7 @@ void Internal::build_lrat_for_clause ( void Internal::clear_sign_marked_literals () { LOG ("clearing %zd marked literals", sign_marked.size ()); for (const auto &lit : sign_marked) { - // assert (marked_signed (lit)); violated on purpose in factor + // CADICAL_assert (marked_signed (lit)); violated on purpose in factor unmark_decomposed (lit); } sign_marked.clear (); @@ -136,7 +140,7 @@ bool Internal::decompose_round () { if (terminated_asynchronously ()) return false; - assert (!level); + CADICAL_assert (!level); START_SIMPLIFIER (decompose, DECOMP); @@ -158,7 +162,7 @@ bool Internal::decompose_round () { } int substituted = 0; -#ifndef QUIET +#ifndef CADICAL_QUIET int non_trivial_sccs = 0; int before = active (); #endif @@ -180,17 +184,17 @@ bool Internal::decompose_round () { if (dfs[vlit (root)].min == TRAVERSED) continue; // skip traversed LOG ("new dfs search starting at root %d", root); - assert (work.empty ()); - assert (scc.empty ()); + CADICAL_assert (work.empty ()); + CADICAL_assert (scc.empty ()); work.push_back (root); while (!unsat && !work.empty ()) { int parent = work.back (); DFS &parent_dfs = dfs[vlit (parent)]; if (parent_dfs.min == TRAVERSED) { // skip traversed - assert (reprs[vlit (parent)]); + CADICAL_assert (reprs[vlit (parent)]); work.pop_back (); } else { - assert (!reprs[vlit (parent)]); + CADICAL_assert (!reprs[vlit (parent)]); // Go over all implied literals, thus need to iterate over all // binary watched clauses with the negation of 'parent'. @@ -236,12 +240,12 @@ bool Internal::decompose_round () { // becomes unsatisfiable. if (lrat) { - assert (analyzed.empty ()); + CADICAL_assert (analyzed.empty ()); int other, first = 0; bool conflicting = false; size_t j = scc.size (); do { - assert (j > 0); + CADICAL_assert (j > 0); other = scc[--j]; if (!first || vlit (other) < vlit (first)) first = other; @@ -256,7 +260,7 @@ bool Internal::decompose_round () { analyzed.push_back (other); } while (other != parent); - assert (!conflicting || first > 0); + CADICAL_assert (!conflicting || first > 0); vector<int> to_justify; if (conflicting) { LOG ("conflicting scc simulating up at %d", parent); @@ -287,13 +291,13 @@ bool Internal::decompose_round () { } int other, repr = parent; -#ifndef QUIET +#ifndef CADICAL_QUIET int size = 0; #endif - assert (!scc.empty ()); + CADICAL_assert (!scc.empty ()); size_t j = scc.size (); do { - assert (j > 0); + CADICAL_assert (j > 0); other = scc[--j]; if (other == -parent) { LOG ("both %d and %d in one SCC", parent, -parent); @@ -307,17 +311,17 @@ bool Internal::decompose_round () { mini_chain.clear (); } assign_unit (parent); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG bool ok = #endif propagate (); - assert (!ok); + CADICAL_assert (!ok); learn_empty_clause (); lrat_chain.clear (); } else { if (abs (other) < abs (repr)) repr = other; -#ifndef QUIET +#ifndef CADICAL_QUIET size++; #endif } @@ -325,11 +329,11 @@ bool Internal::decompose_round () { if (unsat) break; -#ifndef QUIET +#ifndef CADICAL_QUIET LOG ("SCC of representative %d of size %d", repr, size); #endif do { - assert (!scc.empty ()); + CADICAL_assert (!scc.empty ()); other = scc.back (); scc.pop_back (); dfs[vlit (other)].min = TRAVERSED; @@ -344,7 +348,7 @@ bool Internal::decompose_round () { LOG ("literal %d in SCC of %d", other, repr); if (!lrat) continue; - assert (mini_chain.empty ()); + CADICAL_assert (mini_chain.empty ()); Flags &f = flags (repr); f.seen = true; analyzed.push_back (repr); @@ -355,7 +359,7 @@ bool Internal::decompose_round () { clear_analyzed_literals (); } while (other != parent); -#ifndef QUIET +#ifndef CADICAL_QUIET if (size > 1) non_trivial_sccs++; #endif @@ -372,7 +376,7 @@ bool Internal::decompose_round () { } else { // pre-fix dfs_idx++; - assert (dfs_idx < TRAVERSED); + CADICAL_assert (dfs_idx < TRAVERSED); parent_dfs.idx = parent_dfs.min = dfs_idx; scc.push_back (parent); @@ -429,17 +433,17 @@ bool Internal::decompose_round () { int other = reprs[vlit (idx)]; if (other == idx) continue; - assert (!flags (other).eliminated ()); - assert (!flags (other).substituted ()); + CADICAL_assert (!flags (other).eliminated ()); + CADICAL_assert (!flags (other).substituted ()); LOG ("marking equivalence of %d and %d", idx, other); - assert (clause.empty ()); - assert (lrat_chain.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (lrat_chain.empty ()); clause.push_back (other); clause.push_back (-idx); if (lrat) { build_lrat_for_clause (dfs_chains); - assert (!lrat_chain.empty ()); + CADICAL_assert (!lrat_chain.empty ()); } const int64_t id1 = ++clause_id; @@ -454,13 +458,13 @@ bool Internal::decompose_round () { lrat_chain.clear (); clause.clear (); - assert (clause.empty ()); - assert (lrat_chain.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (lrat_chain.empty ()); clause.push_back (idx); clause.push_back (-other); if (lrat) { build_lrat_for_clause (dfs_chains); - assert (!lrat_chain.empty ()); + CADICAL_assert (!lrat_chain.empty ()); } const int64_t id2 = ++clause_id; if (proof) { @@ -480,7 +484,7 @@ bool Internal::decompose_round () { // should be substituted by their representative. size_t clauses_size = clauses.size (); -#ifndef QUIET +#ifndef CADICAL_QUIET size_t garbage = 0, replaced = 0; #endif for (size_t i = 0; substituted && !unsat && i < clauses_size; i++) { @@ -497,7 +501,7 @@ bool Internal::decompose_round () { if (j == size) continue; -#ifndef QUIET +#ifndef CADICAL_QUIET replaced++; #endif LOG (c, "first substituted literal %d in", substituted); @@ -507,9 +511,9 @@ bool Internal::decompose_round () { // literal occurs in both phases or is assigned to true the clause is // satisfied and can be marked as garbage. - assert (clause.empty ()); - assert (lrat_chain.empty ()); - assert (analyzed.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (analyzed.empty ()); bool satisfied = false; for (int k = 0; !satisfied && k < size; k++) { @@ -544,7 +548,7 @@ bool Internal::decompose_round () { if (other == lit) continue; int64_t id = decompose_ids[vlit (-lit)]; - assert (id); + CADICAL_assert (id); lrat_chain.push_back (id); continue; } else if (tmp > 0) @@ -562,7 +566,7 @@ bool Internal::decompose_round () { if (!lrat) continue; int64_t id = decompose_ids[vlit (-lit)]; - assert (id); + CADICAL_assert (id); lrat_chain.push_back (id); } } @@ -574,7 +578,7 @@ bool Internal::decompose_round () { if (satisfied) { LOG (c, "satisfied after substitution (postponed)"); postponed_garbage.push_back (c); -#ifndef QUIET +#ifndef CADICAL_QUIET garbage++; #endif } else if (!clause.size ()) { @@ -585,7 +589,7 @@ bool Internal::decompose_round () { assign_unit (clause[0]); mark_garbage (c); new_unit = true; -#ifndef QUIET +#ifndef CADICAL_QUIET garbage++; #endif } else if (c->literals[0] != clause[0] || c->literals[1] != clause[1]) { @@ -594,16 +598,16 @@ bool Internal::decompose_round () { new_binary_clause = true; size_t d_clause_idx = clauses.size (); Clause *d = new_clause_as (c); - assert (clauses[d_clause_idx] == d); + CADICAL_assert (clauses[d_clause_idx] == d); clauses[d_clause_idx] = c; clauses[i] = d; mark_garbage (c); -#ifndef QUIET +#ifndef CADICAL_QUIET garbage++; #endif } else { LOG ("simply shrinking clause since watches did not change"); - assert (c->size > 2); + CADICAL_assert (c->size > 2); if (!c->redundant) mark_removed (c); if (proof) { @@ -624,11 +628,11 @@ bool Internal::decompose_round () { (void) shrink_clause (c, l); } else if (likely_to_be_kept_clause (c)) mark_added (c); - // we have shrunken c->size to l so even though there is an assertion + // we have shrunken c->size to l so even though there is an CADICAL_assertion // for c->size > 2 at the beginning of this else block, the new size // can be 2 now. if (c->size == 2) { // cheaper to update only new binary clauses - assert (new_binary_clause); + CADICAL_assert (new_binary_clause); update_watch_size (watches (c->literals[0]), c->literals[1], c); update_watch_size (watches (c->literals[1]), c->literals[0], c); } @@ -637,7 +641,7 @@ bool Internal::decompose_round () { while (!clause.empty ()) { int lit = clause.back (); clause.pop_back (); - assert (marked (lit) > 0); + CADICAL_assert (marked (lit) > 0); unmark (lit); } lrat_chain.clear (); @@ -653,9 +657,9 @@ bool Internal::decompose_round () { if (!id1) continue; int other = reprs[vlit (idx)]; - assert (other != idx); - assert (!flags (other).eliminated ()); - assert (!flags (other).substituted ()); + CADICAL_assert (other != idx); + CADICAL_assert (!flags (other).eliminated ()); + CADICAL_assert (!flags (other).substituted ()); clause.push_back (other); clause.push_back (-idx); @@ -702,8 +706,8 @@ bool Internal::decompose_round () { int other = reprs[vlit (idx)]; if (other == idx) continue; - assert (!flags (other).eliminated ()); - assert (!flags (other).substituted ()); + CADICAL_assert (!flags (other).eliminated ()); + CADICAL_assert (!flags (other).substituted ()); if (!flags (other).fixed ()) mark_substituted (idx); } @@ -731,3 +735,5 @@ void Internal::decompose () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/deduplicate.cpp b/src/sat/cadical/cadical_deduplicate.cpp similarity index 94% rename from src/sat/cadical/deduplicate.cpp rename to src/sat/cadical/cadical_deduplicate.cpp index 08a5d01c6..49d26a571 100644 --- a/src/sat/cadical/deduplicate.cpp +++ b/src/sat/cadical/cadical_deduplicate.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { // Equivalent literal substitution in 'decompose' and shrinking in 'subsume' @@ -29,8 +33,8 @@ void Internal::mark_duplicated_binary_clauses_as_garbage () { START_SIMPLIFIER (deduplicate, DEDUP); stats.deduplications++; - assert (!level); - assert (watching ()); + CADICAL_assert (!level); + CADICAL_assert (watching ()); vector<int> stack; // To save marked literals and unmark them later. @@ -49,7 +53,7 @@ void Internal::mark_duplicated_binary_clauses_as_garbage () { const int lit = sign * idx; // Consider all literals. - assert (stack.empty ()); + CADICAL_assert (stack.empty ()); Watches &ws = watches (lit); // We are removing references to garbage clause. Thus no 'auto'. @@ -81,7 +85,7 @@ void Internal::mark_duplicated_binary_clauses_as_garbage () { if (!c->redundant) { watch_iterator k; for (k = ws.begin ();; k++) { - assert (k != i); + CADICAL_assert (k != i); if (!k->binary ()) continue; if (k->blit != other) @@ -109,12 +113,12 @@ void Internal::mark_duplicated_binary_clauses_as_garbage () { unit = lit; if (lrat) { // taken from fradical - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); lrat_chain.push_back (c->id); // We've forgotten where the other binary clause is, so go find // it again for (watch_iterator k = ws.begin ();; k++) { - assert (k != i); + CADICAL_assert (k != i); if (!k->binary ()) continue; if (k->blit != -other) @@ -168,3 +172,5 @@ void Internal::mark_duplicated_binary_clauses_as_garbage () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/definition.cpp b/src/sat/cadical/cadical_definition.cpp similarity index 77% rename from src/sat/cadical/definition.cpp rename to src/sat/cadical/cadical_definition.cpp index 1b704b2b9..7c8fe4fbb 100644 --- a/src/sat/cadical/definition.cpp +++ b/src/sat/cadical/cadical_definition.cpp @@ -1,10 +1,14 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { #define INVALID_LIT UINT_MAX -// functions below are passed to kitten +// functions below are passed to cadical_kitten // struct definition_extractor { Eliminator *eliminator; @@ -17,7 +21,7 @@ struct definition_extractor { extern "C" { -// used to extract definitions from kitten +// used to extract definitions from cadical_kitten // static void traverse_definition_core (void *state, unsigned id) { definition_extractor *extractor = (definition_extractor *) state; @@ -27,17 +31,17 @@ static void traverse_definition_core (void *state, unsigned id) { Eliminator *eliminator = extractor->eliminator; const size_t size_clauses0 = clauses0.size (); const size_t size_clauses1 = clauses1.size (); - assert (size_clauses0 <= UINT_MAX); + CADICAL_assert (size_clauses0 <= UINT_MAX); unsigned sign; - assert (id < size_clauses0 + size_clauses1); + CADICAL_assert (id < size_clauses0 + size_clauses1); if (id < size_clauses0) { clause = clauses0[id]; sign = 1; } else { unsigned tmp = id - size_clauses0; -#ifndef NDEBUG - assert (size_clauses1 <= UINT_MAX); - assert (tmp < size_clauses1); +#ifndef CADICAL_NDEBUG + CADICAL_assert (size_clauses1 <= UINT_MAX); + CADICAL_assert (tmp < size_clauses1); #endif clause = clauses1[tmp]; sign = 2; @@ -73,7 +77,7 @@ static void traverse_one_sided_core_lemma (void *state, bool learned, for (const unsigned *p = lits; p != end; p++) pc.literals.push_back (internal->citten2lit (*p)); // conversion proof_clauses.push_back (pc); - assert (proof); + CADICAL_assert (proof); proof->add_derived_clause (pc.id, true, pc.literals, pc.chain); } else { internal->assign_unit (unit); @@ -97,28 +101,28 @@ static void traverse_one_sided_core_lemma_with_lrat ( const vector<Clause *> &clauses0 = extractor->clauses[0]; const vector<Clause *> &clauses1 = extractor->clauses[1]; vector<proof_clause> &proof_clauses = eliminator->proof_clauses; - if (!learned) { // remember clauses for mapping to kitten internal - assert (size); - assert (!chain_size); + if (!learned) { // remember clauses for mapping to cadical_kitten internal + CADICAL_assert (size); + CADICAL_assert (!chain_size); proof_clause pc; pc.cid = cid; pc.learned = false; const size_t size_clauses0 = clauses0.size (); - assert (size_clauses0 <= UINT_MAX); + CADICAL_assert (size_clauses0 <= UINT_MAX); if (id < size_clauses0) { pc.id = clauses0[id]->id; } else { unsigned tmp = id - size_clauses0; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG const size_t size_clauses1 = clauses1.size (); - assert (size_clauses1 <= UINT_MAX); - assert (tmp < size_clauses1); + CADICAL_assert (size_clauses1 <= UINT_MAX); + CADICAL_assert (tmp < size_clauses1); #endif pc.id = clauses1[tmp]->id; } proof_clauses.push_back (pc); } else { // actually add to proof - assert (chain_size); + CADICAL_assert (chain_size); if (size) { proof_clause pc; pc.id = ++(internal->clause_id); @@ -136,14 +140,14 @@ static void traverse_one_sided_core_lemma_with_lrat ( break; } } - assert (id); + CADICAL_assert (id); pc.chain.push_back (id); } proof_clauses.push_back (pc); - assert (proof); + CADICAL_assert (proof); proof->add_derived_clause (pc.id, true, pc.literals, pc.chain); } else { // learn unit finish proof - assert (internal->lrat_chain.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); for (const unsigned *p = chain + chain_size; p != chain; p--) { int64_t id = 0; for (const auto &cpc : proof_clauses) { @@ -152,11 +156,11 @@ static void traverse_one_sided_core_lemma_with_lrat ( break; } } - assert (id); + CADICAL_assert (id); internal->lrat_chain.push_back (id); } internal->assign_unit (unit); - assert (internal->lrat_chain.empty ()); + CADICAL_assert (internal->lrat_chain.empty ()); for (const auto &pc : proof_clauses) { if (pc.learned) proof->delete_clause (pc.id, true, pc.literals); @@ -171,8 +175,8 @@ static void traverse_one_sided_core_lemma_with_lrat ( // Code ported from kissat. Kitten (and kissat) use unsigned representation // for literals whereas CaDiCaL uses signed representation. Conversion is // necessary for communication using lit2citten and citten2lit. -// This code is called in elim and kitten is initialized beforehand. -// To avoid confusion all cadical interal definitions with kitten are called +// This code is called in elim and cadical_kitten is initialized beforehand. +// To avoid confusion all cadical interal definitions with cadical_kitten are called // citten. // void Internal::find_definition (Eliminator &eliminator, int lit) { @@ -184,9 +188,9 @@ void Internal::find_definition (Eliminator &eliminator, int lit) { return; if (!eliminator.gates.empty ()) return; - assert (!val (lit)); - assert (!level); - assert (citten); + CADICAL_assert (!val (lit)); + CADICAL_assert (!level); + CADICAL_assert (citten); const int not_lit = -lit; definition_extractor extractor; extractor.lit = lit; @@ -200,9 +204,9 @@ void Internal::find_definition (Eliminator &eliminator, int lit) { const unsigned except = sign ? lit2citten (not_lit) : lit2citten (lit); for (auto c : extractor.clauses[sign]) { // to avoid copying the literals of c in their unsigned - // representation we instead implement the translation in kitten + // representation we instead implement the translation in cadical_kitten if (!c->garbage) { - LOG (c, "adding to kitten"); + LOG (c, "adding to cadical_kitten"); citten_clause_with_id_and_exception (citten, exported, c->size, c->literals, except); } @@ -211,42 +215,42 @@ void Internal::find_definition (Eliminator &eliminator, int lit) { } stats.definitions_checked++; const size_t limit = opts.elimdefticks; - kitten_set_ticks_limit (citten, limit); - int status = kitten_solve (citten); + cadical_kitten_set_ticks_limit (citten, limit); + int status = cadical_kitten_solve (citten); if (!exported) goto ABORT; if (status == 20) { LOG ("sub-solver result UNSAT shows definition exists"); uint64_t learned; - unsigned reduced = kitten_compute_clausal_core (citten, &learned); + unsigned reduced = cadical_kitten_compute_clausal_core (citten, &learned); LOG ("1st sub-solver core of size %u original clauses out of %u", reduced, exported); for (int i = 2; i <= opts.elimdefcores; i++) { - kitten_shrink_to_clausal_core (citten); - kitten_shuffle_clauses (citten); - kitten_set_ticks_limit (citten, 10 * limit); - int tmp = kitten_solve (citten); - assert (!tmp || tmp == 20); + cadical_kitten_shrink_to_clausal_core (citten); + cadical_kitten_shuffle_clauses (citten); + cadical_kitten_set_ticks_limit (citten, 10 * limit); + int tmp = cadical_kitten_solve (citten); + CADICAL_assert (!tmp || tmp == 20); if (!tmp) { LOG ("aborting core extraction"); goto ABORT; } -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG unsigned previous = reduced; #endif - reduced = kitten_compute_clausal_core (citten, &learned); + reduced = cadical_kitten_compute_clausal_core (citten, &learned); LOG ("%d sub-solver core of size %u original clauses out of %u", i, reduced, exported); - assert (reduced <= previous); -#if not defined(LOGGING) && defined(NDEBUG) + CADICAL_assert (reduced <= previous); +#if not defined(LOGGING) && defined(CADICAL_NDEBUG) (void) reduced; #endif } stats.definitions_extracted++; eliminator.gatetype = DEF; eliminator.definition_unit = 0; - kitten_traverse_core_ids (citten, &extractor, traverse_definition_core); - assert (eliminator.definition_unit); + cadical_kitten_traverse_core_ids (citten, &extractor, traverse_definition_core); + CADICAL_assert (eliminator.definition_unit); int unit = 0; if (eliminator.definition_unit == 2) { unit = not_lit; @@ -261,11 +265,11 @@ void Internal::find_definition (Eliminator &eliminator, int lit) { if (proof) { if (lrat) { extractor.unit = unit; - kitten_trace_core (citten, &extractor, + cadical_kitten_trace_core (citten, &extractor, traverse_one_sided_core_lemma_with_lrat); } else { extractor.unit = unit; - kitten_traverse_core_clauses (citten, &extractor, + cadical_kitten_traverse_core_clauses (citten, &extractor, traverse_one_sided_core_lemma); } } else @@ -276,8 +280,10 @@ void Internal::find_definition (Eliminator &eliminator, int lit) { ABORT: LOG ("sub-solver failed to show that definition exists"); } - stats.definition_ticks += kitten_current_ticks (citten); + stats.definition_ticks += cadical_kitten_current_ticks (citten); return; } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/drattracer.cpp b/src/sat/cadical/cadical_drattracer.cpp similarity index 87% rename from src/sat/cadical/drattracer.cpp rename to src/sat/cadical/cadical_drattracer.cpp index 057c2d141..eb73b4c95 100644 --- a/src/sat/cadical/drattracer.cpp +++ b/src/sat/cadical/cadical_drattracer.cpp @@ -1,12 +1,16 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ DratTracer::DratTracer (Internal *i, File *f, bool b) : internal (i), file (f), binary (b) -#ifndef QUIET +#ifndef CADICAL_QUIET , added (0), deleted (0) #endif @@ -28,17 +32,17 @@ DratTracer::~DratTracer () { /*------------------------------------------------------------------------*/ inline void DratTracer::put_binary_zero () { - assert (binary); - assert (file); + CADICAL_assert (binary); + CADICAL_assert (file); file->put ((unsigned char) 0); } inline void DratTracer::put_binary_lit (int lit) { - assert (binary); - assert (file); - assert (lit != INT_MIN); + CADICAL_assert (binary); + CADICAL_assert (file); + CADICAL_assert (lit != INT_MIN); unsigned idx = abs (lit); - assert (idx < (1u << 31)); + CADICAL_assert (idx < (1u << 31)); unsigned x = 2u * idx + (lit < 0); unsigned char ch; while (x & ~0x7f) { @@ -90,7 +94,7 @@ void DratTracer::add_derived_clause (int64_t, bool, return; LOG ("DRAT TRACER tracing addition of derived clause"); drat_add_clause (clause); -#ifndef QUIET +#ifndef CADICAL_QUIET added++; #endif } @@ -100,7 +104,7 @@ void DratTracer::delete_clause (int64_t, bool, const vector<int> &clause) { return; LOG ("DRAT TRACER tracing deletion of clause"); drat_delete_clause (clause); -#ifndef QUIET +#ifndef CADICAL_QUIET deleted++; #endif } @@ -109,7 +113,7 @@ void DratTracer::delete_clause (int64_t, bool, const vector<int> &clause) { bool DratTracer::closed () { return file->closed (); } -#ifndef QUIET +#ifndef CADICAL_QUIET void DratTracer::print_statistics () { uint64_t bytes = file->bytes (); @@ -125,9 +129,9 @@ void DratTracer::print_statistics () { #endif void DratTracer::close (bool print) { - assert (!closed ()); + CADICAL_assert (!closed ()); file->close (); -#ifndef QUIET +#ifndef CADICAL_QUIET if (print) { MSG ("DRAT proof file '%s' closed", file->name ()); print_statistics (); @@ -138,9 +142,9 @@ void DratTracer::close (bool print) { } void DratTracer::flush (bool print) { - assert (!closed ()); + CADICAL_assert (!closed ()); file->flush (); -#ifndef QUIET +#ifndef CADICAL_QUIET if (print) { MSG ("DRAT proof file '%s' flushed", file->name ()); print_statistics (); @@ -151,3 +155,5 @@ void DratTracer::flush (bool print) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/elim.cpp b/src/sat/cadical/cadical_elim.cpp similarity index 93% rename from src/sat/cadical/elim.cpp rename to src/sat/cadical/cadical_elim.cpp index 9a5157436..951dfee74 100644 --- a/src/sat/cadical/elim.cpp +++ b/src/sat/cadical/cadical_elim.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -19,7 +23,7 @@ namespace CaDiCaL { /*------------------------------------------------------------------------*/ inline double Internal::compute_elim_score (unsigned lit) { - assert (1 <= lit), assert (lit <= (unsigned) max_var); + 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]; @@ -65,7 +69,7 @@ bool Internal::ineliminating () { if (!preprocessing && !opts.inprocessing) return false; if (preprocessing) - assert (lim.preprocessing); + CADICAL_assert (lim.preprocessing); // Respect (increasing) conflict limit. // @@ -90,7 +94,7 @@ bool Internal::ineliminating () { void Internal::elim_update_added_clause (Eliminator &eliminator, Clause *c) { - assert (!c->redundant); + CADICAL_assert (!c->redundant); ElimSchedule &schedule = eliminator.schedule; for (const auto &lit : *c) { if (!active (lit)) @@ -111,7 +115,7 @@ void Internal::elim_update_removed_lit (Eliminator &eliminator, int lit) { if (frozen (lit)) return; int64_t &score = noccs (lit); - assert (score > 0); + CADICAL_assert (score > 0); score--; const int idx = abs (lit); ElimSchedule &schedule = eliminator.schedule; @@ -125,11 +129,11 @@ void Internal::elim_update_removed_lit (Eliminator &eliminator, int lit) { void Internal::elim_update_removed_clause (Eliminator &eliminator, Clause *c, int except) { - assert (!c->redundant); + CADICAL_assert (!c->redundant); for (const auto &lit : *c) { if (lit == except) continue; - assert (lit != -except); + CADICAL_assert (lit != -except); elim_update_removed_lit (eliminator, lit); } } @@ -141,14 +145,14 @@ void Internal::elim_update_removed_clause (Eliminator &eliminator, // and also marks clauses satisfied by those units as garbage immediately. void Internal::elim_propagate (Eliminator &eliminator, int root) { - assert (val (root) > 0); + CADICAL_assert (val (root) > 0); vector<int> work; size_t i = 0; work.push_back (root); while (i < work.size ()) { int lit = work[i++]; LOG ("elimination propagation of %d", lit); - assert (val (lit) > 0); + CADICAL_assert (val (lit) > 0); const Occs &ns = occs (-lit); for (const auto &c : ns) { if (c->garbage) @@ -212,12 +216,12 @@ void Internal::elim_on_the_fly_self_subsumption (Eliminator &eliminator, LOG (c, "pivot %d on-the-fly self-subsuming resolution", pivot); stats.elimotfstr++; stats.strengthened++; - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); for (const auto &lit : *c) { if (lit == pivot) continue; const signed char tmp = val (lit); - assert (tmp <= 0); + CADICAL_assert (tmp <= 0); if (tmp < 0) continue; clause.push_back (lit); @@ -266,8 +270,8 @@ bool Internal::resolve_clauses (Eliminator &eliminator, Clause *c, int pivot, Clause *d, const bool propagate_eagerly) { - assert (!c->redundant); - assert (!d->redundant); + CADICAL_assert (!c->redundant); + CADICAL_assert (!d->redundant); stats.elimres++; @@ -278,8 +282,8 @@ bool Internal::resolve_clauses (Eliminator &eliminator, Clause *c, swap (c, d); } - assert (!level); - assert (clause.empty ()); + CADICAL_assert (!level); + CADICAL_assert (clause.empty ()); int satisfied = 0; // Contains this satisfying literal. int tautological = 0; // Clashing literal if tautological. @@ -295,7 +299,7 @@ bool Internal::resolve_clauses (Eliminator &eliminator, Clause *c, s++; continue; } - assert (lit != -pivot); + CADICAL_assert (lit != -pivot); const signed char tmp = val (lit); if (tmp > 0) { satisfied = lit; @@ -334,7 +338,7 @@ bool Internal::resolve_clauses (Eliminator &eliminator, Clause *c, t++; continue; } - assert (lit != pivot); + CADICAL_assert (lit != pivot); signed char tmp = val (lit); if (tmp > 0) { satisfied = lit; @@ -356,7 +360,7 @@ bool Internal::resolve_clauses (Eliminator &eliminator, Clause *c, } else if (!tmp) clause.push_back (lit), t++; else - assert (tmp > 0), t++; + CADICAL_assert (tmp > 0), t++; } clear_analyzed_literals (); @@ -405,15 +409,15 @@ bool Internal::resolve_clauses (Eliminator &eliminator, Clause *c, } LOG (clause, "resolvent"); - assert (!lrat || !lrat_chain.empty ()); + CADICAL_assert (!lrat || !lrat_chain.empty ()); // Double self-subsuming resolution. The clauses 'c' and 'd' are // identical except for the pivot which occurs in different phase. The // resolvent subsumes both antecedents. if (s > size && t > size) { - assert (s == size + 1); - assert (t == size + 1); + CADICAL_assert (s == size + 1); + CADICAL_assert (t == size + 1); clause.clear (); // LRAT is c + d (+ eventual units) elim_on_the_fly_self_subsumption (eliminator, c, pivot); @@ -430,7 +434,7 @@ bool Internal::resolve_clauses (Eliminator &eliminator, Clause *c, // with 'pivot' removed and then marking 'c' as garbage. if (s > size) { - assert (s == size + 1); + CADICAL_assert (s == size + 1); clause.clear (); // LRAT is c + d (+ eventual units) elim_on_the_fly_self_subsumption (eliminator, c, pivot); @@ -440,7 +444,7 @@ bool Internal::resolve_clauses (Eliminator &eliminator, Clause *c, // Same single self-subsuming resolution situation, but only for 'd'. if (t > size) { - assert (t == size + 1); + CADICAL_assert (t == size + 1); clause.clear (); // LRAT is c + d (+ eventual units) -> same. elim_on_the_fly_self_subsumption (eliminator, d, -pivot); @@ -469,8 +473,8 @@ bool Internal::elim_resolvents_are_bounded (Eliminator &eliminator, stats.elimtried++; - assert (!unsat); - assert (active (pivot)); + CADICAL_assert (!unsat); + CADICAL_assert (active (pivot)); const Occs &ps = occs (pivot); const Occs &ns = occs (-pivot); @@ -492,11 +496,11 @@ bool Internal::elim_resolvents_are_bounded (Eliminator &eliminator, int64_t resolvents = 0; // Non-tautological resolvents. for (const auto &c : ps) { - assert (!c->redundant); + CADICAL_assert (!c->redundant); if (c->garbage) continue; for (const auto &d : ns) { - assert (!d->redundant); + CADICAL_assert (!d->redundant); if (d->garbage) continue; if (!resolve_gates && substitute && c->gate == d->gate) @@ -562,13 +566,13 @@ inline void Internal::elim_add_resolvents (Eliminator &eliminator, stats.eliminated_def++; break; default: - assert (eliminator.gatetype == NO); + CADICAL_assert (eliminator.gatetype == NO); } LOG ("adding all resolvents on %d", pivot); - assert (!val (pivot)); - assert (!flags (pivot).eliminated ()); + CADICAL_assert (!val (pivot)); + CADICAL_assert (!flags (pivot).eliminated ()); const Occs &ps = occs (pivot); const Occs &ns = occs (-pivot); @@ -589,7 +593,7 @@ inline void Internal::elim_add_resolvents (Eliminator &eliminator, continue; if (!resolve_clauses (eliminator, c, pivot, d, false)) continue; - assert (!lrat || !lrat_chain.empty ()); + CADICAL_assert (!lrat || !lrat_chain.empty ()); Clause *r = new_resolved_irredundant_clause (); elim_update_added_clause (eliminator, r); eliminator.enqueue (r); @@ -611,28 +615,28 @@ inline void Internal::elim_add_resolvents (Eliminator &eliminator, void Internal::mark_eliminated_clauses_as_garbage ( Eliminator &eliminator, int pivot, bool &deleted_binary_clause) { - assert (!unsat); + CADICAL_assert (!unsat); LOG ("marking irredundant clauses with %d as garbage", pivot); const int64_t substitute = eliminator.gates.size (); if (substitute) LOG ("pushing %" PRId64 " gate clauses on extension stack", substitute); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG int64_t pushed = 0; #endif Occs &ps = occs (pivot); for (const auto &c : ps) { if (c->garbage) continue; - assert (!c->redundant); + CADICAL_assert (!c->redundant); if (!substitute || c->gate) { if (proof) proof->weaken_minus (c); if (c->size == 2) deleted_binary_clause = true; external->push_clause_on_extension_stack (c, pivot); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG pushed++; #endif } @@ -647,14 +651,14 @@ void Internal::mark_eliminated_clauses_as_garbage ( for (const auto &d : ns) { if (d->garbage) continue; - assert (!d->redundant); + CADICAL_assert (!d->redundant); if (!substitute || d->gate) { if (proof) proof->weaken_minus (d); if (d->size == 2) deleted_binary_clause = true; external->push_clause_on_extension_stack (d, -pivot); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG pushed++; #endif } @@ -664,7 +668,7 @@ void Internal::mark_eliminated_clauses_as_garbage ( erase_occs (ns); if (substitute) - assert (pushed <= substitute); + CADICAL_assert (pushed <= substitute); // Unfortunately, we can not use the trick by Niklas Soerensson anymore, // which avoids saving all clauses on the extension stack. This would @@ -679,7 +683,7 @@ void Internal::try_to_eliminate_variable (Eliminator &eliminator, int pivot, if (!active (pivot)) return; - assert (!frozen (pivot)); + CADICAL_assert (!frozen (pivot)); // First flush garbage clauses. // @@ -693,17 +697,17 @@ void Internal::try_to_eliminate_variable (Eliminator &eliminator, int pivot, LOG ("pivot %d occurs positively %" PRId64 " times and negatively %" PRId64 " times", pivot, pos, neg); - assert (!eliminator.schedule.contains (abs (pivot))); - assert (pos <= neg); + CADICAL_assert (!eliminator.schedule.contains (abs (pivot))); + CADICAL_assert (pos <= neg); if (pos && neg > opts.elimocclim) { LOG ("too many occurrences thus not eliminated %d", pivot); - assert (!eliminator.schedule.contains (abs (pivot))); + CADICAL_assert (!eliminator.schedule.contains (abs (pivot))); return; } LOG ("trying to eliminate %d", pivot); - assert (!flags (pivot).eliminated ()); + CADICAL_assert (!flags (pivot).eliminated ()); // Sort occurrence lists, such that shorter clauses come first. Occs &ps = occs (pivot); @@ -766,15 +770,15 @@ void Internal:: int Internal::elim_round (bool &completed, bool &deleted_binary_clause) { - assert (opts.elim); - assert (!unsat); + CADICAL_assert (opts.elim); + CADICAL_assert (!unsat); START_SIMPLIFIER (elim, ELIM); stats.elimrounds++; int64_t marked_before = last.elim.marked; last.elim.marked = stats.mark.elim; - assert (!level); + CADICAL_assert (!level); int64_t resolution_limit; @@ -813,7 +817,7 @@ int Internal::elim_round (bool &completed, bool &deleted_binary_clause) { else if (tmp < 0) falsified = true; else - assert (active (lit)); + CADICAL_assert (active (lit)); } if (satisfied) mark_garbage (c); // forces more precise counts @@ -832,7 +836,7 @@ int Internal::elim_round (bool &completed, bool &deleted_binary_clause) { Eliminator eliminator (this); ElimSchedule &schedule = eliminator.schedule; - assert (schedule.empty ()); + CADICAL_assert (schedule.empty ()); // Now find elimination candidates which occurred in clauses removed since // the last time we ran bounded variable elimination, which in turned @@ -851,7 +855,7 @@ int Internal::elim_round (bool &completed, bool &deleted_binary_clause) { schedule.shrink (); -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t scheduled = schedule.size (); #endif @@ -867,7 +871,7 @@ int Internal::elim_round (bool &completed, bool &deleted_binary_clause) { if (active (lit)) occs (lit).push_back (c); -#ifndef QUIET +#ifndef CADICAL_QUIET const int64_t old_resolutions = stats.elimres; #endif const int old_eliminated = stats.all.eliminated; @@ -882,7 +886,7 @@ int Internal::elim_round (bool &completed, bool &deleted_binary_clause) { // schedule is updated dynamically and variables are potentially // rescheduled to be tried again if they occur in a removed clause. // -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t tried = 0; #endif while (!unsat && !terminated_asynchronously () && @@ -891,7 +895,7 @@ int Internal::elim_round (bool &completed, bool &deleted_binary_clause) { schedule.pop_front (); flags (idx).elim = false; try_to_eliminate_variable (eliminator, idx, deleted_binary_clause); -#ifndef QUIET +#ifndef CADICAL_QUIET tried++; #endif if (stats.garbage.literals <= garbage_limit) @@ -931,7 +935,7 @@ int Internal::elim_round (bool &completed, bool &deleted_binary_clause) { mark_redundant_clauses_with_eliminated_variables_as_garbage (); int eliminated = stats.all.eliminated - old_eliminated; -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t resolutions = stats.elimres - old_resolutions; PHASE ("elim-round", stats.elimrounds, "eliminated %d variables %.0f%% in %" PRId64 " resolutions", @@ -1000,13 +1004,13 @@ void Internal::increase_elimination_bound () { void Internal::init_citten () { if (!opts.elimdef) return; - assert (!citten); - citten = kitten_init (); + CADICAL_assert (!citten); + citten = cadical_kitten_init (); } void Internal::reset_citten () { if (citten) { - kitten_release (citten); + cadical_kitten_release (citten); citten = 0; } } @@ -1029,11 +1033,11 @@ void Internal::elim (bool update_limits) { "starting at most %d elimination rounds", opts.elimrounds); if (external_prop) { - assert (!level); + CADICAL_assert (!level); private_steps = true; } -#ifndef QUIET +#ifndef CADICAL_QUIET int old_active_variables = active (); int old_eliminated = stats.all.eliminated; #endif @@ -1062,13 +1066,13 @@ void Internal::elim (bool update_limits) { bool phase_complete = false, deleted_binary_clause = false; int round = 1; -#ifndef QUIET +#ifndef CADICAL_QUIET int eliminated = 0; #endif bool round_complete = false; while (!unsat && !phase_complete && !terminated_asynchronously ()) { -#ifndef QUIET +#ifndef CADICAL_QUIET int eliminated = #endif elim_round (round_complete, deleted_binary_clause); @@ -1076,7 +1080,7 @@ void Internal::elim (bool update_limits) { if (!round_complete) { PHASE ("elim-phase", stats.elimphases, "last round %d incomplete %s", round, eliminated ? "but successful" : "and unsuccessful"); - assert (!phase_complete); + CADICAL_assert (!phase_complete); break; } @@ -1085,7 +1089,7 @@ void Internal::elim (bool update_limits) { round - 1, eliminated ? "though last round successful" : "last round unsuccessful anyhow"); - assert (!phase_complete); + CADICAL_assert (!phase_complete); break; } @@ -1105,7 +1109,7 @@ void Internal::elim (bool update_limits) { PHASE ("elim-phase", stats.elimphases, "no new variable elimination candidates"); - assert (round_complete); + CADICAL_assert (round_complete); phase_complete = true; } @@ -1145,14 +1149,14 @@ void Internal::elim (bool update_limits) { if (phase_complete) increase_elimination_bound (); -#ifndef QUIET +#ifndef CADICAL_QUIET eliminated = stats.all.eliminated - old_eliminated; PHASE ("elim-phase", stats.elimphases, "eliminated %d variables %.2f%%", eliminated, percent (eliminated, old_active_variables)); #endif if (external_prop) { - assert (!level); + CADICAL_assert (!level); private_steps = false; } @@ -1170,3 +1174,5 @@ void Internal::elim (bool update_limits) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/elimfast.cpp b/src/sat/cadical/cadical_elimfast.cpp similarity index 91% rename from src/sat/cadical/elimfast.cpp rename to src/sat/cadical/cadical_elimfast.cpp index 6806a0637..90dacf8c0 100644 --- a/src/sat/cadical/elimfast.cpp +++ b/src/sat/cadical/cadical_elimfast.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -30,7 +34,7 @@ int64_t Internal::flush_elimfast_occs (int lit) { break; } if (++res > occslim) { - assert (opts.fastelimbound < 0 || res == failed); + CADICAL_assert (opts.fastelimbound < 0 || res == failed); break; } } @@ -54,13 +58,13 @@ int64_t Internal::flush_elimfast_occs (int lit) { bool Internal::elimfast_resolvents_are_bounded (Eliminator &eliminator, int pivot) { - assert (eliminator.gates.empty ()); - assert (!eliminator.definition_unit); + CADICAL_assert (eliminator.gates.empty ()); + CADICAL_assert (!eliminator.definition_unit); stats.elimtried++; - assert (!unsat); - assert (active (pivot)); + CADICAL_assert (!unsat); + CADICAL_assert (active (pivot)); const Occs &ps = occs (pivot); const Occs &ns = occs (-pivot); @@ -95,11 +99,11 @@ bool Internal::elimfast_resolvents_are_bounded (Eliminator &eliminator, int64_t resolvents = 0; // Non-tautological resolvents. for (const auto &c : ps) { - assert (!c->redundant); + CADICAL_assert (!c->redundant); if (c->garbage) continue; for (const auto &d : ns) { - assert (!d->redundant); + CADICAL_assert (!d->redundant); if (d->garbage) continue; if (resolve_clauses (eliminator, c, pivot, d, true)) { @@ -139,13 +143,13 @@ bool Internal::elimfast_resolvents_are_bounded (Eliminator &eliminator, inline void Internal::elimfast_add_resolvents (Eliminator &eliminator, int pivot) { - assert (eliminator.gates.empty ()); - assert (!eliminator.definition_unit); + CADICAL_assert (eliminator.gates.empty ()); + CADICAL_assert (!eliminator.definition_unit); LOG ("adding all resolvents on %d", pivot); - assert (!val (pivot)); - assert (!flags (pivot).eliminated ()); + CADICAL_assert (!val (pivot)); + CADICAL_assert (!flags (pivot).eliminated ()); const Occs &ps = occs (pivot); const Occs &ns = occs (-pivot); @@ -164,7 +168,7 @@ inline void Internal::elimfast_add_resolvents (Eliminator &eliminator, continue; if (!resolve_clauses (eliminator, c, pivot, d, false)) continue; - assert (!lrat || !lrat_chain.empty ()); + CADICAL_assert (!lrat || !lrat_chain.empty ()); Clause *r = new_resolved_irredundant_clause (); elim_update_added_clause (eliminator, r); eliminator.enqueue (r); @@ -188,7 +192,7 @@ void Internal::try_to_fasteliminate_variable (Eliminator &eliminator, if (!active (pivot)) return; - assert (!frozen (pivot)); + CADICAL_assert (!frozen (pivot)); // First flush garbage clauses and check limits. @@ -197,14 +201,14 @@ void Internal::try_to_fasteliminate_variable (Eliminator &eliminator, int64_t pos = flush_elimfast_occs (pivot); if (pos > bound) { LOG ("too many occurrences thus not eliminated %d", pivot); - assert (!eliminator.schedule.contains (abs (pivot))); + CADICAL_assert (!eliminator.schedule.contains (abs (pivot))); return; } int64_t neg = flush_elimfast_occs (-pivot); if (neg > bound) { LOG ("too many occurrences thus not eliminated %d", -pivot); - assert (!eliminator.schedule.contains (abs (pivot))); + CADICAL_assert (!eliminator.schedule.contains (abs (pivot))); return; } @@ -222,11 +226,11 @@ void Internal::try_to_fasteliminate_variable (Eliminator &eliminator, " times and negatively %" PRId64 " times", pivot, pos, neg); - assert (!eliminator.schedule.contains (abs (pivot))); - assert (pos <= neg); + CADICAL_assert (!eliminator.schedule.contains (abs (pivot))); + CADICAL_assert (pos <= neg); LOG ("trying to eliminate %d", pivot); - assert (!flags (pivot).eliminated ()); + CADICAL_assert (!flags (pivot).eliminated ()); // Sort occurrence lists, such that shorter clauses come first. Occs &ps = occs (pivot); @@ -264,14 +268,14 @@ void Internal::try_to_fasteliminate_variable (Eliminator &eliminator, int Internal::elimfast_round (bool &completed, bool &deleted_binary_clause) { - assert (opts.fastelim); - assert (!unsat); + CADICAL_assert (opts.fastelim); + CADICAL_assert (!unsat); START_SIMPLIFIER (fastelim, ELIM); stats.elimfastrounds++; - assert (!level); + CADICAL_assert (!level); int64_t resolution_limit; @@ -310,7 +314,7 @@ int Internal::elimfast_round (bool &completed, else if (tmp < 0) falsified = true; else - assert (active (lit)); + CADICAL_assert (active (lit)); } if (satisfied) mark_garbage (c); // forces more precise counts @@ -329,7 +333,7 @@ int Internal::elimfast_round (bool &completed, Eliminator eliminator (this); ElimSchedule &schedule = eliminator.schedule; - assert (schedule.empty ()); + CADICAL_assert (schedule.empty ()); // Now find elimination candidates which occurred in clauses removed since // the last time we ran bounded variable elimination, which in turned @@ -348,7 +352,7 @@ int Internal::elimfast_round (bool &completed, schedule.shrink (); -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t scheduled = schedule.size (); #endif @@ -364,7 +368,7 @@ int Internal::elimfast_round (bool &completed, if (active (lit)) occs (lit).push_back (c); -#ifndef QUIET +#ifndef CADICAL_QUIET const int64_t old_resolutions = stats.elimres; #endif const int old_eliminated = stats.all.eliminated; @@ -379,7 +383,7 @@ int Internal::elimfast_round (bool &completed, // schedule is updated dynamically and variables are potentially // rescheduled to be tried again if they occur in a removed clause. // -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t tried = 0; #endif while (!unsat && !terminated_asynchronously () && @@ -388,7 +392,7 @@ int Internal::elimfast_round (bool &completed, schedule.pop_front (); flags (idx).elim = false; try_to_fasteliminate_variable (eliminator, idx, deleted_binary_clause); -#ifndef QUIET +#ifndef CADICAL_QUIET tried++; #endif if (stats.garbage.literals <= garbage_limit) @@ -419,7 +423,7 @@ int Internal::elimfast_round (bool &completed, int eliminated = stats.all.eliminated - old_eliminated; stats.all.fasteliminated += eliminated; -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t resolutions = stats.elimres - old_resolutions; PHASE ("fastelim-round", stats.elimfastrounds, "eliminated %d variables %.0f%% in %" PRId64 " resolutions", @@ -451,11 +455,11 @@ void Internal::elimfast () { "starting at most %d elimination rounds", opts.fastelimrounds); if (external_prop) { - assert (!level); + CADICAL_assert (!level); private_steps = true; } -#ifndef QUIET +#ifndef CADICAL_QUIET int old_active_variables = active (); int old_eliminated = stats.all.eliminated; #endif @@ -476,13 +480,13 @@ void Internal::elimfast () { bool phase_complete = false, deleted_binary_clause = false; int round = 1; -#ifndef QUIET +#ifndef CADICAL_QUIET int eliminated = 0; #endif bool round_complete = false; while (!unsat && !phase_complete && !terminated_asynchronously ()) { -#ifndef QUIET +#ifndef CADICAL_QUIET int eliminated = #endif elimfast_round (round_complete, deleted_binary_clause); @@ -491,7 +495,7 @@ void Internal::elimfast () { PHASE ("fastelim-phase", stats.elimphases, "last round %d incomplete %s", round, eliminated ? "but successful" : "and unsuccessful"); - assert (!phase_complete); + CADICAL_assert (!phase_complete); break; } @@ -500,7 +504,7 @@ void Internal::elimfast () { round - 1, eliminated ? "though last round successful" : "last round unsuccessful anyhow"); - assert (!phase_complete); + CADICAL_assert (!phase_complete); break; } @@ -516,7 +520,7 @@ void Internal::elimfast () { PHASE ("fastelim-phase", stats.elimphases, "no new variable elimination candidates"); - assert (round_complete); + CADICAL_assert (round_complete); phase_complete = true; } @@ -554,7 +558,7 @@ void Internal::elimfast () { } } -#ifndef QUIET +#ifndef CADICAL_QUIET eliminated = stats.all.eliminated - old_eliminated; PHASE ("fastelim-phase", stats.elimphases, "eliminated %d variables %.2f%%", eliminated, @@ -562,9 +566,11 @@ void Internal::elimfast () { #endif if (external_prop) { - assert (!level); + CADICAL_assert (!level); private_steps = false; } } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/ema.cpp b/src/sat/cadical/cadical_ema.cpp similarity index 96% rename from src/sat/cadical/ema.cpp rename to src/sat/cadical/cadical_ema.cpp index ef6581482..e8faf5d76 100644 --- a/src/sat/cadical/ema.cpp +++ b/src/sat/cadical/cadical_ema.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { // Updating an exponential moving average is placed here since we want to @@ -70,10 +74,10 @@ void EMA::update (Internal *internal, double y, const char *name) { double new_exp, div, new_value; if (old_exp) { new_exp = old_exp * beta; - assert (new_exp < 1); + CADICAL_assert (new_exp < 1); exp = new_exp; div = 1 - new_exp; - assert (div > 0); + CADICAL_assert (div > 0); new_value = new_biased / div; } else { new_value = new_biased; @@ -93,3 +97,5 @@ void EMA::update (Internal *internal, double y, const char *name) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/extend.cpp b/src/sat/cadical/cadical_extend.cpp similarity index 86% rename from src/sat/cadical/extend.cpp rename to src/sat/cadical/cadical_extend.cpp index 26f081360..c549e90fa 100644 --- a/src/sat/cadical/extend.cpp +++ b/src/sat/cadical/cadical_extend.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { void External::push_zero_on_extension_stack () { @@ -16,18 +20,18 @@ void External::push_id_on_extension_stack (int64_t id) { } void External::push_clause_literal_on_extension_stack (int ilit) { - assert (ilit); + CADICAL_assert (ilit); const int elit = internal->externalize (ilit); - assert (elit); + CADICAL_assert (elit); extension.push_back (elit); LOG ("pushing clause literal %d on extension stack (internal %d)", elit, ilit); } void External::push_witness_literal_on_extension_stack (int ilit) { - assert (ilit); + CADICAL_assert (ilit); const int elit = internal->externalize (ilit); - assert (elit); + CADICAL_assert (elit); extension.push_back (elit); LOG ("pushing witness literal %d on extension stack (internal %d)", elit, ilit); @@ -77,10 +81,10 @@ void External::push_binary_clause_on_extension_stack (int64_t id, int pivot, void External::push_external_clause_and_witness_on_extension_stack ( const vector<int> &c, const vector<int> &w, int64_t id) { - assert (id); + CADICAL_assert (id); extension.push_back (0); for (const auto &elit : w) { - assert (elit != INT_MIN); + CADICAL_assert (elit != INT_MIN); init (abs (elit)); extension.push_back (elit); mark (witness, elit); @@ -92,7 +96,7 @@ void External::push_external_clause_and_witness_on_extension_stack ( extension.push_back (lower_bits); extension.push_back (0); for (const auto &elit : c) { - assert (elit != INT_MIN); + CADICAL_assert (elit != INT_MIN); init (abs (elit)); extension.push_back (elit); } @@ -112,7 +116,7 @@ void External::push_external_clause_and_witness_on_extension_stack ( void External::extend () { - assert (!extended); + CADICAL_assert (!extended); START (extend); internal->stats.extensions++; @@ -120,7 +124,7 @@ void External::extend () { "mapping internal %d assignments to %d assignments", internal->max_var, max_var); -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t updated = 0; #endif for (unsigned i = 1; i <= (unsigned) max_var; i++) { @@ -130,7 +134,7 @@ void External::extend () { if (i >= vals.size ()) vals.resize (i + 1, false); vals[i] = (internal->val (ilit) > 0); -#ifndef QUIET +#ifndef CADICAL_QUIET updated++; #endif } @@ -141,50 +145,50 @@ void External::extend () { extension.size ()); const auto begin = extension.begin (); auto i = extension.end (); -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t flipped = 0; #endif while (i != begin) { bool satisfied = false; int lit; - assert (i != begin); + CADICAL_assert (i != begin); while ((lit = *--i)) { if (satisfied) continue; if (ival (lit) == lit) satisfied = true; - assert (i != begin); + CADICAL_assert (i != begin); } - assert (i != begin); + CADICAL_assert (i != begin); LOG ("id=%" PRId64, ((int64_t) *i << 32) + *(i - 1)); - assert (*i || *(i - 1)); + CADICAL_assert (*i || *(i - 1)); --i; - assert (i != begin); + CADICAL_assert (i != begin); --i; - assert (i != begin); - assert (!*i); + CADICAL_assert (i != begin); + CADICAL_assert (!*i); --i; - assert (i != begin); + CADICAL_assert (i != begin); if (satisfied) while (*--i) - assert (i != begin); + CADICAL_assert (i != begin); else { while ((lit = *--i)) { const int tmp = ival (lit); // not 'signed char'!!! if (tmp != lit) { LOG ("flipping blocking literal %d", lit); - assert (lit); - assert (lit != INT_MIN); + CADICAL_assert (lit); + CADICAL_assert (lit != INT_MIN); size_t idx = abs (lit); if (idx >= vals.size ()) vals.resize (idx + 1, false); vals[idx] = !vals[idx]; internal->stats.extended++; -#ifndef QUIET +#ifndef CADICAL_QUIET flipped++; #endif } - assert (i != begin); + CADICAL_assert (i != begin); } } } @@ -207,14 +211,14 @@ bool External::traverse_witnesses_backward (WitnessIterator &it) { int lit; while ((lit = *--i)) clause.push_back (lit); - assert (!lit); + CADICAL_assert (!lit); --i; const int64_t id = ((int64_t) * (i - 1) << 32) + static_cast<int64_t> (*i); - assert (id); + CADICAL_assert (id); i -= 2; - assert (!*i); - assert (i != begin); + CADICAL_assert (!*i); + CADICAL_assert (i != begin); while ((lit = *--i)) witness.push_back (lit); reverse (clause.begin (), clause.end ()); @@ -237,18 +241,18 @@ bool External::traverse_witnesses_forward (WitnessIterator &it) { if (i != end) { int lit = *i++; do { - assert (!lit), (void) lit; + CADICAL_assert (!lit), (void) lit; while ((lit = *i++)) witness.push_back (lit); - assert (!lit); - assert (i != end); - assert (!*i); + CADICAL_assert (!lit); + CADICAL_assert (i != end); + CADICAL_assert (!*i); const int64_t id = ((int64_t) *i << 32) + static_cast<int64_t> (*(i + 1)); - assert (id > 0); + CADICAL_assert (id > 0); i += 3; - assert (*i); - assert (i != end); + CADICAL_assert (*i); + CADICAL_assert (i != end); while (i != end && (lit = *i++)) clause.push_back (lit); if (!it.witness (clause, witness, id)) @@ -279,3 +283,5 @@ void External::conclude_sat () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/external.cpp b/src/sat/cadical/cadical_external.cpp similarity index 90% rename from src/sat/cadical/external.cpp rename to src/sat/cadical/cadical_external.cpp index acb11a9d6..67d1f918d 100644 --- a/src/sat/cadical/external.cpp +++ b/src/sat/cadical/cadical_external.cpp @@ -1,14 +1,18 @@ +#include "global.h" + #include "internal.hpp" #include <cstdint> +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { External::External (Internal *i) : internal (i), max_var (0), vsize (0), extended (false), concluded (false), terminator (0), learner (0), fixed_listener (0), propagator (0), solution (0), vars (max_var) { - assert (internal); - assert (!internal->external); + CADICAL_assert (internal); + CADICAL_assert (!internal->external); internal->external = this; } @@ -19,7 +23,7 @@ External::~External () { void External::enlarge (int new_max_var) { - assert (!extended); + CADICAL_assert (!extended); size_t new_vsize = vsize ? 2 * vsize : 1 + (size_t) new_max_var; while (new_vsize <= (size_t) new_max_var) @@ -29,7 +33,7 @@ void External::enlarge (int new_max_var) { } void External::init (int new_max_var, bool extension) { - assert (!extended); + CADICAL_assert (!extended); if (new_max_var <= max_var) return; int new_vars = new_max_var - max_var; @@ -40,31 +44,31 @@ void External::init (int new_max_var, bool extension) { enlarge (new_max_var); LOG ("initialized %d external variables", new_vars); if (!max_var) { - assert (e2i.empty ()); + CADICAL_assert (e2i.empty ()); e2i.push_back (0); ext_units.push_back (0); ext_units.push_back (0); ext_flags.push_back (0); ervars.push_back (0); - assert (internal->i2e.empty ()); + CADICAL_assert (internal->i2e.empty ()); internal->i2e.push_back (0); } else { - assert (e2i.size () == (size_t) max_var + 1); - assert (internal->i2e.size () == (size_t) old_internal_max_var + 1); + CADICAL_assert (e2i.size () == (size_t) max_var + 1); + CADICAL_assert (internal->i2e.size () == (size_t) old_internal_max_var + 1); } unsigned iidx = old_internal_max_var + 1, eidx; for (eidx = max_var + 1u; eidx <= (unsigned) new_max_var; eidx++, iidx++) { LOG ("mapping external %u to internal %u", eidx, iidx); - assert (e2i.size () == eidx); + CADICAL_assert (e2i.size () == eidx); e2i.push_back (iidx); ext_units.push_back (0); ext_units.push_back (0); ext_flags.push_back (0); ervars.push_back (0); internal->i2e.push_back (eidx); - assert (internal->i2e[iidx] == (int) eidx); - assert (e2i[eidx] == (int) iidx); + CADICAL_assert (internal->i2e[iidx] == (int) eidx); + CADICAL_assert (e2i[eidx] == (int) iidx); } if (extension) internal->stats.variables_extension += new_vars; @@ -75,9 +79,9 @@ void External::init (int new_max_var, bool extension) { if (internal->opts.checkfrozen) if (new_max_var >= (int64_t) moltentab.size ()) moltentab.resize (1 + (size_t) new_max_var, false); - assert (iidx == (size_t) new_internal_max_var + 1); - assert (eidx == (size_t) new_max_var + 1); - assert (ext_units.size () == (size_t) new_max_var * 2 + 2); + CADICAL_assert (iidx == (size_t) new_internal_max_var + 1); + CADICAL_assert (eidx == (size_t) new_max_var + 1); + CADICAL_assert (ext_units.size () == (size_t) new_max_var * 2 + 2); max_var = new_max_var; } @@ -116,7 +120,7 @@ void External::reset_limits () { internal->reset_limits (); } int External::internalize (int elit, bool extension) { int ilit; if (elit) { - assert (elit != INT_MIN); + CADICAL_assert (elit != INT_MIN); const int eidx = abs (elit); if (extension && eidx <= max_var) FATAL ("can not add a definition for an already used variable %d", @@ -125,27 +129,27 @@ int External::internalize (int elit, bool extension) { init (eidx, extension); } if (extension) { - assert (ervars.size () > (size_t) eidx); + CADICAL_assert (ervars.size () > (size_t) eidx); ervars[eidx] = true; } ilit = e2i[eidx]; if (elit < 0) ilit = -ilit; if (!ilit) { - assert (internal->max_var < INT_MAX); + CADICAL_assert (internal->max_var < INT_MAX); ilit = internal->max_var + 1u; internal->init_vars (ilit); e2i[eidx] = ilit; LOG ("mapping external %d to internal %d", eidx, ilit); e2i[eidx] = ilit; internal->i2e.push_back (eidx); - assert (internal->i2e[ilit] == eidx); - assert (e2i[eidx] == ilit); + CADICAL_assert (internal->i2e[ilit] == eidx); + CADICAL_assert (e2i[eidx] == ilit); if (elit < 0) ilit = -ilit; } if (internal->opts.checkfrozen) { - assert (eidx < (int64_t) moltentab.size ()); + CADICAL_assert (eidx < (int64_t) moltentab.size ()); if (moltentab[eidx]) FATAL ("can not reuse molten literal %d", eidx); } @@ -155,7 +159,7 @@ int External::internalize (int elit, bool extension) { else if (f.status != Flags::ACTIVE && f.status != Flags::FIXED) internal->reactivate (ilit); if (!marked (tainted, elit) && marked (witness, -elit)) { - assert (!internal->opts.checkfrozen); + CADICAL_assert (!internal->opts.checkfrozen); LOG ("marking tainted %d", elit); mark (tainted, elit); } @@ -165,7 +169,7 @@ int External::internalize (int elit, bool extension) { } void External::add (int elit) { - assert (elit != INT_MIN); + CADICAL_assert (elit != INT_MIN); reset_extended (); bool forgettable = false; @@ -186,7 +190,7 @@ void External::add (int elit) { } const int ilit = internalize (elit); - assert (!elit == !ilit); + CADICAL_assert (!elit == !ilit); // The external literals of the new clause must be saved for later // when the proof is printed during add_original_lit (0) @@ -195,7 +199,7 @@ void External::add (int elit) { if (internal->lrat) { // actually find unit of -elit (flips elit < 0) unsigned eidx = (elit > 0) + 2u * (unsigned) abs (elit); - assert ((size_t) eidx < ext_units.size ()); + CADICAL_assert ((size_t) eidx < ext_units.size ()); const int64_t id = ext_units[eidx]; bool added = ext_flags[abs (elit)]; if (id && !added) { @@ -221,21 +225,21 @@ void External::add (int elit) { } void External::assume (int elit) { - assert (elit); + CADICAL_assert (elit); reset_extended (); if (internal->proof) internal->proof->add_assumption (elit); assumptions.push_back (elit); const int ilit = internalize (elit); - assert (ilit); + CADICAL_assert (ilit); LOG ("assuming external %d as internal %d", elit, ilit); internal->assume (ilit); } bool External::flip (int elit) { - assert (elit); - assert (elit != INT_MIN); - assert (!propagator); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + CADICAL_assert (!propagator); int eidx = abs (elit); if (eidx > max_var) @@ -252,9 +256,9 @@ bool External::flip (int elit) { } bool External::flippable (int elit) { - assert (elit); - assert (elit != INT_MIN); - assert (!propagator); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + CADICAL_assert (!propagator); int eidx = abs (elit); if (eidx > max_var) @@ -268,8 +272,8 @@ bool External::flippable (int elit) { } bool External::failed (int elit) { - assert (elit); - assert (elit != INT_MIN); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); int eidx = abs (elit); if (eidx > max_var) return 0; @@ -286,10 +290,10 @@ void External::constrain (int elit) { LOG (constraint, "replacing previous constraint"); reset_constraint (); } - assert (elit != INT_MIN); + CADICAL_assert (elit != INT_MIN); reset_extended (); const int ilit = internalize (elit); - assert (!elit == !ilit); + CADICAL_assert (!elit == !ilit); if (elit) LOG ("adding external %d as internal %d to constraint", elit, ilit); else if (!elit && internal->proof) { @@ -304,15 +308,15 @@ bool External::failed_constraint () { } void External::phase (int elit) { - assert (elit); - assert (elit != INT_MIN); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); const int ilit = internalize (elit); internal->phase (ilit); } void External::unphase (int elit) { - assert (elit); - assert (elit != INT_MIN); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); int eidx = abs (elit); if (eidx > max_var) { UNUSED: @@ -341,15 +345,15 @@ void External::add_observed_var (int elit) { return; } - assert (elit); - assert (elit != INT_MIN); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); reset_extended (); // tainting! int eidx = abs (elit); if (eidx <= max_var && (marked (witness, elit) || marked (witness, -elit))) { LOG ("Error, only clean variables are allowed to become observed."); - assert (false); + CADICAL_assert (false); // TODO: here needs to come the taint and restore of the newly // observed variable. Restore_clauses must be called before continue. @@ -395,7 +399,7 @@ void External::add_observed_var (int elit) { unit); // internal add-observed-var had to backtrack to root-level already - assert (!internal->level); + CADICAL_assert (!internal->level); std::vector<int> assigned = {unit}; propagator->notify_assignment (assigned); @@ -425,7 +429,7 @@ void External::remove_observed_var (int elit) { void External::reset_observed_vars () { // Shouldn't be called if there is no connected propagator - assert (propagator); + CADICAL_assert (propagator); reset_extended (); internal->notified = 0; @@ -434,11 +438,11 @@ void External::reset_observed_vars () { if (!is_observed.size ()) return; - assert (!max_var || (size_t) max_var + 1 == is_observed.size ()); + CADICAL_assert (!max_var || (size_t) max_var + 1 == is_observed.size ()); for (auto elit : vars) { int eidx = abs (elit); - assert (eidx <= max_var); + CADICAL_assert (eidx <= max_var); if (is_observed[eidx]) { int ilit = internalize (elit); internal->remove_observed_var (ilit); @@ -450,8 +454,8 @@ void External::reset_observed_vars () { } bool External::observed (int elit) { - assert (elit); - assert (elit != INT_MIN); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); int eidx = abs (elit); if (eidx > max_var) return false; @@ -462,8 +466,8 @@ bool External::observed (int elit) { } bool External::is_witness (int elit) { - assert (elit); - assert (elit != INT_MIN); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); int eidx = abs (elit); if (eidx > max_var) return false; @@ -471,8 +475,8 @@ bool External::is_witness (int elit) { } bool External::is_decision (int elit) { - assert (elit); - assert (elit != INT_MIN); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); int eidx = abs (elit); if (eidx > max_var) return false; @@ -513,7 +517,7 @@ void External::implied (std::vector<int> &trailed) { trailed.clear(); for (const auto &ilit : ilit_implicants) { - assert (ilit); + CADICAL_assert (ilit); const int elit = internal->externalize (ilit); const int eidx = abs (elit); const bool is_extension_var = ervars[eidx]; @@ -578,7 +582,7 @@ void External::check_solve_result (int res) { void External::update_molten_literals () { if (!internal->opts.checkfrozen) return; - assert ((size_t) max_var + 1 == moltentab.size ()); + CADICAL_assert ((size_t) max_var + 1 == moltentab.size ()); #ifdef LOGGING int registered = 0, molten = 0; #endif @@ -667,9 +671,9 @@ void External::melt (int elit) { reset_extended (); int ilit = internalize (elit); unsigned eidx = vidx (elit); - assert (eidx < frozentab.size ()); + CADICAL_assert (eidx < frozentab.size ()); unsigned &ref = frozentab[eidx]; - assert (ref > 0); + CADICAL_assert (ref > 0); if (ref < UINT_MAX) { if (!--ref) { if (observed (elit)) { @@ -700,10 +704,10 @@ void External::check_assignment (int (External::*a) (int) const) { int value_idx = (this->*a) (idx); int value_neg_idx = (this->*a) (-idx); if (value_idx == idx) - assert (value_neg_idx == idx); + CADICAL_assert (value_neg_idx == idx); else { - assert (value_idx == -idx); - assert (value_neg_idx == -idx); + CADICAL_assert (value_idx == -idx); + CADICAL_assert (value_neg_idx == -idx); } if (value_idx != value_neg_idx) FATAL ("inconsistently assigned literals %d and %d", idx, -idx); @@ -714,7 +718,7 @@ void External::check_assignment (int (External::*a) (int) const) { bool satisfied = false; const auto end = original.end (); auto start = original.begin (), i = start; -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t count = 0; #endif for (; i != end; i++) { @@ -730,7 +734,7 @@ void External::check_assignment (int (External::*a) (int) const) { } satisfied = false; start = i + 1; -#ifndef QUIET +#ifndef CADICAL_QUIET count++; #endif } else if (!satisfied && (this->*a) (lit) == lit) @@ -747,7 +751,7 @@ void External::check_assignment (int (External::*a) (int) const) { break; presence_flag = true; satisfied = false; -#ifndef QUIET +#ifndef CADICAL_QUIET count++; #endif std::vector<int> literals; @@ -778,7 +782,7 @@ void External::check_assignment (int (External::*a) (int) const) { fatal_message_end (); } } -#ifndef QUIET +#ifndef CADICAL_QUIET VERBOSE (1, "satisfying assignment checked on %" PRId64 " clauses", count); #endif @@ -945,7 +949,7 @@ bool External::traverse_all_non_frozen_units_as_witnesses ( const int ilit = e2i[idx] * (tmp < 0 ? -1 : 1); // heurstically add + max_var to the id to avoid reusing ids const int64_t id = internal->lrat ? internal->unit_id (ilit) : 1; - assert (id); + CADICAL_assert (id); clause_and_witness.push_back (unit); if (!it.witness (clause_and_witness, clause_and_witness, id + max_var)) return false; @@ -972,8 +976,8 @@ void External::copy_flags (External &other) const { continue; if (!other.internal->active (other_ilit)) continue; - assert (this_ilit != INT_MIN); - assert (other_ilit != INT_MIN); + CADICAL_assert (this_ilit != INT_MIN); + CADICAL_assert (other_ilit != INT_MIN); const Flags &this_flags = this_ftab[abs (this_ilit)]; Flags &other_flags = other_ftab[abs (other_ilit)]; this_flags.copy (other_flags); @@ -983,7 +987,7 @@ void External::copy_flags (External &other) const { /*------------------------------------------------------------------------*/ void External::export_learned_empty_clause () { - assert (learner); + CADICAL_assert (learner); if (learner->learning (0)) { LOG ("exporting learned empty clause"); learner->learn (0); @@ -992,11 +996,11 @@ void External::export_learned_empty_clause () { } void External::export_learned_unit_clause (int ilit) { - assert (learner); + CADICAL_assert (learner); if (learner->learning (1)) { LOG ("exporting learned unit clause"); const int elit = internal->externalize (ilit); - assert (elit); + CADICAL_assert (elit); learner->learn (elit); learner->learn (0); } else @@ -1004,14 +1008,14 @@ void External::export_learned_unit_clause (int ilit) { } void External::export_learned_large_clause (const vector<int> &clause) { - assert (learner); + CADICAL_assert (learner); size_t size = clause.size (); - assert (size <= (unsigned) INT_MAX); + CADICAL_assert (size <= (unsigned) INT_MAX); if (learner->learning ((int) size)) { LOG ("exporting learned clause of size %zu", size); for (auto ilit : clause) { const int elit = internal->externalize (ilit); - assert (elit); + CADICAL_assert (elit); learner->learn (elit); } learner->learn (0); @@ -1020,3 +1024,5 @@ void External::export_learned_large_clause (const vector<int> &clause) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/external_propagate.cpp b/src/sat/cadical/cadical_external_propagate.cpp similarity index 92% rename from src/sat/cadical/external_propagate.cpp rename to src/sat/cadical/cadical_external_propagate.cpp index 5b1605a7f..215170c8b 100644 --- a/src/sat/cadical/external_propagate.cpp +++ b/src/sat/cadical/cadical_external_propagate.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*----------------------------------------------------------------------------*/ @@ -43,12 +47,12 @@ void Internal::remove_observed_var (int ilit) { backtrack (); } - assert (fixed (ilit) || !level); + CADICAL_assert (fixed (ilit) || !level); const int idx = vidx (ilit); - assert ((size_t) idx < relevanttab.size ()); + CADICAL_assert ((size_t) idx < relevanttab.size ()); unsigned &ref = relevanttab[idx]; - assert (fixed (ilit) || ref > 0); + CADICAL_assert (fixed (ilit) || ref > 0); if (fixed (ilit)) ref = 0; else if (ref < UINT_MAX) { @@ -66,7 +70,7 @@ void Internal::remove_observed_var (int ilit) { // Supposed to be used only by mobical. // bool Internal::observed (int ilit) const { - assert ((size_t) vidx (ilit) < relevanttab.size ()); + CADICAL_assert ((size_t) vidx (ilit) < relevanttab.size ()); return relevanttab[vidx (ilit)] > 0; } @@ -87,7 +91,7 @@ void Internal::set_tainted_literal () { tainted_literal = idx; continue; } - assert (val (tainted_literal)); + CADICAL_assert (val (tainted_literal)); if (var (idx).level < var (tainted_literal).level) { tainted_literal = idx; } @@ -100,7 +104,7 @@ void Internal::renotify_trail_after_ilb () { return; } LOG ("notify external propagator about new assignments (after ilb)"); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG LOG ("(decision level: %d, trail size: %zd, notified %zd)", level, trail.size (), notified); #endif @@ -113,7 +117,7 @@ void Internal::renotify_trail_after_local_search () { } LOG ("notify external propagator about new assignments (after local " "search)"); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG LOG ("(decision level: %d, trail size: %zd, notified %zd)", level, trail.size (), notified); #endif @@ -161,12 +165,12 @@ void Internal::renotify_full_trail () { continue; int elit = externalize (ilit); // TODO: double-check tainting - assert (elit); + 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. - assert (external->observed (elit) || fixed (ilit)); + CADICAL_assert (external->observed (elit) || fixed (ilit)); if (!external->ervars[abs (elit)]) assigned.push_back (elit); } @@ -195,7 +199,7 @@ bool Internal::is_decision (int ilit) { const int idx = vidx (ilit); Var &v = var (idx); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG LOG (v.reason, "is_decision: i%d (current level: %d, is_fixed: %d, v.level: %d, " "is_external_reason: %d, v.reason: )", @@ -203,7 +207,7 @@ bool Internal::is_decision (int ilit) { #endif if (!v.level || v.reason) return false; - assert (!v.reason); + CADICAL_assert (!v.reason); return true; } @@ -211,7 +215,7 @@ void Internal::force_backtrack (size_t new_level) { if (!forced_backt_allowed || level <= 0 || new_level >= (size_t) level) return; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG LOG ("external propagator forces backtrack to decision level" "%zd (from level %d)", new_level, level); @@ -231,13 +235,13 @@ void Internal::force_backtrack (size_t new_level) { bool Internal::external_propagate () { if (level) require_mode (SEARCH); - assert (!unsat); + CADICAL_assert (!unsat); size_t before = num_assigned; bool cb_repropagate_needed = true; while (cb_repropagate_needed && !conflict && external_prop && !external_prop_is_lazy && !private_steps) { -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG LOG ("external propagation starts (decision level: %d, trail size: " "%zd, notified %zd)", level, trail.size (), notified); @@ -251,13 +255,13 @@ bool Internal::external_propagate () { stats.ext_prop.ext_cb++; stats.ext_prop.eprop_call++; while (elit) { - assert (external->is_observed[abs (elit)]); + CADICAL_assert (external->is_observed[abs (elit)]); int ilit = external->e2i[abs (elit)]; if (elit < 0) ilit = -ilit; int tmp = val (ilit); -#ifndef NDEBUG - assert (fixed (ilit) || observed (ilit)); +#ifndef CADICAL_NDEBUG + CADICAL_assert (fixed (ilit) || observed (ilit)); LOG ("External propagation of e%d (i%d val: %d)", elit, ilit, tmp); #endif if (!tmp) { @@ -308,7 +312,7 @@ bool Internal::external_propagate () { stats.ext_prop.eprop_call++; } -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG LOG ("External propagation ends (decision level: %d, trail size: %zd, " "notified %zd)", level, trail.size (), notified); @@ -331,7 +335,7 @@ bool Internal::external_propagate () { notify_assignments (); } -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG if (has_external_clause) LOG ("New external clauses are to be added."); else @@ -367,7 +371,7 @@ bool Internal::external_propagate () { stats.ext_prop.elearn_call++; } } -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG LOG ("External clause addition ends on decision level %d at trail " "size " "%zd (notified %zd)", @@ -433,7 +437,7 @@ void Internal::move_literals_to_watch () { highest_level = other_level; highest_value = other_value; } -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG LOG ("highest position: %d highest level: %d highest value: %d", highest_position, highest_level, highest_value); #endif @@ -473,7 +477,7 @@ void Internal::move_literals_to_watch () { // void Internal::add_external_clause (int propagated_elit, bool no_backtrack) { - assert (original.empty ()); + CADICAL_assert (original.empty ()); int elit = 0; if (propagated_elit) { @@ -482,7 +486,7 @@ void Internal::add_external_clause (int propagated_elit, // propagator can add them as an explicit unforgettable external clause // or set 'are_reasons_forgettable' to false. ext_clause_forgettable = external->propagator->are_reasons_forgettable; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG LOG ("add external reason of propagated lit: %d", propagated_elit); #endif elit = external->propagator->cb_add_reason_clause_lit (propagated_elit); @@ -497,15 +501,15 @@ void Internal::add_external_clause (int propagated_elit, clause.clear (); // Read out the external lemma into original and simplify it into clause - assert (clause.empty ()); - assert (original.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (original.empty ()); - assert (!force_no_backtrack); - assert (!from_propagator); + CADICAL_assert (!force_no_backtrack); + CADICAL_assert (!from_propagator); force_no_backtrack = no_backtrack; from_propagator = true; while (elit) { - assert (external->is_observed[abs (elit)]); + CADICAL_assert (external->is_observed[abs (elit)]); external->add (elit); if (propagated_elit) elit = @@ -514,8 +518,8 @@ void Internal::add_external_clause (int propagated_elit, elit = external->propagator->cb_add_external_clause_lit (); } external->add (elit); - assert (original.empty ()); - assert (clause.empty ()); + CADICAL_assert (original.empty ()); + CADICAL_assert (clause.empty ()); force_no_backtrack = false; from_propagator = false; lrat_chain = std::move (lrat_chain_ext); @@ -530,11 +534,11 @@ void Internal::add_external_clause (int propagated_elit, void Internal::explain_reason (int ilit, Clause *reason, int &open) { if (!opts.exteagerreasons) return; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG LOG (reason, "explain_reason of %d (open: %d)", ilit, open); #endif - assert (reason); - assert (reason != external_reason); + CADICAL_assert (reason); + CADICAL_assert (reason != external_reason); for (const auto &other : *reason) { if (other == ilit) continue; @@ -544,8 +548,8 @@ void Internal::explain_reason (int ilit, Clause *reason, int &open) { Var &v = var (other); if (!v.level) continue; - assert (val (other) < 0); - assert (v.level <= level); + CADICAL_assert (val (other) < 0); + CADICAL_assert (v.level <= level); if (v.reason == external_reason) { v.reason = learn_external_reason_clause (-other, 0, true); } @@ -577,8 +581,8 @@ void Internal::explain_reason (int ilit, Clause *reason, int &open) { // over-approximated. // void Internal::explain_external_propagations () { - assert (conflict); - assert (clause.empty ()); + CADICAL_assert (conflict); + CADICAL_assert (clause.empty ()); Clause *reason = conflict; std::vector<int> seen_lits; @@ -601,16 +605,16 @@ void Internal::explain_external_propagations () { if (!open) break; } - assert (!open); + CADICAL_assert (!open); if (!opts.exteagerrecalc) { for (auto lit : seen_lits) { Flags &f = flags (lit); f.seen = false; } -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG for (auto idx : vars) { - assert (!flags (idx).seen); + CADICAL_assert (!flags (idx).seen); } #endif } @@ -636,7 +640,7 @@ void Internal::explain_external_propagations () { lrat_chain.clear (); v.reason = 0; } - assert (v.level >= real_level); + CADICAL_assert (v.level >= real_level); if (v.level > real_level) { v.level = real_level; } @@ -646,7 +650,7 @@ void Internal::explain_external_propagations () { #if 0 // has been fuzzed extensively for (auto idx : vars) { - assert (!flags (idx).seen); + CADICAL_assert (!flags (idx).seen); } #endif } @@ -664,18 +668,18 @@ void Internal::explain_external_propagations () { Clause *Internal::learn_external_reason_clause (int ilit, int falsified_elit, bool no_backtrack) { - assert (external->propagator); + CADICAL_assert (external->propagator); // we cannot modify clause during analysis auto clause_tmp = std::move (clause); - assert (clause.empty ()); - assert (original.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (original.empty ()); stats.ext_prop.eprop_expl++; int elit = 0; if (!falsified_elit) { - assert (!fixed (ilit)); + CADICAL_assert (!fixed (ilit)); elit = externalize (ilit); } else elit = falsified_elit; @@ -683,7 +687,7 @@ Clause *Internal::learn_external_reason_clause (int ilit, LOG ("ilit: %d, elit: %d", ilit, elit); add_external_clause (elit, no_backtrack); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG if (!falsified_elit && newest_clause) { // Check if external propagation is correct wrt to the topological order // defined by the trail. In case it is a falsified external propagation @@ -691,7 +695,7 @@ Clause *Internal::learn_external_reason_clause (int ilit, // falsified clause. const int propagated_ilit = ilit; for (auto const reason_ilit : *newest_clause) { - assert (var (reason_ilit).trail <= var (propagated_ilit).trail); + CADICAL_assert (var (reason_ilit).trail <= var (propagated_ilit).trail); } } #endif @@ -721,12 +725,12 @@ Clause *Internal::wrapped_learn_external_reason_clause (int ilit) { // The learn_external_reason clause can leave a literal in clause when // there is a falsified elit arg. Here it is not allowed to // happen. - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); clause = std::move (clause_tmp); clause_tmp.clear (); } - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); lrat_chain = std::move (chain_tmp); chain_tmp.clear (); return res; @@ -747,8 +751,8 @@ void Internal::handle_external_clause (Clause *res) { if (from_propagator) stats.ext_prop.elearn_prop++; // new unit clause. For now just backtrack. - assert (!force_no_backtrack); - assert (level); + CADICAL_assert (!force_no_backtrack); + CADICAL_assert (level); // if (!opts.chrono) { backtrack (); // } @@ -756,18 +760,18 @@ void Internal::handle_external_clause (Clause *res) { } if (from_propagator) stats.ext_prop.elearned++; - assert (res->size >= 2); + CADICAL_assert (res->size >= 2); const int pos0 = res->literals[0]; const int pos1 = res->literals[1]; if (force_no_backtrack) { - assert (val (pos1) < 0); - assert (val (pos0) >= 0); + CADICAL_assert (val (pos1) < 0); + CADICAL_assert (val (pos0) >= 0); return; // TODO: maybe fix levels } const int l1 = var (pos1).level; if (val (pos0) < 0) { // conflicting or propagating clause - assert (0 < l1 && l1 <= var (pos0).level); + CADICAL_assert (0 < l1 && l1 <= var (pos0).level); if (!opts.chrono) { backtrack (l1); } @@ -777,7 +781,7 @@ void Internal::handle_external_clause (Clause *res) { // its better to backtrack instead of analyze backtrack (l1 - 1); conflict = 0; - assert (!val (pos0) && !val (pos1)); + CADICAL_assert (!val (pos0) && !val (pos1)); } } else { search_assign_driving (pos0, res); @@ -850,7 +854,7 @@ bool Internal::external_check_solution () { continue; const int lit = external->ival (idx); etrail.push_back (lit); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG #ifdef LOGGING bool p = external->vals[idx]; LOG ("evals[%d]: %d ival(%d): %d", idx, p, idx, lit); @@ -943,14 +947,14 @@ void Internal::notify_assignments () { continue; int elit = externalize (ilit); // TODO: double-check tainting - assert (elit); + CADICAL_assert (elit); if (external->ervars[abs (elit)]) continue; // Fixed variables might get mapped (during compact) to another // non-observed but fixed variable. // This happens on root level, so notification about their assignment is // already done. - assert (external->observed (elit) || + CADICAL_assert (external->observed (elit) || (fixed (ilit) && !external->ervars[abs (elit)])); assigned.push_back (elit); } @@ -995,8 +999,8 @@ int Internal::ask_decision () { if (!external_prop || external_prop_is_lazy || private_steps) return 0; - assert (!unsat); - assert (!conflict); + CADICAL_assert (!unsat); + CADICAL_assert (!conflict); notify_assignments (); int level_before = level; forced_backt_allowed = true; @@ -1007,8 +1011,8 @@ int Internal::ask_decision () { if (level_before != level) { propagate (); - assert (!unsat); - assert (!conflict); + CADICAL_assert (!unsat); + CADICAL_assert (!conflict); notify_assignments (); // In case the external propagator forced to backtrack below the @@ -1023,7 +1027,7 @@ int Internal::ask_decision () { if (!elit) return 0; LOG ("external propagator proposes decision: %d", elit); - assert (external->is_observed[abs (elit)]); + CADICAL_assert (external->is_observed[abs (elit)]); if (!external->is_observed[abs (elit)]) return 0; @@ -1031,7 +1035,7 @@ int Internal::ask_decision () { if (elit < 0) ilit = -ilit; - assert (fixed (ilit) || observed (ilit)); + CADICAL_assert (fixed (ilit) || observed (ilit)); LOG ("Asking external propagator for decision returned: %d (internal: " "%d, fixed: %d, val: %d)", @@ -1052,7 +1056,7 @@ int Internal::ask_decision () { // propagator. // bool Internal::is_external_forgettable (int64_t id) { - assert (opts.check); + CADICAL_assert (opts.check); return (external->forgettable_original.find (id) != external->forgettable_original.end ()); } @@ -1064,8 +1068,8 @@ bool Internal::is_external_forgettable (int64_t id) { // ignore it. // void Internal::mark_garbage_external_forgettable (int64_t id) { - assert (opts.check); - assert (is_external_forgettable (id)); + CADICAL_assert (opts.check); + CADICAL_assert (is_external_forgettable (id)); LOG (external->forgettable_original[id], "forgettable external lemma is deleted:"); @@ -1079,7 +1083,7 @@ void Internal::mark_garbage_external_forgettable (int64_t id) { // internally for debug purposes. // void Internal::check_watched_literal_invariants () { -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG int v0 = 0; int v1 = 0; @@ -1092,19 +1096,19 @@ void Internal::check_watched_literal_invariants () { v1 = 1; else if (val (clause[1]) < 0) v1 = -1; - assert (v0 >= v1); + CADICAL_assert (v0 >= v1); #endif if (val (clause[0]) > 0) { if (val (clause[1]) > 0) { // Case 1: Both literals are satisfied // They are ordered by lower to higher decision level - assert (var (clause[0]).level <= var (clause[1]).level); + CADICAL_assert (var (clause[0]).level <= var (clause[1]).level); // Every other literal of the clause is either // - satisfied at higher level // - unassigned // - falsified for (size_t i = 2; i < clause.size (); i++) - assert (val (clause[i]) <= 0 || + CADICAL_assert (val (clause[i]) <= 0 || (var (clause[1]).level <= var (clause[i]).level)); } else if (val (clause[1]) == @@ -1114,14 +1118,14 @@ void Internal::check_watched_literal_invariants () { // - unassigned // - falsified for (size_t i = 2; i < clause.size (); i++) - assert (val (clause[i]) <= 0); + CADICAL_assert (val (clause[i]) <= 0); } else { // Case 3: First satisfied, next falsified -> could have been a // reason of a previous propagation // Every other literal of the clause is falsified but at a lower // decision level for (size_t i = 2; i < clause.size (); i++) - assert (val (clause[i]) < 0 && + CADICAL_assert (val (clause[i]) < 0 && (var (clause[1]).level >= var (clause[i]).level)); } } else if (val (clause[0]) == 0) { @@ -1131,30 +1135,30 @@ void Internal::check_watched_literal_invariants () { // - unassigned // - falsified for (size_t i = 2; i < clause.size (); i++) - assert (val (clause[i]) <= 0); + CADICAL_assert (val (clause[i]) <= 0); } else { // Case 5: First unassigned, next falsified -> PROPAGATE // Every other literal of the clause is falsified but at a lower // decision level for (size_t i = 2; i < clause.size (); i++) - assert (val (clause[i]) < 0 && + CADICAL_assert (val (clause[i]) < 0 && (var (clause[1]).level >= var (clause[i]).level)); } } else { - assert (val (clause[0]) < 0 && + CADICAL_assert (val (clause[0]) < 0 && val (clause[1]) < 0); // Case 6: Both literals are falsified // They are ordered by higher to lower decision level - assert (var (clause[0]).level >= var (clause[1]).level); + CADICAL_assert (var (clause[0]).level >= var (clause[1]).level); // Every other literal of the clause is falsified, but at a lower level for (size_t i = 2; i < clause.size (); i++) - assert (val (clause[i]) < 0 && + CADICAL_assert (val (clause[i]) < 0 && (var (clause[1]).level >= var (clause[i]).level)); } } -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG /*----------------------------------------------------------------------------*/ // @@ -1224,3 +1228,5 @@ void Internal::get_all_fixed_literals (std::vector<int> &fixed_lits) { #endif } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/factor.cpp b/src/sat/cadical/cadical_factor.cpp similarity index 90% rename from src/sat/cadical/factor.cpp rename to src/sat/cadical/cadical_factor.cpp index dbb5a84e5..3fee40685 100644 --- a/src/sat/cadical/factor.cpp +++ b/src/sat/cadical/cadical_factor.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { #define FACTORS 1 @@ -20,7 +24,7 @@ inline bool factor_occs_size::operator() (unsigned a, unsigned b) { void Internal::factor_mode () { reset_watches (); - assert (!watching ()); + CADICAL_assert (!watching ()); init_occs (); const int size_limit = opts.factorsize; @@ -121,9 +125,9 @@ Factoring::Factoring (Internal *i, int64_t l) } Factoring::~Factoring () { - assert (counted.empty ()); - assert (nounted.empty ()); - assert (flauses.empty ()); + CADICAL_assert (counted.empty ()); + CADICAL_assert (nounted.empty ()); + CADICAL_assert (flauses.empty ()); internal->release_quotients (*this); schedule.erase (); // actually not necessary } @@ -143,7 +147,7 @@ double Internal::tied_next_factor_score (int lit) { // use markfact, unmarkfact, getfact for this purpose. // Quotient *Internal::new_quotient (Factoring &factoring, int factor) { - assert (!getfact (factor, FACTORS)); + CADICAL_assert (!getfact (factor, FACTORS)); markfact (factor, FACTORS); Quotient *res = new Quotient (factor); res->next = 0; @@ -151,12 +155,12 @@ Quotient *Internal::new_quotient (Factoring &factoring, int factor) { Quotient *last = factoring.quotients.last; res->bid = 0; if (last) { - assert (factoring.quotients.first); - assert (!last->next); + CADICAL_assert (factoring.quotients.first); + CADICAL_assert (!last->next); last->next = res; res->id = last->id + 1; } else { - assert (!factoring.quotients.first); + CADICAL_assert (!factoring.quotients.first); factoring.quotients.first = res; res->id = 0; } @@ -170,7 +174,7 @@ void Internal::release_quotients (Factoring &factoring) { for (Quotient *q = factoring.quotients.first, *next; q; q = next) { next = q->next; int factor = q->factor; - assert (getfact (factor, FACTORS)); + CADICAL_assert (getfact (factor, FACTORS)); unmarkfact (factor, FACTORS); delete q; } @@ -178,7 +182,7 @@ void Internal::release_quotients (Factoring &factoring) { } size_t Internal::first_factor (Factoring &factoring, int factor) { - assert (!factoring.quotients.first); + CADICAL_assert (!factoring.quotients.first); Quotient *quotient = new_quotient (factoring, factor); vector<Clause *> &qlauses = quotient->qlauses; int64_t ticks = 0; @@ -189,14 +193,14 @@ size_t Internal::first_factor (Factoring &factoring, int factor) { size_t res = qlauses.size (); LOG ("quotient[0] factor %d size %zu", factor, res); // This invariant can of course be broken by previous factorings - // assert (res > 1); + // CADICAL_assert (res > 1); stats.ticks.factor += ticks; return res; } void Internal::clear_nounted (vector<int> &nounted) { for (const auto &lit : nounted) { - assert (getfact (lit, NOUNTED)); + CADICAL_assert (getfact (lit, NOUNTED)); unmarkfact (lit, NOUNTED); } nounted.clear (); @@ -204,7 +208,7 @@ void Internal::clear_nounted (vector<int> &nounted) { void Internal::clear_flauses (vector<Clause *> &flauses) { for (auto c : flauses) { - assert (c->swept); + CADICAL_assert (c->swept); c->swept = false; } flauses.clear (); @@ -248,17 +252,17 @@ Quotient *Internal::best_quotient (Factoring &factoring, int Internal::next_factor (Factoring &factoring, unsigned *next_count_ptr) { Quotient *last_quotient = factoring.quotients.last; - assert (last_quotient); + CADICAL_assert (last_quotient); vector<Clause *> &last_clauses = last_quotient->qlauses; vector<unsigned> &count = factoring.count; vector<int> &counted = factoring.counted; vector<Clause *> &flauses = factoring.flauses; - assert (counted.empty ()); - assert (flauses.empty ()); + CADICAL_assert (counted.empty ()); + CADICAL_assert (flauses.empty ()); const int initial = factoring.initial; int64_t ticks = 1 + cache_lines (last_clauses.size (), sizeof (Clause *)); for (auto c : last_clauses) { - assert (!c->swept); + CADICAL_assert (!c->swept); int min_lit = 0; unsigned factors = 0; size_t min_size = 0; @@ -268,7 +272,7 @@ int Internal::next_factor (Factoring &factoring, unsigned *next_count_ptr) { if (factors++) break; } else { - assert (!getfact (other, QUOTIENT)); + CADICAL_assert (!getfact (other, QUOTIENT)); markfact (other, QUOTIENT); const size_t other_size = occs (other).size (); if (!min_lit || other_size < min_size) { @@ -277,12 +281,12 @@ int Internal::next_factor (Factoring &factoring, unsigned *next_count_ptr) { } } } - assert (factors); + CADICAL_assert (factors); if (factors == 1) { - assert (min_lit); + CADICAL_assert (min_lit); const int c_size = c->size; vector<int> &nounted = factoring.nounted; - assert (nounted.empty ()); + CADICAL_assert (nounted.empty ()); ticks += 1 + cache_lines (occs (min_lit).size (), sizeof (Clause *)); for (auto d : occs (min_lit)) { if (c == d) @@ -304,13 +308,13 @@ int Internal::next_factor (Factoring &factoring, unsigned *next_count_ptr) { goto CONTINUE_WITH_NEXT_MIN_WATCH; next = other; } - assert (next); + CADICAL_assert (next); if (abs (next) > abs (initial)) continue; if (!active (next)) continue; - assert (!getfact (next, FACTORS)); - assert (!getfact (next, NOUNTED)); + CADICAL_assert (!getfact (next, FACTORS)); + CADICAL_assert (!getfact (next, NOUNTED)); markfact (next, NOUNTED); nounted.push_back (next); d->swept = true; @@ -340,10 +344,10 @@ int Internal::next_factor (Factoring &factoring, unsigned *next_count_ptr) { if (lit_count < next_count) continue; if (lit_count == next_count) { - assert (lit_count); + CADICAL_assert (lit_count); ties++; } else { - assert (lit_count > next_count); + CADICAL_assert (lit_count > next_count); next_count = lit_count; next = lit; ties = 1; @@ -361,25 +365,25 @@ int Internal::next_factor (Factoring &factoring, unsigned *next_count_ptr) { if (lit_count != next_count) continue; double lit_score = tied_next_factor_score (lit); - assert (lit_score >= 0); + CADICAL_assert (lit_score >= 0); LOG ("score %g of next factor candidate %d", lit_score, lit); if (lit_score <= next_score) continue; next_score = lit_score; next = lit; } - assert (next_score >= 0); - assert (next); + CADICAL_assert (next_score >= 0); + CADICAL_assert (next); LOG ("best score %g of next factor %d", next_score, next); } else { - assert (ties == 1); + CADICAL_assert (ties == 1); LOG ("single next factor %d with count %u", next, next_count); } } for (const auto &lit : counted) count[vlit (lit)] = 0; counted.clear (); - assert (!next || next_count > 1); + CADICAL_assert (!next || next_count > 1); *next_count_ptr = next_count; return next; } @@ -389,19 +393,19 @@ void Internal::factorize_next (Factoring &factoring, int next, Quotient *last_quotient = factoring.quotients.last; Quotient *next_quotient = new_quotient (factoring, next); - assert (last_quotient); + CADICAL_assert (last_quotient); vector<Clause *> &last_clauses = last_quotient->qlauses; vector<Clause *> &next_clauses = next_quotient->qlauses; vector<size_t> &matches = next_quotient->matches; vector<Clause *> &flauses = factoring.flauses; - assert (flauses.empty ()); + CADICAL_assert (flauses.empty ()); int64_t ticks = 1 + cache_lines (last_clauses.size (), sizeof (Clause *)); size_t i = 0; for (auto c : last_clauses) { - assert (!c->swept); + CADICAL_assert (!c->swept); int min_lit = 0; unsigned factors = 0; size_t min_size = 0; @@ -411,7 +415,7 @@ void Internal::factorize_next (Factoring &factoring, int next, if (factors++) break; } else { - assert (!getfact (other, QUOTIENT)); + CADICAL_assert (!getfact (other, QUOTIENT)); markfact (other, QUOTIENT); const size_t other_size = occs (other).size (); if (!min_lit || other_size < min_size) { @@ -420,9 +424,9 @@ void Internal::factorize_next (Factoring &factoring, int next, } } } - assert (factors); + CADICAL_assert (factors); if (factors == 1) { - assert (min_lit); + CADICAL_assert (min_lit); const int c_size = c->size; ticks += 1 + cache_lines (occs (min_lit).size (), sizeof (Clause *)); for (auto d : occs (min_lit)) { @@ -459,14 +463,14 @@ void Internal::factorize_next (Factoring &factoring, int next, clear_flauses (flauses); stats.ticks.factor += ticks; - assert (expected_next_count <= next_clauses.size ()); + CADICAL_assert (expected_next_count <= next_clauses.size ()); (void) expected_next_count; } // We only need to enlarge factoring.count as everything else is // initialized in internal void Internal::resize_factoring (Factoring &factoring, int lit) { - assert (lit > 0); + CADICAL_assert (lit > 0); size_t new_var_size = lit + 1; size_t new_lit_size = 2 * new_var_size; enlarge_zero (factoring.count, new_lit_size); @@ -477,13 +481,13 @@ void Internal::flush_unmatched_clauses (Quotient *q) { vector<size_t> &q_matches = q->matches, &prev_matches = prev->matches; vector<Clause *> &q_clauses = q->qlauses, &prev_clauses = prev->qlauses; const size_t n = q_clauses.size (); - assert (n == q_matches.size ()); + CADICAL_assert (n == q_matches.size ()); bool prev_is_first = !prev->id; size_t i = 0; while (i < q_matches.size ()) { size_t j = q_matches[i]; q_matches[i] = i; - assert (i <= j); + CADICAL_assert (i <= j); if (!prev_is_first) { size_t matches = prev_matches[j]; prev_matches[i] = matches; @@ -506,7 +510,7 @@ void Internal::flush_unmatched_clauses (Quotient *q) { void Internal::add_self_subsuming_factor (Quotient *q, Quotient *p) { const int factor = q->factor; const int not_factor = p->factor; - assert (-factor == not_factor); + CADICAL_assert (-factor == not_factor); LOG ( "adding self subsuming factor because blocked clause is a tautology"); for (auto c : q->qlauses) { @@ -533,7 +537,7 @@ void Internal::add_self_subsuming_factor (Quotient *q, Quotient *p) { } } lrat_chain.push_back (c->id); - assert (lrat_chain.size () == 2); + CADICAL_assert (lrat_chain.size () == 2); } if (clause.size () > 1) { new_factor_clause (); @@ -566,7 +570,7 @@ bool Internal::self_subsuming_factor (Quotient *q) { const int factor = p->factor; Flags &f = flags (factor); if (f.seen) { - assert (std::find (analyzed.begin (), analyzed.end (), -factor) != + CADICAL_assert (std::find (analyzed.begin (), analyzed.end (), -factor) != analyzed.end ()); found = true; x = p; @@ -581,7 +585,7 @@ bool Internal::self_subsuming_factor (Quotient *q) { analyzed.push_back (factor); f.seen = true; } - assert (!found || (x && y)); + CADICAL_assert (!found || (x && y)); clear_analyzed_literals (); if (found) { add_self_subsuming_factor (x, y); @@ -611,11 +615,11 @@ void Internal::blocked_clause (Quotient *q, int not_fresh) { return; int64_t new_id = ++clause_id; q->bid = new_id; - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); for (Quotient *p = q; p; p = p->prev) clause.push_back (-p->factor); clause.push_back (not_fresh); - assert (!lrat || mini_chain.size ()); + CADICAL_assert (!lrat || mini_chain.size ()); proof->add_derived_clause (new_id, true, clause, mini_chain); mini_chain.clear (); clause.clear (); @@ -627,11 +631,11 @@ void Internal::blocked_clause (Quotient *q, int not_fresh) { void Internal::add_factored_quotient (Quotient *q, int not_fresh) { LOG ("adding factored quotient[%zu] clauses", q->id); const int factor = q->factor; - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); auto qlauses = q->qlauses; for (unsigned idx = 0; idx < qlauses.size (); idx++) { const auto c = qlauses[idx]; - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); for (const auto &other : *c) { if (other == factor) { continue; @@ -639,8 +643,8 @@ void Internal::add_factored_quotient (Quotient *q, int not_fresh) { clause.push_back (other); } if (lrat) { - assert (proof); - assert (q->bid); + CADICAL_assert (proof); + CADICAL_assert (q->bid); unsigned idxtoo = idx; for (Quotient *p = q; p; p = p->prev) { lrat_chain.push_back (p->qlauses[idxtoo]->id); @@ -677,7 +681,7 @@ void Internal::eagerly_remove_from_occurences (Clause *c) { if (*q != c) q++; } - assert (q + 1 == p); + CADICAL_assert (q + 1 == p); occ.resize (q - begin); } } @@ -730,7 +734,7 @@ bool Internal::apply_factoring (Factoring &factoring, Quotient *q) { delete_unfactored (p); for (Quotient *p = q; p; p = p->prev) update_factored (factoring, p); - assert (fresh > 0); + CADICAL_assert (fresh > 0); resize_factoring (factoring, fresh); return true; } @@ -758,7 +762,7 @@ void Internal::schedule_factorization (Factoring &factoring) { update_factor_candidate (factoring, not_lit); } } -#ifndef QUIET +#ifndef CADICAL_QUIET size_t size_cands = factoring.schedule.size (); VERBOSE (2, "scheduled %zu factorization candidate literals %.0f %%", size_cands, percent (size_cands, max_var)); @@ -769,7 +773,7 @@ bool Internal::run_factorization (int64_t limit) { Factoring factoring = Factoring (this, limit); schedule_factorization (factoring); bool done = false; -#ifndef QUIET +#ifndef CADICAL_QUIET unsigned factored = 0; #endif int64_t *ticks = &stats.ticks.factor; @@ -804,7 +808,7 @@ bool Internal::run_factorization (int64_t limit) { const int next = next_factor (factoring, &next_count); if (next == 0) break; - assert (next_count > 1); + CADICAL_assert (next_count > 1); if (next_count < 2) break; factorize_next (factoring, next, next_count); @@ -813,7 +817,7 @@ bool Internal::run_factorization (int64_t limit) { Quotient *q = best_quotient (factoring, &reduction); if (q && (int) reduction > factoring.bound) { if (apply_factoring (factoring, q)) { -#ifndef QUIET +#ifndef CADICAL_QUIET factored++; #endif } else @@ -832,7 +836,7 @@ bool Internal::run_factorization (int64_t limit) { } // kissat initializes scores for new variables at this point, however // this is actually done already during resize of internal -#ifndef QUIET +#ifndef CADICAL_QUIET report ('f', !factored); #endif return completed; @@ -848,7 +852,7 @@ int Internal::get_new_extension_variable () { reset_watches (); // it does not enlarge otab, however, so we do this manually init_occs (); - assert (vlit (new_internal)); + CADICAL_assert (vlit (new_internal)); return new_internal; } @@ -859,9 +863,9 @@ bool Internal::factor () { return false; if (!opts.factor) return false; - // The following assertion fails if there are *only* user propagator + // The following CADICAL_assertion fails if there are *only* user propagator // clauses (which are redundant). - // assert (stats.mark.factor || clauses.empty ()); + // CADICAL_assert (stats.mark.factor || clauses.empty ()); if (last.factor.marked >= stats.mark.factor) { VERBOSE (3, "factorization skipped as no literals have been" @@ -869,7 +873,7 @@ bool Internal::factor () { last.factor.marked, stats.mark.factor); return false; } - assert (!level); + CADICAL_assert (!level); SET_EFFORT_LIMIT (limit, factor, stats.factor); if (!stats.factor) @@ -878,7 +882,7 @@ bool Internal::factor () { START_SIMPLIFIER (factor, FACTOR); stats.factor++; -#ifndef QUIET +#ifndef CADICAL_QUIET struct { int64_t variables, clauses, ticks; } before, after, delta; @@ -896,7 +900,7 @@ bool Internal::factor () { learn_empty_clause (); } -#ifndef QUIET +#ifndef CADICAL_QUIET after.variables = stats.variables_extension + stats.variables_original; after.clauses = stats.current.irredundant; after.ticks = stats.ticks.factor; @@ -919,3 +923,5 @@ bool Internal::factor () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/file.cpp b/src/sat/cadical/cadical_file.cpp similarity index 94% rename from src/sat/cadical/file.cpp rename to src/sat/cadical/cadical_file.cpp index 7f8a82dea..98f19f752 100644 --- a/src/sat/cadical/file.cpp +++ b/src/sat/cadical/cadical_file.cpp @@ -1,3 +1,5 @@ +#include "global.h" + #include "internal.hpp" /*------------------------------------------------------------------------*/ @@ -11,13 +13,28 @@ extern "C" { #include <string.h> #include <sys/stat.h> #include <sys/types.h> -#include <unistd.h> } -#ifndef _WIN32 +#ifdef WIN32 + +#include <windows.h> +#include <io.h> +#include <cstdio> + +#define access _access +#define popen _popen +#define pclose _pclose +#define R_OK 4 +#define W_OK 2 +#define S_IFIFO _S_IFIFO +#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) + +#else extern "C" { #include <sys/wait.h> +#include <unistd.h> } #endif @@ -33,6 +50,8 @@ extern "C" { #endif +ABC_NAMESPACE_IMPL_START + /*------------------------------------------------------------------------*/ namespace CaDiCaL { @@ -43,13 +62,13 @@ namespace CaDiCaL { File::File (Internal *i, bool w, int c, int p, FILE *f, const char *n) : internal (i), -#if !defined(QUIET) || !defined(NDEBUG) +#if !defined(CADICAL_QUIET) || !defined(CADICAL_NDEBUG) writing (w), #endif close_file (c), child_pid (p), file (f), _name (strdup (n)), _lineno (1), _bytes (0) { (void) w; - assert (f), assert (n); + CADICAL_assert (f), CADICAL_assert (n); } /*------------------------------------------------------------------------*/ @@ -129,7 +148,7 @@ static int sig7z[] = {0x37, 0x7A, 0xBC, 0xAF, 0x27, 0x1C, EOF}; static int lzmasig[] = {0x5D, EOF}; bool File::match (Internal *internal, const char *path, const int *sig) { - assert (path); + CADICAL_assert (path); FILE *tmp = fopen (path, "r"); if (!tmp) { WARNING ("failed to open '%s' to check signature", path); @@ -170,7 +189,7 @@ char *File::find_program (const char *prg) { size_t pathlen = (q - p) + prglen; char *path = new char[pathlen + 1]; snprintf (path, pathlen + 1, "%s/%s", p, prg); - assert (strlen (path) == pathlen); + CADICAL_assert (strlen (path) == pathlen); if (exists (path)) res = path; else @@ -226,7 +245,7 @@ void File::delete_str_vector (std::vector<char *> &argv) { FILE *File::open_pipe (Internal *internal, const char *fmt, const char *path, const char *mode) { -#ifdef QUIET +#ifdef CADICAL_QUIET (void) internal; #endif size_t prglen = 0; @@ -274,14 +293,14 @@ static std::mutex compressed_file_writing_mutex; FILE *File::write_pipe (Internal *internal, const char *command, const char *path, int &child_pid) { - assert (command[0] && command[0] != ' '); + CADICAL_assert (command[0] && command[0] != ' '); MSG ("writing through command '%s' to '%s'", command, path); -#ifdef QUIET +#ifdef CADICAL_QUIET (void) internal; #endif std::vector<char *> args; split_str (command, args); - assert (!args.empty ()); + CADICAL_assert (!args.empty ()); args.push_back (0); char **argv = args.data (); char *absolute_command_path = find_program (argv[0]); @@ -328,7 +347,7 @@ FILE *File::write_pipe (Internal *internal, const char *command, // be closed by the parent process we have to close all of the // erroneously cloned fds here. -#ifndef NCLOSEFROM +#ifndef CADICAL_NCLOSEFROM ::closefrom (3); #else // Simplistic replacement on Unix without 'closefrom'. @@ -341,7 +360,7 @@ FILE *File::write_pipe (Internal *internal, const char *command, if (absolute_command_path) delete[] absolute_command_path; delete_str_vector (args); -#ifdef QUIET +#ifdef CADICAL_QUIET (void) internal; #endif #if defined(__APPLE__) || defined(__MACH__) @@ -421,8 +440,8 @@ File *File::write (Internal *internal, const char *path) { } void File::close (bool print) { - assert (file); -#ifndef QUIET + CADICAL_assert (file); +#ifndef CADICAL_QUIET if (internal->opts.quiet) print = false; else if (internal->opts.verbose > 0) @@ -457,7 +476,7 @@ void File::close (bool print) { // TODO what about error checking for 'fclose', 'pclose' or 'waitpid'? -#ifndef QUIET +#ifndef CADICAL_QUIET if (print) { if (writing) { uint64_t written_bytes = bytes (); @@ -493,7 +512,7 @@ void File::close (bool print) { } void File::flush () { - assert (file); + CADICAL_assert (file); fflush (file); } @@ -504,3 +523,5 @@ File::~File () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/flags.cpp b/src/sat/cadical/cadical_flags.cpp similarity index 64% rename from src/sat/cadical/flags.cpp rename to src/sat/cadical/cadical_flags.cpp index de7e13b4f..5c06a33b4 100644 --- a/src/sat/cadical/flags.cpp +++ b/src/sat/cadical/cadical_flags.cpp @@ -1,104 +1,108 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { void Internal::mark_fixed (int lit) { if (external->fixed_listener) { int elit = externalize (lit); - assert (elit); + CADICAL_assert (elit); const int eidx = abs (elit); if (!external->ervars[eidx]) external->fixed_listener->notify_fixed_assignment (elit); } Flags &f = flags (lit); - assert (f.status == Flags::ACTIVE); + CADICAL_assert (f.status == Flags::ACTIVE); f.status = Flags::FIXED; LOG ("fixed %d", abs (lit)); stats.all.fixed++; stats.now.fixed++; stats.inactive++; - assert (stats.active); + CADICAL_assert (stats.active); stats.active--; - assert (!active (lit)); - assert (f.fixed ()); + CADICAL_assert (!active (lit)); + CADICAL_assert (f.fixed ()); if (external_prop && private_steps) { // If pre/inprocessing found a fixed assignment, we want the propagator // to know about it. // But at that point it is not guaranteed to be already on the trail, so // notification will happen only later. - assert (!level); + CADICAL_assert (!level); } } void Internal::mark_eliminated (int lit) { Flags &f = flags (lit); - assert (f.status == Flags::ACTIVE); + CADICAL_assert (f.status == Flags::ACTIVE); f.status = Flags::ELIMINATED; LOG ("eliminated %d", abs (lit)); stats.all.eliminated++; stats.now.eliminated++; stats.inactive++; - assert (stats.active); + CADICAL_assert (stats.active); stats.active--; - assert (!active (lit)); - assert (f.eliminated ()); + CADICAL_assert (!active (lit)); + CADICAL_assert (f.eliminated ()); } void Internal::mark_pure (int lit) { Flags &f = flags (lit); - assert (f.status == Flags::ACTIVE); + CADICAL_assert (f.status == Flags::ACTIVE); f.status = Flags::PURE; LOG ("pure %d", abs (lit)); stats.all.pure++; stats.now.pure++; stats.inactive++; - assert (stats.active); + CADICAL_assert (stats.active); stats.active--; - assert (!active (lit)); - assert (f.pure ()); + CADICAL_assert (!active (lit)); + CADICAL_assert (f.pure ()); } void Internal::mark_substituted (int lit) { Flags &f = flags (lit); - assert (f.status == Flags::ACTIVE); + CADICAL_assert (f.status == Flags::ACTIVE); f.status = Flags::SUBSTITUTED; LOG ("substituted %d", abs (lit)); stats.all.substituted++; stats.now.substituted++; stats.inactive++; - assert (stats.active); + CADICAL_assert (stats.active); stats.active--; - assert (!active (lit)); - assert (f.substituted ()); + CADICAL_assert (!active (lit)); + CADICAL_assert (f.substituted ()); } void Internal::mark_active (int lit) { Flags &f = flags (lit); - assert (f.status == Flags::UNUSED); + CADICAL_assert (f.status == Flags::UNUSED); f.status = Flags::ACTIVE; LOG ("activate %d previously unused", abs (lit)); - assert (stats.inactive); + CADICAL_assert (stats.inactive); stats.inactive--; - assert (stats.unused); + CADICAL_assert (stats.unused); stats.unused--; stats.active++; - assert (active (lit)); + CADICAL_assert (active (lit)); } void Internal::reactivate (int lit) { - assert (!active (lit)); + CADICAL_assert (!active (lit)); Flags &f = flags (lit); - assert (f.status != Flags::FIXED); - assert (f.status != Flags::UNUSED); + CADICAL_assert (f.status != Flags::FIXED); + CADICAL_assert (f.status != Flags::UNUSED); #ifdef LOGGING const char *msg = 0; #endif switch (f.status) { default: case Flags::ELIMINATED: - assert (f.status == Flags::ELIMINATED); - assert (stats.now.eliminated > 0); + CADICAL_assert (f.status == Flags::ELIMINATED); + CADICAL_assert (stats.now.eliminated > 0); stats.now.eliminated--; #ifdef LOGGING msg = "eliminated"; @@ -108,28 +112,30 @@ void Internal::reactivate (int lit) { #ifdef LOGGING msg = "substituted"; #endif - assert (stats.now.substituted > 0); + CADICAL_assert (stats.now.substituted > 0); stats.now.substituted--; break; case Flags::PURE: #ifdef LOGGING msg = "pure literal"; #endif - assert (stats.now.pure > 0); + CADICAL_assert (stats.now.pure > 0); stats.now.pure--; break; } #ifdef LOGGING - assert (msg); + CADICAL_assert (msg); LOG ("reactivate previously %s %d", msg, abs (lit)); #endif f.status = Flags::ACTIVE; f.sweep = false; - assert (active (lit)); + CADICAL_assert (active (lit)); stats.reactivated++; - assert (stats.inactive > 0); + CADICAL_assert (stats.inactive > 0); stats.inactive--; stats.active++; } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/flip.cpp b/src/sat/cadical/cadical_flip.cpp similarity index 86% rename from src/sat/cadical/flip.cpp rename to src/sat/cadical/cadical_flip.cpp index 0df508246..a89e3abb7 100644 --- a/src/sat/cadical/flip.cpp +++ b/src/sat/cadical/cadical_flip.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { bool Internal::flip (int lit) { @@ -11,7 +15,7 @@ bool Internal::flip (int lit) { /* if (flags (lit).unused ()) { - assert (lit <= max_var); + CADICAL_assert (lit <= max_var); mark_active (lit); set_val (lit, 1); return true; @@ -31,9 +35,9 @@ bool Internal::flip (int lit) { const int idx = vidx (lit); const signed char original_value = vals[idx]; - assert (original_value); + CADICAL_assert (original_value); lit = original_value < 0 ? -idx : idx; - assert (val (lit) > 0); + CADICAL_assert (val (lit) > 0); // Here we go over all the clauses in which 'lit' is watched by 'lit' and // check whether assigning 'lit' to false would break watching invariants @@ -57,7 +61,7 @@ bool Internal::flip (int lit) { const signed char b = val (w.blit); if (b > 0) continue; - assert (b < 0); + CADICAL_assert (b < 0); res = false; break; } @@ -96,7 +100,7 @@ bool Internal::flip (int lit) { k++; if (v < 0) { k = lits + 2; - assert (w.clause->pos <= size); + CADICAL_assert (w.clause->pos <= size); while (k != middle && (v = val (r = *k)) < 0) k++; } @@ -106,8 +110,8 @@ bool Internal::flip (int lit) { break; } - assert (v > 0); - assert (lits + 2 <= k), assert (k <= w.clause->end ()); + CADICAL_assert (v > 0); + CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ()); w.clause->pos = k - lits; lits[0] = other, lits[1] = r, *k = lit; watch_literal (r, lit, w.clause); @@ -133,24 +137,24 @@ bool Internal::flip (int lit) { const int idx = vidx (lit); const signed char original_value = vals[idx]; - assert (original_value); + CADICAL_assert (original_value); lit = original_value < 0 ? -idx : idx; - assert (val (lit) > 0); + CADICAL_assert (val (lit) > 0); LOG ("flipping value of %d = 1 to %d = -1", lit, lit); set_val (idx, -original_value); - assert (val (-lit) > 0); - assert (val (lit) < 0); + CADICAL_assert (val (-lit) > 0); + CADICAL_assert (val (lit) < 0); Var &v = var (idx); - assert (trail[v.trail] == lit); + CADICAL_assert (trail[v.trail] == lit); trail[v.trail] = -lit; if (opts.ilb) { if (!tainted_literal) tainted_literal = lit; else { - assert (val (tainted_literal)); + CADICAL_assert (val (tainted_literal)); if (v.level < var (tainted_literal).level) { tainted_literal = lit; } @@ -171,7 +175,7 @@ bool Internal::flippable (int lit) { /* if (flags (lit).unused ()) { - assert (lit <= max_var); + CADICAL_assert (lit <= max_var); mark_active (lit); return true; } @@ -189,9 +193,9 @@ bool Internal::flippable (int lit) { const int idx = vidx (lit); const signed char original_value = vals[idx]; - assert (original_value); + CADICAL_assert (original_value); lit = original_value < 0 ? -idx : idx; - assert (val (lit) > 0); + CADICAL_assert (val (lit) > 0); // Here we go over all the clauses in which 'lit' is watched by 'lit' and // check whether assigning 'lit' to false would break watching invariants @@ -209,7 +213,7 @@ bool Internal::flippable (int lit) { const signed char b = val (w.blit); if (b > 0) continue; - assert (b < 0); + CADICAL_assert (b < 0); if (w.binary ()) { res = false; @@ -239,7 +243,7 @@ bool Internal::flippable (int lit) { k++; if (v < 0) { k = lits + 2; - assert (w.clause->pos <= size); + CADICAL_assert (w.clause->pos <= size); while (k != middle && (v = val (r = *k)) < 0) k++; } @@ -249,9 +253,9 @@ bool Internal::flippable (int lit) { break; } - assert (v > 0); - assert (lits + 2 <= k); - assert (k <= w.clause->end ()); + CADICAL_assert (v > 0); + CADICAL_assert (lits + 2 <= k); + CADICAL_assert (k <= w.clause->end ()); w.clause->pos = k - lits; i->blit = r; } @@ -267,3 +271,5 @@ bool Internal::flippable (int lit) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/format.cpp b/src/sat/cadical/cadical_format.cpp similarity index 94% rename from src/sat/cadical/format.cpp rename to src/sat/cadical/cadical_format.cpp index 87421a0fb..942d9d74a 100644 --- a/src/sat/cadical/format.cpp +++ b/src/sat/cadical/cadical_format.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { void Format::enlarge () { @@ -34,7 +38,7 @@ void Format::push_uint64 (uint64_t u) { } static bool match_format (const char *&str, const char *pattern) { - assert (pattern); + CADICAL_assert (pattern); const char *p = str; const char *q = pattern; while (*q) @@ -87,3 +91,5 @@ const char *Format::append (const char *fmt, ...) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/frattracer.cpp b/src/sat/cadical/cadical_frattracer.cpp similarity index 93% rename from src/sat/cadical/frattracer.cpp rename to src/sat/cadical/cadical_frattracer.cpp index 4f9193b42..d35a18271 100644 --- a/src/sat/cadical/frattracer.cpp +++ b/src/sat/cadical/cadical_frattracer.cpp @@ -1,12 +1,16 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ FratTracer::FratTracer (Internal *i, File *f, bool b, bool a) : internal (i), file (f), binary (b), with_antecedents (a) -#ifndef QUIET +#ifndef CADICAL_QUIET , added (0), deleted (0), finalized (0), original (0) #endif @@ -28,15 +32,15 @@ FratTracer::~FratTracer () { /*------------------------------------------------------------------------*/ inline void FratTracer::put_binary_zero () { - assert (binary); - assert (file); + CADICAL_assert (binary); + CADICAL_assert (file); file->put ((unsigned char) 0); } inline void FratTracer::put_binary_lit (int lit) { - assert (binary); - assert (file); - assert (lit != INT_MIN); + CADICAL_assert (binary); + CADICAL_assert (file); + CADICAL_assert (lit != INT_MIN); unsigned x = 2 * abs (lit) + (lit < 0); unsigned char ch; while (x & ~0x7f) { @@ -49,8 +53,8 @@ inline void FratTracer::put_binary_lit (int lit) { } inline void FratTracer::put_binary_id (int64_t id, bool can_be_negative) { - assert (binary); - assert (file); + CADICAL_assert (binary); + CADICAL_assert (file); uint64_t x = abs (id); if (can_be_negative) { x = 2 * x + (id < 0); @@ -202,7 +206,7 @@ void FratTracer::add_derived_clause (int64_t id, bool, frat_add_derived_clause (id, clause, chain); else frat_add_derived_clause (id, clause); -#ifndef QUIET +#ifndef CADICAL_QUIET added++; #endif } @@ -213,7 +217,7 @@ void FratTracer::delete_clause (int64_t id, bool, return; LOG ("FRAT TRACER tracing deletion of clause"); frat_delete_clause (id, clause); -#ifndef QUIET +#ifndef CADICAL_QUIET deleted++; #endif } @@ -229,7 +233,7 @@ void FratTracer::finalize_clause (int64_t id, const vector<int> &clause) { bool FratTracer::closed () { return file->closed (); } -#ifndef QUIET +#ifndef CADICAL_QUIET void FratTracer::print_statistics () { uint64_t bytes = file->bytes (); @@ -249,9 +253,9 @@ void FratTracer::print_statistics () { #endif void FratTracer::close (bool print) { - assert (!closed ()); + CADICAL_assert (!closed ()); file->close (); -#ifndef QUIET +#ifndef CADICAL_QUIET if (print) { MSG ("FRAT proof file '%s' closed", file->name ()); print_statistics (); @@ -262,9 +266,9 @@ void FratTracer::close (bool print) { } void FratTracer::flush (bool print) { - assert (!closed ()); + CADICAL_assert (!closed ()); file->flush (); -#ifndef QUIET +#ifndef CADICAL_QUIET if (print) { MSG ("FRAT proof file '%s' flushed", file->name ()); print_statistics (); @@ -275,3 +279,5 @@ void FratTracer::flush (bool print) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/gates.cpp b/src/sat/cadical/cadical_gates.cpp similarity index 92% rename from src/sat/cadical/gates.cpp rename to src/sat/cadical/cadical_gates.cpp index 78c6d0a09..06a73d1eb 100644 --- a/src/sat/cadical/gates.cpp +++ b/src/sat/cadical/cadical_gates.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -19,7 +23,7 @@ namespace CaDiCaL { int Internal::second_literal_in_binary_clause (Eliminator &eliminator, Clause *c, int first) { - assert (!c->garbage); + CADICAL_assert (!c->garbage); int second = 0; for (const auto &lit : *c) { if (lit == first) @@ -42,7 +46,7 @@ int Internal::second_literal_in_binary_clause (Eliminator &eliminator, return 0; if (second == INT_MIN) return 0; - assert (active (second)); + CADICAL_assert (active (second)); #ifdef LOGGING if (c->size == 2) LOG (c, "found binary"); @@ -115,8 +119,8 @@ void Internal::mark_binary_literals (Eliminator &eliminator, int first) { if (!eliminator.gates.empty ()) return; - assert (!marked (first)); - assert (eliminator.marked.empty ()); + CADICAL_assert (!marked (first)); + CADICAL_assert (eliminator.marked.empty ()); const Occs &os = occs (first); for (const auto &c : os) { @@ -133,11 +137,11 @@ void Internal::mark_binary_literals (Eliminator &eliminator, int first) { LOG ("found binary resolved unit %d", first); if (lrat) { Clause *d = find_binary_clause (first, -second); - assert (d); + CADICAL_assert (d); for (auto &lit : *d) { if (lit == first || lit == -second) continue; - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); Flags &f = flags (lit); if (f.seen) continue; @@ -150,7 +154,7 @@ void Internal::mark_binary_literals (Eliminator &eliminator, int first) { for (auto &lit : *c) { if (lit == first || lit == second) continue; - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); Flags &f = flags (lit); if (f.seen) continue; @@ -202,7 +206,7 @@ void Internal::find_equivalence (Eliminator &eliminator, int pivot) { if (!opts.elimequivs) return; - assert (opts.elimsubst); + CADICAL_assert (opts.elimsubst); if (unsat) return; @@ -231,11 +235,11 @@ void Internal::find_equivalence (Eliminator &eliminator, int pivot) { // still solved potential issues with flags if (lrat) { Clause *d = find_binary_clause (pivot, second); - assert (d); + CADICAL_assert (d); for (auto &lit : *d) { if (lit == pivot || lit == second) continue; - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); Flags &f = flags (lit); if (f.seen) continue; @@ -248,7 +252,7 @@ void Internal::find_equivalence (Eliminator &eliminator, int pivot) { for (auto &lit : *c) { if (lit == -pivot || lit == second) continue; - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); Flags &f = flags (lit); if (f.seen) continue; @@ -279,7 +283,7 @@ void Internal::find_equivalence (Eliminator &eliminator, int pivot) { stats.elimgates++; LOG (c, "first gate clause"); - assert (!c->gate); + CADICAL_assert (!c->gate); c->gate = true; eliminator.gates.push_back (c); @@ -295,10 +299,10 @@ void Internal::find_equivalence (Eliminator &eliminator, int pivot) { break; } } - assert (d); + CADICAL_assert (d); LOG (d, "second gate clause"); - assert (!d->gate); + CADICAL_assert (!d->gate); d->gate = true; eliminator.gates.push_back (d); eliminator.gatetype = EQUI; @@ -321,7 +325,7 @@ void Internal::find_and_gate (Eliminator &eliminator, int pivot) { if (!opts.elimands) return; - assert (opts.elimsubst); + CADICAL_assert (opts.elimsubst); if (unsat) return; @@ -348,7 +352,7 @@ void Internal::find_and_gate (Eliminator &eliminator, int pivot) { for (const auto &lit : *c) { if (lit == -pivot) continue; - assert (lit != pivot); + CADICAL_assert (lit != pivot); signed char tmp = val (lit); if (tmp < 0) continue; @@ -383,7 +387,7 @@ void Internal::find_and_gate (Eliminator &eliminator, int pivot) { for (const auto &lit : *c) { if (lit == -pivot) continue; - assert (lit != pivot); + CADICAL_assert (lit != pivot); if (!first) fputs (" & ", stdout); printf ("%d", -lit); @@ -399,18 +403,18 @@ void Internal::find_and_gate (Eliminator &eliminator, int pivot) { eliminator.gatetype = AND; (void) arity; - assert (!c->gate); + CADICAL_assert (!c->gate); c->gate = true; eliminator.gates.push_back (c); for (const auto &lit : *c) { if (lit == -pivot) continue; - assert (lit != pivot); + CADICAL_assert (lit != pivot); signed char tmp = val (lit); if (tmp < 0) continue; - assert (!tmp); - assert (marked (lit) < 0); + CADICAL_assert (!tmp); + CADICAL_assert (marked (lit) < 0); marks[vidx (lit)] *= 2; } @@ -426,12 +430,12 @@ void Internal::find_and_gate (Eliminator &eliminator, int pivot) { if (tmp != 2) continue; LOG (d, "AND gate binary side clause"); - assert (!d->gate); + CADICAL_assert (!d->gate); d->gate = true; eliminator.gates.push_back (d); count++; } - assert (count >= arity); + CADICAL_assert (count >= arity); (void) count; break; @@ -503,7 +507,7 @@ void Internal::find_if_then_else (Eliminator &eliminator, int pivot) { if (!opts.elimites) return; - assert (opts.elimsubst); + CADICAL_assert (opts.elimsubst); if (unsat) return; @@ -523,7 +527,7 @@ void Internal::find_if_then_else (Eliminator &eliminator, int pivot) { swap (ai, bi); if (ci == pivot) swap (ai, ci); - assert (ai == pivot); + CADICAL_assert (ai == pivot); for (auto j = i + 1; j != end; j++) { Clause *dj = *j; int aj, bj, cj; @@ -533,7 +537,7 @@ void Internal::find_if_then_else (Eliminator &eliminator, int pivot) { swap (aj, bj); if (cj == pivot) swap (aj, cj); - assert (aj == pivot); + CADICAL_assert (aj == pivot); if (abs (bi) == abs (cj)) swap (bj, cj); if (abs (ci) == abs (cj)) @@ -551,10 +555,10 @@ void Internal::find_if_then_else (Eliminator &eliminator, int pivot) { LOG (d1, "3rd if-then-else"); LOG (d2, "4th if-then-else"); LOG ("found ITE gate %d == (%d ? %d : %d)", pivot, -bi, -ci, -cj); - assert (!di->gate); - assert (!dj->gate); - assert (!d1->gate); - assert (!d2->gate); + CADICAL_assert (!di->gate); + CADICAL_assert (!dj->gate); + CADICAL_assert (!d1->gate); + CADICAL_assert (!d2->gate); di->gate = true; dj->gate = true; d1->gate = true; @@ -634,7 +638,7 @@ void Internal::find_xor_gate (Eliminator &eliminator, int pivot) { if (!opts.elimxors) return; - assert (opts.elimsubst); + CADICAL_assert (opts.elimsubst); if (unsat) return; @@ -658,7 +662,7 @@ void Internal::find_xor_gate (Eliminator &eliminator, int pivot) { if (arity > opts.elimxorlim) continue; - assert (eliminator.gates.empty ()); + CADICAL_assert (eliminator.gates.empty ()); unsigned needed = (1u << arity) - 1; // additional clauses unsigned signs = 0; // literals to negate @@ -685,7 +689,7 @@ void Internal::find_xor_gate (Eliminator &eliminator, int pivot) { } eliminator.gates.push_back (d); - assert (eliminator.gates.size () == (1u << arity)); + CADICAL_assert (eliminator.gates.size () == (1u << arity)); #ifdef LOGGING if (opts.log) { @@ -696,7 +700,7 @@ void Internal::find_xor_gate (Eliminator &eliminator, int pivot) { for (const auto &lit : *d) { if (lit == pivot) continue; - assert (lit != -pivot); + CADICAL_assert (lit != -pivot); if (!first) fputs (" ^ ", stdout); printf ("%d", lit); @@ -741,7 +745,7 @@ void Internal::find_gate_clauses (Eliminator &eliminator, int pivot) { if (val (pivot)) return; - assert (eliminator.gates.empty ()); + CADICAL_assert (eliminator.gates.empty ()); find_equivalence (eliminator, pivot); find_and_gate (eliminator, pivot); @@ -754,7 +758,7 @@ void Internal::find_gate_clauses (Eliminator &eliminator, int pivot) { void Internal::unmark_gate_clauses (Eliminator &eliminator) { LOG ("unmarking %zd gate clauses", eliminator.gates.size ()); for (const auto &c : eliminator.gates) { - assert (c->gate); + CADICAL_assert (c->gate); c->gate = false; } eliminator.gates.clear (); @@ -764,3 +768,5 @@ void Internal::unmark_gate_clauses (Eliminator &eliminator) { /*------------------------------------------------------------------------*/ } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/idruptracer.cpp b/src/sat/cadical/cadical_idruptracer.cpp similarity index 91% rename from src/sat/cadical/idruptracer.cpp rename to src/sat/cadical/cadical_idruptracer.cpp index f4cc0f3f6..ec6467163 100644 --- a/src/sat/cadical/idruptracer.cpp +++ b/src/sat/cadical/cadical_idruptracer.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -7,7 +11,7 @@ namespace CaDiCaL { IdrupTracer::IdrupTracer (Internal *i, File *f, bool b) : internal (i), file (f), binary (b), num_clauses (0), size_clauses (0), clauses (0), last_hash (0), last_id (0), last_clause (0) -#ifndef QUIET +#ifndef CADICAL_QUIET , added (0), deleted (0) #endif @@ -21,10 +25,10 @@ IdrupTracer::IdrupTracer (Internal *i, File *f, bool b) uint64_t nonce = random.next (); if (!(nonce & 1)) nonce++; - assert (nonce), assert (nonce & 1); + CADICAL_assert (nonce), CADICAL_assert (nonce & 1); nonces[n] = nonce; } -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG binary = b; #else (void) b; @@ -50,7 +54,7 @@ IdrupTracer::~IdrupTracer () { /*------------------------------------------------------------------------*/ void IdrupTracer::enlarge_clauses () { - assert (num_clauses == size_clauses); + CADICAL_assert (num_clauses == size_clauses); const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1; LOG ("IDRUP Tracer enlarging clauses of tracer from %" PRIu64 " to %" PRIu64, @@ -73,7 +77,7 @@ void IdrupTracer::enlarge_clauses () { IdrupClause *IdrupTracer::new_clause () { const size_t size = imported_clause.size (); - assert (size <= UINT_MAX); + CADICAL_assert (size <= UINT_MAX); const int off = size ? -1 : 0; const size_t bytes = sizeof (IdrupClause) + (size - off) * sizeof (int); IdrupClause *res = (IdrupClause *) new char[bytes]; @@ -91,13 +95,13 @@ IdrupClause *IdrupTracer::new_clause () { } void IdrupTracer::delete_clause (IdrupClause *c) { - assert (c); + CADICAL_assert (c); num_clauses--; delete[] (char *) c; } uint64_t IdrupTracer::reduce_hash (uint64_t hash, uint64_t size) { - assert (size > 0); + CADICAL_assert (size > 0); unsigned shift = 32; uint64_t res = hash; while ((((uint64_t) 1) << shift) > size) { @@ -105,12 +109,12 @@ uint64_t IdrupTracer::reduce_hash (uint64_t hash, uint64_t size) { shift >>= 1; } res &= size - 1; - assert (res < size); + CADICAL_assert (res < size); return res; } uint64_t IdrupTracer::compute_hash (const int64_t id) { - assert (id > 0); + CADICAL_assert (id > 0); unsigned j = id % num_nonces; uint64_t tmp = nonces[j] * (uint64_t) id; return last_hash = tmp; @@ -131,7 +135,7 @@ bool IdrupTracer::find_and_delete (const int64_t id) { } if (!c) return false; - assert (c && res); + CADICAL_assert (c && res); *res = c->next; int *begin = c->literals; for (size_t i = 0; i < c->size; i++) { @@ -158,15 +162,15 @@ inline void IdrupTracer::flush_if_piping () { } inline void IdrupTracer::put_binary_zero () { - assert (binary); - assert (file); + CADICAL_assert (binary); + CADICAL_assert (file); file->put ((unsigned char) 0); } inline void IdrupTracer::put_binary_lit (int lit) { - assert (binary); - assert (file); - assert (lit != INT_MIN); + CADICAL_assert (binary); + CADICAL_assert (file); + CADICAL_assert (lit != INT_MIN); unsigned x = 2 * abs (lit) + (lit < 0); unsigned char ch; while (x & ~0x7f) { @@ -179,8 +183,8 @@ inline void IdrupTracer::put_binary_lit (int lit) { } inline void IdrupTracer::put_binary_id (int64_t id, bool can_be_negative) { - assert (binary); - assert (file); + CADICAL_assert (binary); + CADICAL_assert (file); uint64_t x = abs (id); if (can_be_negative) { x = 2 * x + (id < 0); @@ -251,12 +255,12 @@ void IdrupTracer::idrup_add_original_clause (const vector<int> &clause) { void IdrupTracer::idrup_delete_clause (int64_t id, const vector<int> &clause) { if (find_and_delete (id)) { - assert (imported_clause.empty ()); + CADICAL_assert (imported_clause.empty ()); if (binary) file->put ('w'); else file->put ("w "); -#ifndef QUIET +#ifndef CADICAL_QUIET weakened++; #endif } else { @@ -264,7 +268,7 @@ void IdrupTracer::idrup_delete_clause (int64_t id, file->put ('d'); else file->put ("d "); -#ifndef QUIET +#ifndef CADICAL_QUIET deleted++; #endif } @@ -392,10 +396,10 @@ void IdrupTracer::add_derived_clause (int64_t, bool, const vector<int64_t> &) { if (file->closed ()) return; - assert (imported_clause.empty ()); + CADICAL_assert (imported_clause.empty ()); LOG (clause, "IDRUP TRACER tracing addition of derived clause"); idrup_add_derived_clause (clause); -#ifndef QUIET +#ifndef CADICAL_QUIET added++; #endif } @@ -405,7 +409,7 @@ void IdrupTracer::add_assumption_clause (int64_t id, const vector<int64_t> &) { if (file->closed ()) return; - assert (imported_clause.empty ()); + CADICAL_assert (imported_clause.empty ()); LOG (clause, "IDRUP TRACER tracing addition of assumption clause"); for (auto &lit : clause) imported_clause.push_back (lit); @@ -418,7 +422,7 @@ void IdrupTracer::delete_clause (int64_t id, bool, const vector<int> &clause) { if (file->closed ()) return; - assert (imported_clause.empty ()); + CADICAL_assert (imported_clause.empty ()); LOG ("IDRUP TRACER tracing deletion of clause[%" PRId64 "]", id); idrup_delete_clause (id, clause); } @@ -426,11 +430,11 @@ void IdrupTracer::delete_clause (int64_t id, bool, void IdrupTracer::weaken_minus (int64_t id, const vector<int> &) { if (file->closed ()) return; - assert (imported_clause.empty ()); + CADICAL_assert (imported_clause.empty ()); LOG ("IDRUP TRACER tracing weaken minus of clause[%" PRId64 "]", id); last_id = id; insert (); -#ifndef QUIET +#ifndef CADICAL_QUIET weakened++; #endif } @@ -439,7 +443,7 @@ void IdrupTracer::conclude_unsat (ConclusionType, const vector<int64_t> &conclusion) { if (file->closed ()) return; - assert (imported_clause.empty ()); + CADICAL_assert (imported_clause.empty ()); LOG (conclusion, "IDRUP TRACER tracing conclusion of clause(s)"); idrup_conclude_and_delete (conclusion); } @@ -451,12 +455,12 @@ void IdrupTracer::add_original_clause (int64_t id, bool, return; if (!restored) { LOG (clause, "IDRUP TRACER tracing addition of original clause"); -#ifndef QUIET +#ifndef CADICAL_QUIET original++; #endif return idrup_add_original_clause (clause); } - assert (restored); + CADICAL_assert (restored); if (find_and_delete (id)) { LOG (clause, "IDRUP TRACER the clause was not yet weakened, so no restore"); @@ -464,7 +468,7 @@ void IdrupTracer::add_original_clause (int64_t id, bool, } LOG (clause, "IDRUP TRACER tracing addition of restored clause"); idrup_add_restored_clause (clause); -#ifndef QUIET +#ifndef CADICAL_QUIET restore++; #endif } @@ -495,7 +499,7 @@ void IdrupTracer::solve_query () { return; LOG (assumptions, "IDRUP TRACER tracing solve query with assumptions"); idrup_solve_query (); -#ifndef QUIET +#ifndef CADICAL_QUIET solved++; #endif } @@ -514,7 +518,7 @@ void IdrupTracer::reset_assumptions () { bool IdrupTracer::closed () { return file->closed (); } -#ifndef QUIET +#ifndef CADICAL_QUIET void IdrupTracer::print_statistics () { // TODO complete this. @@ -538,9 +542,9 @@ void IdrupTracer::print_statistics () { #endif void IdrupTracer::close (bool print) { - assert (!closed ()); + CADICAL_assert (!closed ()); file->close (); -#ifndef QUIET +#ifndef CADICAL_QUIET if (print) { MSG ("IDRUP proof file '%s' closed", file->name ()); print_statistics (); @@ -551,9 +555,9 @@ void IdrupTracer::close (bool print) { } void IdrupTracer::flush (bool print) { - assert (!closed ()); + CADICAL_assert (!closed ()); file->flush (); -#ifndef QUIET +#ifndef CADICAL_QUIET if (print) { MSG ("IDRUP proof file '%s' flushed", file->name ()); print_statistics (); @@ -564,3 +568,5 @@ void IdrupTracer::flush (bool print) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/instantiate.cpp b/src/sat/cadical/cadical_instantiate.cpp similarity index 89% rename from src/sat/cadical/instantiate.cpp rename to src/sat/cadical/cadical_instantiate.cpp index 494686b81..2c34015b2 100644 --- a/src/sat/cadical/instantiate.cpp +++ b/src/sat/cadical/cadical_instantiate.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -13,7 +17,7 @@ namespace CaDiCaL { void Internal::collect_instantiation_candidates ( Instantiator &instantiator) { - assert (occurring ()); + CADICAL_assert (occurring ()); for (auto idx : vars) { if (frozen (idx)) continue; @@ -63,8 +67,8 @@ void Internal::collect_instantiation_candidates ( inline void Internal::inst_assign (int lit) { LOG ("instantiate assign %d", lit); - assert (!val (lit)); - assert ((int) num_assigned < max_var); + CADICAL_assert (!val (lit)); + CADICAL_assert ((int) num_assigned < max_var); num_assigned++; set_val (lit, 1); trail.push_back (lit); @@ -123,12 +127,12 @@ bool Internal::inst_propagate () { // Adapted from 'propagate'. k++; if (v < 0) { k = lits + 2; - assert (w.clause->pos <= size); + CADICAL_assert (w.clause->pos <= size); while (k != middle && (v = val (r = *k)) < 0) k++; } w.clause->pos = k - lits; - assert (lits + 2 <= k), assert (k <= w.clause->end ()); + CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ()); if (v > 0) { j[-1].blit = r; } else if (!v) { @@ -138,14 +142,14 @@ bool Internal::inst_propagate () { // Adapted from 'propagate'. watch_literal (r, lit, w.clause); j--; } else if (!u) { - assert (v < 0); + CADICAL_assert (v < 0); if (lrat) { inst_chain.push_back (w.clause); } inst_assign (other); } else { - assert (u < 0); - assert (v < 0); + CADICAL_assert (u < 0); + CADICAL_assert (v < 0); if (lrat) { inst_chain.push_back (w.clause); } @@ -176,7 +180,7 @@ bool Internal::instantiate_candidate (int lit, Clause *c) { stats.instried++; if (c->garbage) return false; - assert (!level); + CADICAL_assert (!level); bool found = false, satisfied = false, inactive = false; int unassigned = 0; for (const auto &other : *c) { @@ -203,13 +207,13 @@ bool Internal::instantiate_candidate (int lit, Clause *c) { if (unassigned < 3) return false; size_t before = trail.size (); - assert (propagated == before); - assert (active (lit)); - assert (inst_chain.empty ()); + CADICAL_assert (propagated == before); + CADICAL_assert (active (lit)); + CADICAL_assert (inst_chain.empty ()); LOG (c, "trying to instantiate %d in", lit); - assert (!c->garbage); + CADICAL_assert (!c->garbage); c->instantiated = true; - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); level++; inst_assign (lit); // Assume 'lit' to true. for (const auto &other : *c) { @@ -217,23 +221,23 @@ bool Internal::instantiate_candidate (int lit, Clause *c) { continue; const signed char tmp = val (other); if (tmp) { - assert (tmp < 0); + CADICAL_assert (tmp < 0); continue; } inst_assign (-other); // Assume other to false. } bool ok = inst_propagate (); // Propagate. - assert (lrat_chain.empty ()); // chain will be built here + CADICAL_assert (lrat_chain.empty ()); // chain will be built here if (ok) { inst_chain.clear (); } else if (lrat) { // analyze conflict for lrat - assert (inst_chain.size ()); + CADICAL_assert (inst_chain.size ()); Clause *reason = inst_chain.back (); inst_chain.pop_back (); lrat_chain.push_back (reason->id); for (const auto &other : *reason) { Flags &f = flags (other); - assert (!f.seen); + CADICAL_assert (!f.seen); f.seen = true; analyzed.push_back (other); } @@ -242,7 +246,7 @@ bool Internal::instantiate_candidate (int lit, Clause *c) { const int other = trail.back (); LOG ("instantiate unassign %d", other); trail.pop_back (); - assert (val (other) > 0); + CADICAL_assert (val (other) > 0); num_assigned--; set_val (other, 0); // this is a variant of conflict analysis which is only needed for lrat @@ -263,7 +267,7 @@ bool Internal::instantiate_candidate (int lit, Clause *c) { inst_chain.pop_back (); } } - assert (inst_chain.empty ()); + CADICAL_assert (inst_chain.empty ()); // post processing step for lrat if (!ok && lrat) { if (flags (lit).seen) @@ -284,12 +288,12 @@ bool Internal::instantiate_candidate (int lit, Clause *c) { clear_analyzed_literals (); reverse (lrat_chain.begin (), lrat_chain.end ()); } - assert (analyzed.empty ()); + CADICAL_assert (analyzed.empty ()); propagated = before; - assert (level == 1); + CADICAL_assert (level == 1); level = 0; if (ok) { - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); LOG ("instantiation failed"); return false; } @@ -298,7 +302,7 @@ bool Internal::instantiate_candidate (int lit, Clause *c) { strengthen_clause (c, lit); watch_clause (c); lrat_chain.clear (); - assert (c->size > 1); + CADICAL_assert (c->size > 1); LOG ("instantiation succeeded"); stats.instantiated++; return true; @@ -310,10 +314,10 @@ bool Internal::instantiate_candidate (int lit, Clause *c) { // 'collect_instantiation_candidates' routine. void Internal::instantiate (Instantiator &instantiator) { - assert (opts.instantiate); + CADICAL_assert (opts.instantiate); START (instantiate); stats.instrounds++; -#ifndef QUIET +#ifndef CADICAL_QUIET const int64_t candidates = instantiator.candidates.size (); int64_t tried = 0; #endif @@ -324,7 +328,7 @@ void Internal::instantiate (Instantiator &instantiator) { if (!propagate ()) { LOG ("propagation after connecting watches failed"); learn_empty_clause (); - assert (unsat); + CADICAL_assert (unsat); } } PHASE ("instantiate", stats.instrounds, @@ -335,7 +339,7 @@ void Internal::instantiate (Instantiator &instantiator) { !instantiator.candidates.empty ()) { Instantiator::Candidate cand = instantiator.candidates.back (); instantiator.candidates.pop_back (); -#ifndef QUIET +#ifndef CADICAL_QUIET tried++; #endif if (!active (cand.lit)) @@ -363,3 +367,5 @@ void Internal::instantiate (Instantiator &instantiator) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/internal.cpp b/src/sat/cadical/cadical_internal.cpp similarity index 94% rename from src/sat/cadical/internal.cpp rename to src/sat/cadical/cadical_internal.cpp index 4ef1df058..abe18c59c 100644 --- a/src/sat/cadical/internal.cpp +++ b/src/sat/cadical/cadical_internal.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -25,7 +29,7 @@ Internal::Internal () target_assigned (0), no_conflict_until (0), unsat_constraint (false), marked_failed (true), sweep_incomplete (false), citten (0), num_assigned (0), proof (0), opts (this), -#ifndef QUIET +#ifndef CADICAL_QUIET profiles (this), force_phase_messages (false), #endif arena (this), prefix ("c "), internal (this), external (0), @@ -51,7 +55,7 @@ Internal::Internal () Internal::~Internal () { // If a memory exception ocurred a profile might still be active. -#ifndef QUIET +#ifndef CADICAL_QUIET #define PROFILE(NAME, LEVEL) \ if (PROFILE_ACTIVE (NAME)) \ STOP (NAME); @@ -108,7 +112,7 @@ void Internal::enlarge_vals (size_t new_vsize) { vals -= vsize; delete[] vals; } else - assert (!vsize); + CADICAL_assert (!vsize); vals = new_vals; } @@ -156,16 +160,16 @@ void Internal::init_vars (int new_max_var) { new_max_var - max_var, max_var + 1, new_max_var); if ((size_t) new_max_var >= vsize) enlarge (new_max_var); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG for (int64_t i = -new_max_var; i < -max_var; i++) - assert (!vals[i]); + CADICAL_assert (!vals[i]); for (unsigned i = max_var + 1; i <= (unsigned) new_max_var; i++) - assert (!vals[i]), assert (!btab[i]), assert (!gtab[i]); + CADICAL_assert (!vals[i]), CADICAL_assert (!btab[i]), CADICAL_assert (!gtab[i]); for (uint64_t i = 2 * ((uint64_t) max_var + 1); i <= 2 * (uint64_t) new_max_var + 1; i++) - assert (ptab[i] == -1); + CADICAL_assert (ptab[i] == -1); #endif - assert (!btab[0]); + CADICAL_assert (!btab[0]); int old_max_var = max_var; max_var = new_max_var; init_queue (old_max_var, new_max_var); @@ -178,7 +182,7 @@ void Internal::init_vars (int new_max_var) { } void Internal::add_original_lit (int lit) { - assert (abs (lit) <= max_var); + CADICAL_assert (abs (lit) <= max_var); if (lit) { original.push_back (lit); } else { @@ -187,14 +191,14 @@ void Internal::add_original_lit (int lit) { if (proof) { // Use the external form of the clause for printing in proof // Externalize(internalized literal) != external literal - assert (!original.size () || !external->eclause.empty ()); + CADICAL_assert (!original.size () || !external->eclause.empty ()); proof->add_external_original_clause (id, false, external->eclause); } if (internal->opts.check && (internal->opts.checkwitness || internal->opts.checkfailed)) { bool forgettable = from_propagator && ext_clause_forgettable; if (forgettable && opts.check) { - assert (!original.size () || !external->eclause.empty ()); + CADICAL_assert (!original.size () || !external->eclause.empty ()); // First integer is the presence-flag (even if the clause is empty) external->forgettable_original[id] = {1}; @@ -216,7 +220,7 @@ void Internal::finish_added_clause_with_id (int64_t id, bool restore) { if (proof) { // Use the external form of the clause for printing in proof // Externalize(internalized literal) != external literal - assert (!original.size () || !external->eclause.empty ()); + CADICAL_assert (!original.size () || !external->eclause.empty ()); proof->add_external_original_clause (id, false, external->eclause, restore); } @@ -229,8 +233,8 @@ void Internal::finish_added_clause_with_id (int64_t id, bool restore) { void Internal::reserve_ids (int number) { // return; LOG ("reserving %d ids", number); - assert (number >= 0); - assert (!clause_id && !reserved_ids && !original_id); + CADICAL_assert (number >= 0); + CADICAL_assert (!clause_id && !reserved_ids && !original_id); clause_id = reserved_ids = number; if (proof) proof->begin_proof (reserved_ids); @@ -350,7 +354,7 @@ int Internal::propagate_assumptions () { stats.ilbsuccess += (level > 0); stats.levelsreused += level; if (level) { - assert (control.size () > 1); + CADICAL_assert (control.size () > 1); stats.literalsreused += num_assigned - control[1].trail; } } @@ -577,7 +581,7 @@ void Internal::init_search_limits () { init_averages (); } else if (opts.stabilize && opts.stabilizeonly) { LOG ("keeping always forced stable phase"); - assert (stable); + CADICAL_assert (stable); } else if (stable) { LOG ("switching back to default non-stable phase"); stable = false; @@ -655,7 +659,7 @@ bool Internal::preprocess_round (int round) { before.vars = active (); before.clauses = stats.current.irredundant; stats.preprocessings++; - assert (!preprocessing); + CADICAL_assert (!preprocessing); preprocessing = true; PHASE ("preprocessing", stats.preprocessings, "starting round %d with %" PRId64 " variables and %" PRId64 @@ -671,7 +675,7 @@ bool Internal::preprocess_round (int round) { after.vars = active (); after.clauses = stats.current.irredundant; - assert (preprocessing); + CADICAL_assert (preprocessing); preprocessing = false; PHASE ("preprocessing", stats.preprocessings, "finished round %d with %" PRId64 " variables and %" PRId64 @@ -703,7 +707,7 @@ void Internal::preprocess_quickly () { before.vars = active (); before.clauses = stats.current.irredundant; // stats.preprocessings++; - assert (!preprocessing); + CADICAL_assert (!preprocessing); preprocessing = true; PHASE ("preprocessing", stats.preprocessings, "starting with %" PRId64 " variables and %" PRId64 " clauses", @@ -724,7 +728,7 @@ void Internal::preprocess_quickly () { // condition (false); after.vars = active (); after.clauses = stats.current.irredundant; - assert (preprocessing); + CADICAL_assert (preprocessing); preprocessing = false; PHASE ("preprocessing", stats.preprocessings, "finished with %" PRId64 " variables and %" PRId64 " clauses", @@ -747,12 +751,12 @@ int Internal::preprocess () { int Internal::try_to_satisfy_formula_by_saved_phases () { LOG ("satisfying formula by saved phases"); - assert (!level); - assert (!force_saved_phase); - assert (propagated == trail.size ()); + CADICAL_assert (!level); + CADICAL_assert (!force_saved_phase); + CADICAL_assert (propagated == trail.size ()); force_saved_phase = true; if (external_prop) { - assert (!level); + CADICAL_assert (!level); LOG ("external notifications are turned off during preprocessing."); private_steps = true; } @@ -766,14 +770,14 @@ int Internal::try_to_satisfy_formula_by_saved_phases () { res = 20; } else if (!propagate ()) { LOG ("saved phases do not satisfy redundant clauses"); - assert (level > 0); + CADICAL_assert (level > 0); backtrack (); conflict = 0; // ignore conflict - assert (!res); + CADICAL_assert (!res); break; } } - assert (force_saved_phase); + CADICAL_assert (force_saved_phase); force_saved_phase = false; if (external_prop) { private_steps = false; @@ -791,10 +795,10 @@ int Internal::try_to_satisfy_formula_by_saved_phases () { void Internal::produce_failed_assumptions () { LOG ("producing failed assumptions"); - assert (!level); - assert (!assumptions.empty ()); + CADICAL_assert (!level); + CADICAL_assert (!assumptions.empty ()); while (!unsat) { - assert (!satisfied ()); + CADICAL_assert (!satisfied ()); notify_assignments (); if (decide ()) break; @@ -812,7 +816,7 @@ void Internal::produce_failed_assumptions () { int Internal::local_search_round (int round) { - assert (round > 0); + CADICAL_assert (round > 0); if (unsat) return false; @@ -820,7 +824,7 @@ int Internal::local_search_round (int round) { return false; START_OUTER_WALK (); - assert (!localsearching); + CADICAL_assert (!localsearching); localsearching = true; // Determine propagation limit quadratically scaled with rounds. @@ -834,7 +838,7 @@ int Internal::local_search_round (int round) { int res = walk_round (limit, true); - assert (localsearching); + CADICAL_assert (localsearching); localsearching = false; STOP_OUTER_WALK (); @@ -861,11 +865,11 @@ int Internal::local_search () { if (res == 10) { LOG ("local search determined formula to be satisfiable"); - assert (!stats.walk.minimum); + CADICAL_assert (!stats.walk.minimum); res = try_to_satisfy_formula_by_saved_phases (); } else if (res == 20) { LOG ("local search determined assumptions to be inconsistent"); - assert (!assumptions.empty ()); + CADICAL_assert (!assumptions.empty ()); produce_failed_assumptions (); } @@ -878,7 +882,7 @@ int Internal::local_search () { // such that we do not have to backtrack to level 0. // int Internal::solve (bool preprocess_only) { - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); START (solve); if (proof) proof->solve_query (); @@ -889,7 +893,7 @@ int Internal::solve (bool preprocess_only) { stats.ilbsuccess += (level > 0); stats.levelsreused += level; if (level) { - assert (control.size () > 1); + CADICAL_assert (control.size () > 1); stats.literalsreused += num_assigned - control[1].trail; } if (external->propagator) @@ -998,9 +1002,9 @@ int Internal::restore_clauses () { } int Internal::lookahead () { - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); START (lookahead); - assert (!lookingahead); + CADICAL_assert (!lookingahead); lookingahead = true; if (external_prop) { if (level) { @@ -1021,7 +1025,7 @@ int Internal::lookahead () { res = 0; reset_solving (); report_solving (tmp); - assert (lookingahead); + CADICAL_assert (lookingahead); lookingahead = false; STOP (lookahead); if (external_prop) { @@ -1041,7 +1045,7 @@ void Internal::finalize (int res) { // finalize external units if (frat) { for (const auto &evar : external->vars) { - assert (evar > 0); + CADICAL_assert (evar > 0); const auto eidx = 2 * evar; int sign = 1; int64_t id = external->ext_units[eidx]; @@ -1060,7 +1064,7 @@ void Internal::finalize (int res) { const unsigned eidx = (elit < 0) + 2u * (unsigned) abs (elit); const int64_t id = external->ext_units[eidx]; if (id) { - assert (unit_clauses (vlit (lit)) == id); + CADICAL_assert (unit_clauses (vlit (lit)) == id); continue; } } @@ -1188,3 +1192,5 @@ bool Internal::traverse_clauses (ClauseIterator &it) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/ipasir.cpp b/src/sat/cadical/cadical_ipasir.cpp similarity index 93% rename from src/sat/cadical/ipasir.cpp rename to src/sat/cadical/cadical_ipasir.cpp index 0eae5e95f..cb88deb4b 100644 --- a/src/sat/cadical/ipasir.cpp +++ b/src/sat/cadical/cadical_ipasir.cpp @@ -1,7 +1,9 @@ +#include "global.h" + #include "ipasir.h" #include "ccadical.h" -extern "C" { +ABC_NAMESPACE_IMPL_START const char *ipasir_signature () { return ccadical_signature (); } @@ -40,4 +42,5 @@ void ipasir_set_learn (void *solver, void *state, int max_length, void (*learn) (void *state, int *clause)) { ccadical_set_learn ((CCaDiCaL *) solver, state, max_length, learn); } -} + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/cadical_kitten.c b/src/sat/cadical/cadical_kitten.c new file mode 100644 index 000000000..da3738980 --- /dev/null +++ b/src/sat/cadical/cadical_kitten.c @@ -0,0 +1,2609 @@ +#include "global.h" + +#include "kitten.h" +#include "random.h" +#include "stack.h" + +#include <assert.h> +#include <inttypes.h> +#include <limits.h> +#include <stdarg.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +ABC_NAMESPACE_IMPL_START + +typedef signed char value; + +static void die (const char *fmt, ...) { + fputs ("cadical_kitten: error: ", stderr); + va_list ap; + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + fputc ('\n', stderr); + exit (1); +} + +static inline void *cadical_kitten_calloc (size_t n, size_t size) { + void *res = calloc (n, size); + if (n && size && !res) + die ("out of memory allocating '%zu * %zu' bytes", n, size); + return res; +} + +#define CALLOC(T, P, N) \ + do { \ + (P) = (T*)cadical_kitten_calloc (N, sizeof *(P)); \ + } while (0) +#define DEALLOC(P, N) free (P) + +#undef ENLARGE_STACK + +#define ENLARGE_STACK(S) \ + do { \ + CADICAL_assert (FULL_STACK (S)); \ + const size_t SIZE = SIZE_STACK (S); \ + const size_t OLD_CAPACITY = CAPACITY_STACK (S); \ + const size_t NEW_CAPACITY = OLD_CAPACITY ? 2 * OLD_CAPACITY : 1; \ + const size_t BYTES = NEW_CAPACITY * sizeof *(S).begin; \ + (S).begin = (unsigned*)realloc ((S).begin, BYTES); \ + if (!(S).begin) \ + die ("out of memory reallocating '%zu' bytes", BYTES); \ + (S).allocated = (S).begin + NEW_CAPACITY; \ + (S).end = (S).begin + SIZE; \ + } while (0) + +// Beside allocators above also use stand alone statistics counters. + +#define INC(NAME) \ + do { \ + statistics *statistics = &cadical_kitten->statistics; \ + CADICAL_assert (statistics->NAME < UINT64_MAX); \ + statistics->NAME++; \ + } while (0) + +#define ADD(NAME, DELTA) \ + do { \ + statistics *statistics = &cadical_kitten->statistics; \ + CADICAL_assert (statistics->NAME <= UINT64_MAX - (DELTA)); \ + statistics->NAME += (DELTA); \ + } while (0) + +#define KITTEN_TICKS (cadical_kitten->statistics.cadical_kitten_ticks) + +#define INVALID UINT_MAX +#define MAX_VARS ((1u << 31) - 1) + +#define CORE_FLAG (1u) +#define LEARNED_FLAG (2u) + +// clang-format off + +typedef struct kar kar; +typedef struct kink kink; +typedef struct klause klause; +typedef STACK (unsigned) klauses; +typedef unsigneds katches; + +// clang-format on + +struct kar { + unsigned level; + unsigned reason; +}; + +struct kink { + unsigned next; + unsigned prev; + uint64_t stamp; +}; + +struct klause { + unsigned aux; + unsigned size; + unsigned flags; + unsigned lits[1]; +}; + +typedef struct statistics statistics; + +struct statistics { + uint64_t learned; + uint64_t original; + uint64_t cadical_kitten_flip; + uint64_t cadical_kitten_flipped; + uint64_t cadical_kitten_sat; + uint64_t cadical_kitten_solved; + uint64_t cadical_kitten_conflicts; + uint64_t cadical_kitten_decisions; + uint64_t cadical_kitten_propagations; + uint64_t cadical_kitten_ticks; + uint64_t cadical_kitten_unknown; + uint64_t cadical_kitten_unsat; +}; + +typedef struct kimits kimits; + +struct kimits { + uint64_t ticks; +}; + +struct cadical_kitten { + // First zero initialized field in 'clear_cadical_kitten' is 'status'. + // + int status; + +#if defined(LOGGING) + bool logging; +#endif + bool antecedents; + bool learned; + + unsigned level; + unsigned propagated; + unsigned unassigned; + unsigned inconsistent; + unsigned failing; + + uint64_t generator; + + size_t lits; + size_t evars; + + size_t end_original_ref; + + struct { + unsigned first, last; + uint64_t stamp; + unsigned search; + } queue; + + // The 'size' field below is the first not zero reinitialized field + // by 'memset' in 'clear_cadical_kitten' (after 'kissat'). + + size_t size; + size_t esize; + + kar *vars; + kink *links; + value *marks; + value *values; + bool *failed; + unsigned char *phases; + unsigned *import; + katches *watches; + + unsigneds analyzed; + unsigneds assumptions; + unsigneds core; + unsigneds rcore; + unsigneds eclause; + unsigneds export_; + unsigneds klause; + unsigneds klauses; + unsigneds resolved; + unsigneds trail; + unsigneds units; + unsigneds prime[2]; + + kimits limits; + int (*terminator) (void *); + void *terminator_data; + unsigneds clause; + uint64_t initialized; + statistics statistics; +}; + +/*------------------------------------------------------------------------*/ + +static inline bool is_core_klause (klause *c) { + return c->flags & CORE_FLAG; +} + +static inline bool is_learned_klause (klause *c) { + return c->flags & LEARNED_FLAG; +} + +static inline void set_core_klause (klause *c) { c->flags |= CORE_FLAG; } + +static inline void unset_core_klause (klause *c) { c->flags &= ~CORE_FLAG; } + +static inline klause *dereference_klause (cadical_kitten *cadical_kitten, unsigned ref) { + unsigned *res = BEGIN_STACK (cadical_kitten->klauses) + ref; + CADICAL_assert (res < END_STACK (cadical_kitten->klauses)); + return (klause *) res; +} + +static inline unsigned reference_klause (cadical_kitten *cadical_kitten, const klause *c) { + const unsigned *const begin = BEGIN_STACK (cadical_kitten->klauses); + const unsigned *p = (const unsigned *) c; + CADICAL_assert (begin <= p); + CADICAL_assert (p < END_STACK (cadical_kitten->klauses)); + const unsigned res = p - begin; + return res; +} + +/*------------------------------------------------------------------------*/ + +#define KATCHES(KIT) (cadical_kitten->watches[CADICAL_assert ((KIT) < cadical_kitten->lits), (KIT)]) + +#define all_klauses(C) \ + klause *C = begin_klauses (cadical_kitten), *end_##C = end_klauses (cadical_kitten); \ + (C) != end_##C; \ + (C) = next_klause (cadical_kitten, C) + +#define all_original_klauses(C) \ + klause *C = begin_klauses (cadical_kitten), \ + *end_##C = end_original_klauses (cadical_kitten); \ + (C) != end_##C; \ + (C) = next_klause (cadical_kitten, C) + +#define all_learned_klauses(C) \ + klause *C = begin_learned_klauses (cadical_kitten), \ + *end_##C = end_klauses (cadical_kitten); \ + (C) != end_##C; \ + (C) = next_klause (cadical_kitten, C) + +#define all_kits(KIT) \ + size_t KIT = 0, KIT_##END = cadical_kitten->lits; \ + KIT != KIT_##END; \ + KIT++ + +#define BEGIN_KLAUSE(C) (C)->lits + +#define END_KLAUSE(C) (BEGIN_KLAUSE (C) + (C)->size) + +#define all_literals_in_klause(KIT, C) \ + unsigned KIT, *KIT##_PTR = BEGIN_KLAUSE (C), \ + *KIT##_END = END_KLAUSE (C); \ + KIT##_PTR != KIT##_END && ((KIT = *KIT##_PTR), true); \ + ++KIT##_PTR + +#define all_antecedents(REF, C) \ + unsigned REF, *REF##_PTR = antecedents (C), \ + *REF##_END = REF##_PTR + (C)->aux; \ + REF##_PTR != REF##_END && ((REF = *REF##_PTR), true); \ + ++REF##_PTR + +#ifdef LOGGING + +#define logging (cadical_kitten->logging) + +static void log_basic (cadical_kitten *, const char *, ...) + __attribute__ ((format (printf, 2, 3))); + +static void log_basic (cadical_kitten *cadical_kitten, const char *fmt, ...) { + CADICAL_assert (logging); + printf ("c KITTEN %u ", cadical_kitten->level); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + fputc ('\n', stdout); + fflush (stdout); +} + +static void log_reference (cadical_kitten *, unsigned, const char *, ...) + __attribute__ ((format (printf, 3, 4))); + +static void log_reference (cadical_kitten *cadical_kitten, unsigned ref, const char *fmt, + ...) { + klause *c = dereference_klause (cadical_kitten, ref); + CADICAL_assert (logging); + printf ("c KITTEN %u ", cadical_kitten->level); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + if (is_learned_klause (c)) { + fputs (" learned", stdout); + if (c->aux) + printf ("[%u]", c->aux); + } else { + fputs (" original", stdout); + if (c->aux != INVALID) + printf ("[%u]", c->aux); + } + printf (" size %u clause[%u]", c->size, ref); + value *values = cadical_kitten->values; + kar *vars = cadical_kitten->vars; + for (all_literals_in_klause (lit, c)) { + printf (" %u", lit); + const value value = values[lit]; + if (value) + printf ("@%u=%d", vars[lit / 2].level, (int) value); + } + fputc ('\n', stdout); + fflush (stdout); +} + +static void log_literals (cadical_kitten *, unsigned *, unsigned, const char *, ...) + __attribute__ ((format (printf, 4, 5))); + +static void log_literals (cadical_kitten *cadical_kitten, unsigned *lits, unsigned size, + const char *fmt, ...) { + CADICAL_assert (logging); + printf ("c KITTEN %u ", cadical_kitten->level); + va_list ap; + va_start (ap, fmt); + vprintf (fmt, ap); + va_end (ap); + value *values = cadical_kitten->values; + kar *vars = cadical_kitten->vars; + for (unsigned i = 0; i < size; i++) { + const unsigned lit = lits[i]; + printf (" %u", lit); + const value value = values[lit]; + if (value) + printf ("@%u=%d", vars[lit / 2].level, (int) value); + } + fputc ('\n', stdout); + fflush (stdout); +} + +#define LOG(...) \ + do { \ + if (logging) \ + log_basic (cadical_kitten, __VA_ARGS__); \ + } while (0) + +#define ROG(...) \ + do { \ + if (logging) \ + log_reference (cadical_kitten, __VA_ARGS__); \ + } while (0) + +#define LOGLITS(...) \ + do { \ + if (logging) \ + log_literals (cadical_kitten, __VA_ARGS__); \ + } while (0) + +#else + +#define LOG(...) \ + do { \ + } while (0) +#define ROG(...) \ + do { \ + } while (0) +#define LOGLITS(...) \ + do { \ + } while (0) + +#endif + +static void check_queue (cadical_kitten *cadical_kitten) { +#ifdef CHECK_KITTEN + const unsigned vars = cadical_kitten->lits / 2; + unsigned found = 0, prev = INVALID; + kink *links = cadical_kitten->links; + uint64_t stamp = 0; + for (unsigned idx = cadical_kitten->queue.first, next; idx != INVALID; + idx = next) { + kink *link = links + idx; + CADICAL_assert (link->prev == prev); + CADICAL_assert (!found || stamp < link->stamp); + CADICAL_assert (link->stamp < cadical_kitten->queue.stamp); + stamp = link->stamp; + next = link->next; + prev = idx; + found++; + } + CADICAL_assert (found == vars); + unsigned next = INVALID; + found = 0; + for (unsigned idx = cadical_kitten->queue.last, prev; idx != INVALID; + idx = prev) { + kink *link = links + idx; + CADICAL_assert (link->next == next); + prev = link->prev; + next = idx; + found++; + } + CADICAL_assert (found == vars); + value *values = cadical_kitten->values; + bool first = true; + for (unsigned idx = cadical_kitten->queue.search, next; idx != INVALID; + idx = next) { + kink *link = links + idx; + next = link->next; + const unsigned lit = 2 * idx; + CADICAL_assert (first || values[lit]); + first = false; + } +#else + (void) cadical_kitten; +#endif +} + +static void update_search (cadical_kitten *cadical_kitten, unsigned idx) { + if (cadical_kitten->queue.search == idx) + return; + cadical_kitten->queue.search = idx; + LOG ("search updated to %u stamped %" PRIu64, idx, + cadical_kitten->links[idx].stamp); +} + +static void enqueue (cadical_kitten *cadical_kitten, unsigned idx) { + LOG ("enqueue %u", idx); + kink *links = cadical_kitten->links; + kink *l = links + idx; + const unsigned last = cadical_kitten->queue.last; + if (last == INVALID) + cadical_kitten->queue.first = idx; + else + links[last].next = idx; + l->prev = last; + l->next = INVALID; + cadical_kitten->queue.last = idx; + l->stamp = cadical_kitten->queue.stamp++; + LOG ("stamp %" PRIu64, l->stamp); +} + +static void dequeue (cadical_kitten *cadical_kitten, unsigned idx) { + LOG ("dequeue %u", idx); + kink *links = cadical_kitten->links; + kink *l = links + idx; + const unsigned prev = l->prev; + const unsigned next = l->next; + if (prev == INVALID) + cadical_kitten->queue.first = next; + else + links[prev].next = next; + if (next == INVALID) + cadical_kitten->queue.last = prev; + else + links[next].prev = prev; +} + +static void init_queue (cadical_kitten *cadical_kitten, size_t old_vars, size_t new_vars) { + for (size_t idx = old_vars; idx < new_vars; idx++) { + CADICAL_assert (!cadical_kitten->values[2 * idx]); + CADICAL_assert (cadical_kitten->unassigned < UINT_MAX); + cadical_kitten->unassigned++; + enqueue (cadical_kitten, idx); + } + LOG ("initialized decision queue from %zu to %zu", old_vars, new_vars); + update_search (cadical_kitten, cadical_kitten->queue.last); + check_queue (cadical_kitten); +} + +static void initialize_cadical_kitten (cadical_kitten *cadical_kitten) { + cadical_kitten->queue.first = INVALID; + cadical_kitten->queue.last = INVALID; + cadical_kitten->inconsistent = INVALID; + cadical_kitten->failing = INVALID; + cadical_kitten->queue.search = INVALID; + cadical_kitten->terminator = 0; + cadical_kitten->terminator_data = 0; + cadical_kitten->limits.ticks = UINT64_MAX; + cadical_kitten->generator = cadical_kitten->initialized++; +} + +static void clear_cadical_kitten (cadical_kitten *cadical_kitten) { + size_t bytes = (char *) &cadical_kitten->size - (char *) &cadical_kitten->status; + memset (&cadical_kitten->status, 0, bytes); + memset (&cadical_kitten->statistics, 0, sizeof (statistics)); + initialize_cadical_kitten (cadical_kitten); +} + +#define RESIZE1(T, P) \ + do { \ + void *OLD_PTR = (P); \ + CALLOC (T, (P), new_size / 2); \ + const size_t BYTES = old_vars * sizeof *(P); \ + memcpy ((P), OLD_PTR, BYTES); \ + void *NEW_PTR = (P); \ + (P) = (T*)OLD_PTR; \ + DEALLOC ((P), old_size / 2); \ + (P) = (T*)NEW_PTR; \ + } while (0) + +#define RESIZE2(T, P) \ + do { \ + void *OLD_PTR = (P); \ + CALLOC (T, (P), new_size); \ + const size_t BYTES = old_lits * sizeof *(P); \ + memcpy ((P), OLD_PTR, BYTES); \ + void *NEW_PTR = (P); \ + (P) = (T*)OLD_PTR; \ + DEALLOC ((P), old_size); \ + (P) = (T*)NEW_PTR; \ + } while (0) + +static void enlarge_internal (cadical_kitten *cadical_kitten, size_t lit) { + const size_t new_lits = (lit | 1) + 1; + const size_t old_lits = cadical_kitten->lits; + CADICAL_assert (old_lits <= lit); + CADICAL_assert (old_lits < new_lits); + CADICAL_assert ((lit ^ 1) < new_lits); + CADICAL_assert (lit < new_lits); + const size_t old_size = cadical_kitten->size; + const unsigned new_vars = new_lits / 2; + const unsigned old_vars = old_lits / 2; + if (old_size < new_lits) { + size_t new_size = old_size ? 2 * old_size : 2; + while (new_size <= lit) + new_size *= 2; + LOG ("internal literals resized to %zu from %zu (requested %zu)", + new_size, old_size, new_lits); + + RESIZE1 (value, cadical_kitten->marks); + RESIZE1 (unsigned char, cadical_kitten->phases); + RESIZE2 (value, cadical_kitten->values); + RESIZE2 (bool, cadical_kitten->failed); + RESIZE1 (kar, cadical_kitten->vars); + RESIZE1 (kink, cadical_kitten->links); + RESIZE2 (katches, cadical_kitten->watches); + + cadical_kitten->size = new_size; + } + cadical_kitten->lits = new_lits; + init_queue (cadical_kitten, old_vars, new_vars); + LOG ("internal literals activated until %zu literals", new_lits); + return; +} + +static const char *status_to_string (int status) { + switch (status) { + case 10: + return "formula satisfied"; + case 11: + return "formula satisfied and prime implicant computed"; + case 20: + return "formula inconsistent"; + case 21: + return "formula inconsistent and core computed"; + default: + CADICAL_assert (!status); + return "formula unsolved"; + } +} + +static void invalid_api_usage (const char *fun, const char *fmt, ...) { + fprintf (stderr, "cadical_kitten: fatal error: invalid API usage in '%s': ", fun); + va_list ap; + va_start (ap, fmt); + vfprintf (stderr, fmt, ap); + va_end (ap); + fputc ('\n', stderr); + fflush (stderr); + abort (); +} + +#define INVALID_API_USAGE(...) invalid_api_usage (__func__, __VA_ARGS__) + +#define REQUIRE_INITIALIZED() \ + do { \ + if (!cadical_kitten) \ + INVALID_API_USAGE ("solver argument zero"); \ + } while (0) + +#define REQUIRE_STATUS(EXPECTED) \ + do { \ + REQUIRE_INITIALIZED (); \ + if (cadical_kitten->status != (EXPECTED)) \ + INVALID_API_USAGE ("invalid status '%s' (expected '%s')", \ + status_to_string (cadical_kitten->status), \ + status_to_string (EXPECTED)); \ + } while (0) + +#define UPDATE_STATUS(STATUS) \ + do { \ + if (cadical_kitten->status != (STATUS)) \ + LOG ("updating status from '%s' to '%s'", \ + status_to_string (cadical_kitten->status), status_to_string (STATUS)); \ + else \ + LOG ("keeping status at '%s'", status_to_string (STATUS)); \ + cadical_kitten->status = (STATUS); \ + } while (0) + +cadical_kitten *cadical_kitten_init (void) { + cadical_kitten *cadical_kitten; + CALLOC (struct cadical_kitten, cadical_kitten, 1); + initialize_cadical_kitten (cadical_kitten); + return cadical_kitten; +} + +#ifdef LOGGING +void cadical_kitten_set_logging (cadical_kitten *cadical_kitten) { logging = true; } +#endif + +void cadical_kitten_track_antecedents (cadical_kitten *cadical_kitten) { + REQUIRE_STATUS (0); + + if (cadical_kitten->learned) + INVALID_API_USAGE ("can not start tracking antecedents after learning"); + + LOG ("enabling antecedents tracking"); + cadical_kitten->antecedents = true; +} + +void cadical_kitten_randomize_phases (cadical_kitten *cadical_kitten) { + REQUIRE_INITIALIZED (); + + LOG ("randomizing phases"); + + unsigned char *phases = cadical_kitten->phases; + const unsigned vars = cadical_kitten->size / 2; + + uint64_t random = kissat_next_random64 (&cadical_kitten->generator); + + unsigned i = 0; + const unsigned rest = vars & ~63u; + + while (i != rest) { + uint64_t *p = (uint64_t *) (phases + i); + p[0] = (random >> 0) & 0x0101010101010101; + p[1] = (random >> 1) & 0x0101010101010101; + p[2] = (random >> 2) & 0x0101010101010101; + p[3] = (random >> 3) & 0x0101010101010101; + p[4] = (random >> 4) & 0x0101010101010101; + p[5] = (random >> 5) & 0x0101010101010101; + p[6] = (random >> 6) & 0x0101010101010101; + p[7] = (random >> 7) & 0x0101010101010101; + random = kissat_next_random64 (&cadical_kitten->generator); + i += 64; + } + + unsigned shift = 0; + while (i != vars) + phases[i++] = (random >> shift++) & 1; +} + +void cadical_kitten_flip_phases (cadical_kitten *cadical_kitten) { + REQUIRE_INITIALIZED (); + + LOG ("flipping phases"); + + unsigned char *phases = cadical_kitten->phases; + const unsigned vars = cadical_kitten->size / 2; + + unsigned i = 0; + const unsigned rest = vars & ~7u; + + while (i != rest) { + uint64_t *p = (uint64_t *) (phases + i); + *p ^= 0x0101010101010101; + i += 8; + } + + while (i != vars) + phases[i++] ^= 1; +} + +void cadical_kitten_no_ticks_limit (cadical_kitten *cadical_kitten) { + REQUIRE_INITIALIZED (); + LOG ("forcing no ticks limit"); + cadical_kitten->limits.ticks = UINT64_MAX; +} + +uint64_t cadical_kitten_current_ticks (cadical_kitten *cadical_kitten) { + REQUIRE_INITIALIZED (); + const uint64_t current = KITTEN_TICKS; + return current; +} + +void cadical_kitten_set_ticks_limit (cadical_kitten *cadical_kitten, uint64_t delta) { + REQUIRE_INITIALIZED (); + const uint64_t current = KITTEN_TICKS; + uint64_t limit; + if (UINT64_MAX - delta <= current) { + LOG ("forcing unlimited ticks limit"); + limit = UINT64_MAX; + } else { + limit = current + delta; + LOG ("new limit of %" PRIu64 " ticks after %" PRIu64, limit, delta); + } + + cadical_kitten->limits.ticks = limit; +} + +void cadical_kitten_no_terminator (cadical_kitten *cadical_kitten) { + REQUIRE_INITIALIZED (); + LOG ("removing terminator"); + cadical_kitten->terminator = 0; + cadical_kitten->terminator_data = 0; +} + +void cadical_kitten_set_terminator (cadical_kitten *cadical_kitten, void *data, + int (*terminator) (void *)) { + REQUIRE_INITIALIZED (); + LOG ("setting terminator"); + cadical_kitten->terminator = terminator; + cadical_kitten->terminator_data = data; +} + +static void shuffle_unsigned_array (cadical_kitten *cadical_kitten, size_t size, + unsigned *a) { + for (size_t i = 0; i < size; i++) { + const size_t j = kissat_pick_random (&cadical_kitten->generator, 0, i); + if (j == i) + continue; + const unsigned first = a[i]; + const unsigned second = a[j]; + a[i] = second; + a[j] = first; + } +} + +static void shuffle_unsigned_stack (cadical_kitten *cadical_kitten, unsigneds *stack) { + const size_t size = SIZE_STACK (*stack); + unsigned *a = BEGIN_STACK (*stack); + shuffle_unsigned_array (cadical_kitten, size, a); +} + +static void shuffle_katches (cadical_kitten *cadical_kitten) { + LOG ("shuffling watch lists"); + for (size_t lit = 0; lit < cadical_kitten->lits; lit++) + shuffle_unsigned_stack (cadical_kitten, &KATCHES (lit)); +} + +static void shuffle_queue (cadical_kitten *cadical_kitten) { + LOG ("shuffling variable decision order"); + + const unsigned vars = cadical_kitten->lits / 2; + for (unsigned i = 0; i < vars; i++) { + const unsigned idx = kissat_pick_random (&cadical_kitten->generator, 0, vars); + dequeue (cadical_kitten, idx); + enqueue (cadical_kitten, idx); + } + update_search (cadical_kitten, cadical_kitten->queue.last); +} + +static void shuffle_units (cadical_kitten *cadical_kitten) { + LOG ("shuffling units"); + shuffle_unsigned_stack (cadical_kitten, &cadical_kitten->units); +} + +void cadical_kitten_shuffle_clauses (cadical_kitten *cadical_kitten) { + REQUIRE_STATUS (0); + shuffle_queue (cadical_kitten); + shuffle_katches (cadical_kitten); + shuffle_units (cadical_kitten); +} + +static inline unsigned *antecedents (klause *c) { + CADICAL_assert (is_learned_klause (c)); + return c->lits + c->size; +} + +static inline void watch_klause (cadical_kitten *cadical_kitten, unsigned lit, + unsigned ref) { + ROG (ref, "watching %u in", lit); + katches *watches = &KATCHES (lit); + PUSH_STACK (*watches, ref); +} + +static inline void connect_new_klause (cadical_kitten *cadical_kitten, unsigned ref) { + ROG (ref, "new"); + + klause *c = dereference_klause (cadical_kitten, ref); + + if (!c->size) { + if (cadical_kitten->inconsistent == INVALID) { + ROG (ref, "registering inconsistent empty"); + cadical_kitten->inconsistent = ref; + } else + ROG (ref, "ignoring inconsistent empty"); + } else if (c->size == 1) { + ROG (ref, "watching unit"); + PUSH_STACK (cadical_kitten->units, ref); + } else { + watch_klause (cadical_kitten, c->lits[0], ref); + watch_klause (cadical_kitten, c->lits[1], ref); + } +} + +static unsigned new_reference (cadical_kitten *cadical_kitten) { + size_t ref = SIZE_STACK (cadical_kitten->klauses); + if (ref >= INVALID) { + die ("maximum number of literals exhausted"); + } + const unsigned res = (unsigned) ref; + CADICAL_assert (res != INVALID); + INC (cadical_kitten_ticks); + return res; +} + +static void new_original_klause (cadical_kitten *cadical_kitten, unsigned id) { + unsigned res = new_reference (cadical_kitten); + unsigned size = SIZE_STACK (cadical_kitten->klause); + unsigneds *klauses = &cadical_kitten->klauses; + PUSH_STACK (*klauses, id); + PUSH_STACK (*klauses, size); + PUSH_STACK (*klauses, 0); + for (all_stack (unsigned, lit, cadical_kitten->klause)) + PUSH_STACK (*klauses, lit); + connect_new_klause (cadical_kitten, res); + cadical_kitten->end_original_ref = SIZE_STACK (*klauses); + cadical_kitten->statistics.original++; +} + +static void enlarge_external (cadical_kitten *cadical_kitten, size_t eidx) { + const size_t old_size = cadical_kitten->esize; + const unsigned old_evars = cadical_kitten->evars; + CADICAL_assert (old_evars <= eidx); + const unsigned new_evars = eidx + 1; + if (old_size <= eidx) { + size_t new_size = old_size ? 2 * old_size : 1; + while (new_size <= eidx) + new_size *= 2; + LOG ("external resizing to %zu variables from %zu (requested %u)", + new_size, old_size, new_evars); + unsigned *old_import = cadical_kitten->import; + CALLOC (unsigned, cadical_kitten->import, new_size); + const size_t bytes = old_evars * sizeof *cadical_kitten->import; + memcpy (cadical_kitten->import, old_import, bytes); + DEALLOC (old_import, old_size); + cadical_kitten->esize = new_size; + } + cadical_kitten->evars = new_evars; + LOG ("external variables enlarged to %u", new_evars); +} + +static unsigned import_literal (cadical_kitten *cadical_kitten, unsigned elit) { + const unsigned eidx = elit / 2; + if (eidx >= cadical_kitten->evars) + enlarge_external (cadical_kitten, eidx); + + unsigned iidx = cadical_kitten->import[eidx]; + if (!iidx) { + iidx = SIZE_STACK (cadical_kitten->export_); + PUSH_STACK (cadical_kitten->export_, eidx); + cadical_kitten->import[eidx] = iidx + 1; + } else + iidx--; + unsigned ilit = 2 * iidx + (elit & 1); + LOG ("imported external literal %u as internal literal %u", elit, ilit); + if (ilit >= cadical_kitten->lits) + enlarge_internal (cadical_kitten, ilit); + return ilit; +} + +static unsigned export_literal (cadical_kitten *cadical_kitten, unsigned ilit) { + const unsigned iidx = ilit / 2; + CADICAL_assert (iidx < SIZE_STACK (cadical_kitten->export_)); + const unsigned eidx = PEEK_STACK (cadical_kitten->export_, iidx); + const unsigned elit = 2 * eidx + (ilit & 1); + return elit; +} + +unsigned cadical_new_learned_klause (cadical_kitten *cadical_kitten) { + unsigned res = new_reference (cadical_kitten); + unsigneds *klauses = &cadical_kitten->klauses; + const size_t size = SIZE_STACK (cadical_kitten->klause); + CADICAL_assert (size <= UINT_MAX); + const size_t aux = + cadical_kitten->antecedents ? SIZE_STACK (cadical_kitten->resolved) : 0; + CADICAL_assert (aux <= UINT_MAX); + PUSH_STACK (*klauses, (unsigned) aux); + PUSH_STACK (*klauses, (unsigned) size); + PUSH_STACK (*klauses, LEARNED_FLAG); + for (all_stack (unsigned, lit, cadical_kitten->klause)) + PUSH_STACK (*klauses, lit); + if (aux) + for (all_stack (unsigned, ref, cadical_kitten->resolved)) + PUSH_STACK (*klauses, ref); + connect_new_klause (cadical_kitten, res); + cadical_kitten->learned = true; + cadical_kitten->statistics.learned++; + return res; +} + +void cadical_kitten_clear (cadical_kitten *cadical_kitten) { + LOG ("clear cadical_kitten of size %zu", cadical_kitten->size); + + CADICAL_assert (EMPTY_STACK (cadical_kitten->analyzed)); + CADICAL_assert (EMPTY_STACK (cadical_kitten->eclause)); + CADICAL_assert (EMPTY_STACK (cadical_kitten->resolved)); + + CLEAR_STACK (cadical_kitten->assumptions); + CLEAR_STACK (cadical_kitten->core); + CLEAR_STACK (cadical_kitten->klause); + CLEAR_STACK (cadical_kitten->klauses); + CLEAR_STACK (cadical_kitten->trail); + CLEAR_STACK (cadical_kitten->units); + CLEAR_STACK (cadical_kitten->clause); + CLEAR_STACK (cadical_kitten->prime[0]); + CLEAR_STACK (cadical_kitten->prime[1]); + + for (all_kits (kit)) + CLEAR_STACK (KATCHES (kit)); + + while (!EMPTY_STACK (cadical_kitten->export_)) + cadical_kitten->import[POP_STACK (cadical_kitten->export_)] = 0; + + const size_t lits = cadical_kitten->size; + const unsigned vars = lits / 2; + +#ifndef CADICAL_NDEBUG + for (unsigned i = 0; i < vars; i++) + CADICAL_assert (!cadical_kitten->marks[i]); +#endif + + memset (cadical_kitten->phases, 0, vars); + memset (cadical_kitten->values, 0, lits); + memset (cadical_kitten->failed, 0, lits); + memset (cadical_kitten->vars, 0, vars); + + clear_cadical_kitten (cadical_kitten); +} + +void cadical_kitten_release (cadical_kitten *cadical_kitten) { + RELEASE_STACK (cadical_kitten->analyzed); + RELEASE_STACK (cadical_kitten->assumptions); + RELEASE_STACK (cadical_kitten->core); + RELEASE_STACK (cadical_kitten->eclause); + RELEASE_STACK (cadical_kitten->export_); + RELEASE_STACK (cadical_kitten->klause); + RELEASE_STACK (cadical_kitten->klauses); + RELEASE_STACK (cadical_kitten->resolved); + RELEASE_STACK (cadical_kitten->trail); + RELEASE_STACK (cadical_kitten->units); + RELEASE_STACK (cadical_kitten->clause); + RELEASE_STACK (cadical_kitten->prime[0]); + RELEASE_STACK (cadical_kitten->prime[1]); + + for (size_t lit = 0; lit < cadical_kitten->size; lit++) + RELEASE_STACK (cadical_kitten->watches[lit]); + + const size_t lits = cadical_kitten->size; + const unsigned vars = lits / 2; + DEALLOC (cadical_kitten->marks, vars); + DEALLOC (cadical_kitten->phases, vars); + DEALLOC (cadical_kitten->values, lits); + DEALLOC (cadical_kitten->failed, lits); + DEALLOC (cadical_kitten->vars, vars); + DEALLOC (cadical_kitten->links, vars); + DEALLOC (cadical_kitten->watches, lits); + DEALLOC (cadical_kitten->import, cadical_kitten->esize); + (void) vars; + + free (cadical_kitten); +} + +static inline void move_to_front (cadical_kitten *cadical_kitten, unsigned idx) { + if (idx == cadical_kitten->queue.last) + return; + LOG ("move to front variable %u", idx); + dequeue (cadical_kitten, idx); + enqueue (cadical_kitten, idx); + CADICAL_assert (cadical_kitten->values[2 * idx]); +} + +static inline void assign (cadical_kitten *cadical_kitten, unsigned lit, unsigned reason) { +#ifdef LOGGING + if (reason == INVALID) + LOG ("assign %u as decision", lit); + else + ROG (reason, "assign %u reason", lit); +#endif + value *values = cadical_kitten->values; + const unsigned not_lit = lit ^ 1; + CADICAL_assert (!values[lit]); + CADICAL_assert (!values[not_lit]); + values[lit] = 1; + values[not_lit] = -1; + const unsigned idx = lit / 2; + const unsigned sign = lit & 1; + cadical_kitten->phases[idx] = sign; + PUSH_STACK (cadical_kitten->trail, lit); + kar *v = cadical_kitten->vars + idx; + v->level = cadical_kitten->level; + if (!v->level) { + CADICAL_assert (reason != INVALID); + klause *c = dereference_klause (cadical_kitten, reason); + if (c->size > 1) { + if (cadical_kitten->antecedents) { + PUSH_STACK (cadical_kitten->resolved, reason); + for (all_literals_in_klause (other, c)) + if (other != lit) { + const unsigned other_idx = other / 2; + const unsigned other_ref = cadical_kitten->vars[other_idx].reason; + CADICAL_assert (other_ref != INVALID); + PUSH_STACK (cadical_kitten->resolved, other_ref); + } + } + PUSH_STACK (cadical_kitten->klause, lit); + reason = cadical_new_learned_klause (cadical_kitten); + CLEAR_STACK (cadical_kitten->resolved); + CLEAR_STACK (cadical_kitten->klause); + } + } + v->reason = reason; + CADICAL_assert (cadical_kitten->unassigned); + cadical_kitten->unassigned--; +} + +static inline unsigned propagate_literal (cadical_kitten *cadical_kitten, unsigned lit) { + LOG ("propagating %u", lit); + value *values = cadical_kitten->values; + CADICAL_assert (values[lit] > 0); + const unsigned not_lit = lit ^ 1; + katches *watches = cadical_kitten->watches + not_lit; + unsigned conflict = INVALID; + unsigned *q = BEGIN_STACK (*watches); + const unsigned *const end_watches = END_STACK (*watches); + unsigned const *p = q; + uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; + while (p != end_watches) { + const unsigned ref = *q++ = *p++; + klause *c = dereference_klause (cadical_kitten, ref); + CADICAL_assert (c->size > 1); + unsigned *lits = c->lits; + const unsigned other = lits[0] ^ lits[1] ^ not_lit; + const value other_value = values[other]; + ticks++; + if (other_value > 0) + continue; + value replacement_value = -1; + unsigned replacement = INVALID; + const unsigned *const end_lits = lits + c->size; + unsigned *r; + for (r = lits + 2; r != end_lits; r++) { + replacement = *r; + replacement_value = values[replacement]; + if (replacement_value >= 0) + break; + } + if (replacement_value >= 0) { + CADICAL_assert (replacement != INVALID); + ROG (ref, "unwatching %u in", not_lit); + lits[0] = other; + lits[1] = replacement; + *r = not_lit; + watch_klause (cadical_kitten, replacement, ref); + q--; + } else if (other_value < 0) { + ROG (ref, "conflict"); + INC (cadical_kitten_conflicts); + conflict = ref; + break; + } else { + CADICAL_assert (!other_value); + assign (cadical_kitten, other, ref); + } + } + while (p != end_watches) + *q++ = *p++; + SET_END_OF_STACK (*watches, q); + ADD (cadical_kitten_ticks, ticks); + return conflict; +} + +static inline unsigned propagate (cadical_kitten *cadical_kitten) { + CADICAL_assert (cadical_kitten->inconsistent == INVALID); + unsigned propagated = 0; + unsigned conflict = INVALID; + while (conflict == INVALID && + cadical_kitten->propagated < SIZE_STACK (cadical_kitten->trail)) { + if (cadical_kitten->terminator && + cadical_kitten->terminator (cadical_kitten->terminator_data)) { + break; + } + const unsigned lit = PEEK_STACK (cadical_kitten->trail, cadical_kitten->propagated); + conflict = propagate_literal (cadical_kitten, lit); + cadical_kitten->propagated++; + propagated++; + } + ADD (cadical_kitten_propagations, propagated); + return conflict; +} + +static void bump (cadical_kitten *cadical_kitten) { + value *marks = cadical_kitten->marks; + for (all_stack (unsigned, idx, cadical_kitten->analyzed)) { + marks[idx] = 0; + move_to_front (cadical_kitten, idx); + } + check_queue (cadical_kitten); +} + +static inline void unassign (cadical_kitten *cadical_kitten, value *values, unsigned lit) { + const unsigned not_lit = lit ^ 1; + CADICAL_assert (values[lit]); + CADICAL_assert (values[not_lit]); + const unsigned idx = lit / 2; +#ifdef LOGGING + kar *var = cadical_kitten->vars + idx; + cadical_kitten->level = var->level; + LOG ("unassign %u", lit); +#endif + values[lit] = values[not_lit] = 0; + CADICAL_assert (cadical_kitten->unassigned < cadical_kitten->lits / 2); + cadical_kitten->unassigned++; + kink *links = cadical_kitten->links; + kink *link = links + idx; + if (link->stamp > links[cadical_kitten->queue.search].stamp) + update_search (cadical_kitten, idx); +} + +static void backtrack (cadical_kitten *cadical_kitten, unsigned jump) { + check_queue (cadical_kitten); + CADICAL_assert (jump < cadical_kitten->level); + LOG ("back%s to level %u", + (cadical_kitten->level == jump + 1 ? "tracking" : "jumping"), jump); + kar *vars = cadical_kitten->vars; + value *values = cadical_kitten->values; + unsigneds *trail = &cadical_kitten->trail; + while (!EMPTY_STACK (*trail)) { + const unsigned lit = TOP_STACK (*trail); + const unsigned idx = lit / 2; + const unsigned level = vars[idx].level; + if (level == jump) + break; + (void) POP_STACK (*trail); + unassign (cadical_kitten, values, lit); + } + cadical_kitten->propagated = SIZE_STACK (*trail); + cadical_kitten->level = jump; + check_queue (cadical_kitten); +} + +void cadical_completely_backtrack_to_root_level (cadical_kitten *cadical_kitten) { + check_queue (cadical_kitten); + LOG ("completely backtracking to level 0"); + value *values = cadical_kitten->values; + unsigneds *trail = &cadical_kitten->trail; + unsigneds *units = &cadical_kitten->units; +#ifndef CADICAL_NDEBUG + kar *vars = cadical_kitten->vars; +#endif + for (all_stack (unsigned, lit, *trail)) { + CADICAL_assert (vars[lit / 2].level); + unassign (cadical_kitten, values, lit); + } + CLEAR_STACK (*trail); + for (all_stack (unsigned, ref, *units)) { + klause *c = dereference_klause (cadical_kitten, ref); + CADICAL_assert (c->size == 1); + const unsigned unit = c->lits[0]; + const value value = values[unit]; + if (value <= 0) + continue; + unassign (cadical_kitten, values, unit); + } + cadical_kitten->propagated = 0; + cadical_kitten->level = 0; + check_queue (cadical_kitten); +} + +static void analyze (cadical_kitten *cadical_kitten, unsigned conflict) { + CADICAL_assert (cadical_kitten->level); + CADICAL_assert (cadical_kitten->inconsistent == INVALID); + CADICAL_assert (EMPTY_STACK (cadical_kitten->analyzed)); + CADICAL_assert (EMPTY_STACK (cadical_kitten->resolved)); + CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); + PUSH_STACK (cadical_kitten->klause, INVALID); + unsigned reason = conflict; + value *marks = cadical_kitten->marks; + const kar *const vars = cadical_kitten->vars; + const unsigned level = cadical_kitten->level; + unsigned const *p = END_STACK (cadical_kitten->trail); + unsigned open = 0, jump = 0, size = 1, uip; + for (;;) { + CADICAL_assert (reason != INVALID); + klause *c = dereference_klause (cadical_kitten, reason); + CADICAL_assert (c); + ROG (reason, "analyzing"); + PUSH_STACK (cadical_kitten->resolved, reason); + for (all_literals_in_klause (lit, c)) { + const unsigned idx = lit / 2; + if (marks[idx]) + continue; + CADICAL_assert (cadical_kitten->values[lit] < 0); + LOG ("analyzed %u", lit); + marks[idx] = true; + PUSH_STACK (cadical_kitten->analyzed, idx); + const kar *const v = vars + idx; + const unsigned tmp = v->level; + if (tmp < level) { + if (tmp > jump) { + jump = tmp; + if (size > 1) { + const unsigned other = PEEK_STACK (cadical_kitten->klause, 1); + POKE_STACK (cadical_kitten->klause, 1, lit); + lit = other; + } + } + PUSH_STACK (cadical_kitten->klause, lit); + size++; + } else + open++; + } + unsigned idx; + do { + CADICAL_assert (BEGIN_STACK (cadical_kitten->trail) < p); + uip = *--p; + } while (!marks[idx = uip / 2]); + CADICAL_assert (open); + if (!--open) + break; + reason = vars[idx].reason; + } + const unsigned not_uip = uip ^ 1; + LOG ("first UIP %u jump level %u size %u", not_uip, jump, size); + POKE_STACK (cadical_kitten->klause, 0, not_uip); + bump (cadical_kitten); + CLEAR_STACK (cadical_kitten->analyzed); + const unsigned learned_ref = cadical_new_learned_klause (cadical_kitten); + CLEAR_STACK (cadical_kitten->resolved); + CLEAR_STACK (cadical_kitten->klause); + backtrack (cadical_kitten, jump); + assign (cadical_kitten, not_uip, learned_ref); +} + +static void failing (cadical_kitten *cadical_kitten) { + CADICAL_assert (cadical_kitten->inconsistent == INVALID); + CADICAL_assert (!EMPTY_STACK (cadical_kitten->assumptions)); + CADICAL_assert (EMPTY_STACK (cadical_kitten->analyzed)); + CADICAL_assert (EMPTY_STACK (cadical_kitten->resolved)); + CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); + LOG ("analyzing failing assumptions"); + const value *const values = cadical_kitten->values; + const kar *const vars = cadical_kitten->vars; + unsigned failed_clashing = INVALID; + unsigned first_failed = INVALID; + unsigned failed_unit = INVALID; + for (all_stack (unsigned, lit, cadical_kitten->assumptions)) { + if (values[lit] >= 0) + continue; + if (first_failed == INVALID) + first_failed = lit; + const unsigned failed_idx = lit / 2; + const kar *const failed_var = vars + failed_idx; + if (!failed_var->level) { + failed_unit = lit; + break; + } + if (failed_clashing == INVALID && failed_var->reason == INVALID) + failed_clashing = lit; + } + unsigned failed; + if (failed_unit != INVALID) + failed = failed_unit; + else if (failed_clashing != INVALID) + failed = failed_clashing; + else + failed = first_failed; + CADICAL_assert (failed != INVALID); + const unsigned failed_idx = failed / 2; + const kar *const failed_var = vars + failed_idx; + const unsigned failed_reason = failed_var->reason; + LOG ("first failed assumption %u", failed); + cadical_kitten->failed[failed] = true; + + if (failed_unit != INVALID) { + CADICAL_assert (dereference_klause (cadical_kitten, failed_reason)->size == 1); + LOG ("root-level falsified assumption %u", failed); + cadical_kitten->failing = failed_reason; + ROG (cadical_kitten->failing, "failing reason"); + return; + } + + const unsigned not_failed = failed ^ 1; + if (failed_clashing != INVALID) { + LOG ("clashing with negated assumption %u", not_failed); + cadical_kitten->failed[not_failed] = true; + CADICAL_assert (cadical_kitten->failing == INVALID); + return; + } + + value *marks = cadical_kitten->marks; + CADICAL_assert (!marks[failed_idx]); + marks[failed_idx] = true; + PUSH_STACK (cadical_kitten->analyzed, failed_idx); + PUSH_STACK (cadical_kitten->klause, not_failed); + + unsigneds work; + INIT_STACK (work); + + LOGLITS (BEGIN_STACK (cadical_kitten->trail), SIZE_STACK (cadical_kitten->trail), + "trail"); + + CADICAL_assert (SIZE_STACK (cadical_kitten->trail)); + unsigned const *p = END_STACK (cadical_kitten->trail); + unsigned open = 1; + for (;;) { + if (!open) + break; + open--; + unsigned idx, uip; + do { + CADICAL_assert (BEGIN_STACK (cadical_kitten->trail) < p); + uip = *--p; + } while (!marks[idx = uip / 2]); + + const kar *var = vars + idx; + const unsigned reason = var->reason; + if (reason == INVALID) { + unsigned lit = 2 * idx; + if (values[lit] < 0) + lit ^= 1; + LOG ("failed assumption %u", lit); + CADICAL_assert (!cadical_kitten->failed[lit]); + cadical_kitten->failed[lit] = true; + const unsigned not_lit = lit ^ 1; + PUSH_STACK (cadical_kitten->klause, not_lit); + } else { + ROG (reason, "analyzing"); + PUSH_STACK (cadical_kitten->resolved, reason); + klause *c = dereference_klause (cadical_kitten, reason); + for (all_literals_in_klause (other, c)) { + const unsigned other_idx = other / 2; + if (marks[other_idx]) + continue; + CADICAL_assert (other_idx != idx); + marks[other_idx] = true; + CADICAL_assert (values[other]); + if (vars[other_idx].level) + open++; + else + PUSH_STACK (work, other_idx); + PUSH_STACK (cadical_kitten->analyzed, other_idx); + + LOG ("analyzing final literal %u", other ^ 1); + } + } + } + for (size_t next = 0; next < SIZE_STACK (work); next++) { + const unsigned idx = PEEK_STACK (work, next); + const kar *var = vars + idx; + const unsigned reason = var->reason; + if (reason == INVALID) { + unsigned lit = 2 * idx; + if (values[lit] < 0) + lit ^= 1; + LOG ("failed assumption %u", lit); + CADICAL_assert (!cadical_kitten->failed[lit]); + cadical_kitten->failed[lit] = true; + const unsigned not_lit = lit ^ 1; + PUSH_STACK (cadical_kitten->klause, not_lit); + } else { + ROG (reason, "analyzing unit"); + PUSH_STACK (cadical_kitten->resolved, reason); + } + } + + // this is bfs not dfs so it does not work for lrat :/ + /* + for (size_t next = 0; next < SIZE_STACK (cadical_kitten->analyzed); next++) { + const unsigned idx = PEEK_STACK (cadical_kitten->analyzed, next); + CADICAL_assert (marks[idx]); + const kar *var = vars + idx; + const unsigned reason = var->reason; + if (reason == INVALID) { + unsigned lit = 2 * idx; + if (values[lit] < 0) + lit ^= 1; + LOG ("failed assumption %u", lit); + CADICAL_assert (!cadical_kitten->failed[lit]); + cadical_kitten->failed[lit] = true; + const unsigned not_lit = lit ^ 1; + PUSH_STACK (cadical_kitten->klause, not_lit); + } else { + ROG (reason, "analyzing"); + PUSH_STACK (cadical_kitten->resolved, reason); + klause *c = dereference_klause (cadical_kitten, reason); + for (all_literals_in_klause (other, c)) { + const unsigned other_idx = other / 2; + if (other_idx == idx) + continue; + if (marks[other_idx]) + continue; + marks[other_idx] = true; + PUSH_STACK (cadical_kitten->analyzed, other_idx); + LOG ("analyzing final literal %u", other ^ 1); + } + } + } + */ + + for (all_stack (unsigned, idx, cadical_kitten->analyzed)) + CADICAL_assert (marks[idx]), marks[idx] = 0; + CLEAR_STACK (cadical_kitten->analyzed); + + RELEASE_STACK (work); + + const size_t resolved = SIZE_STACK (cadical_kitten->resolved); + CADICAL_assert (resolved); + + if (resolved == 1) { + cadical_kitten->failing = PEEK_STACK (cadical_kitten->resolved, 0); + ROG (cadical_kitten->failing, "reusing as core"); + } else { + cadical_kitten->failing = cadical_new_learned_klause (cadical_kitten); + ROG (cadical_kitten->failing, "new core"); + } + + CLEAR_STACK (cadical_kitten->resolved); + CLEAR_STACK (cadical_kitten->klause); +} + +static void flush_trail (cadical_kitten *cadical_kitten) { + unsigneds *trail = &cadical_kitten->trail; + LOG ("flushing %zu root-level literals from trail", SIZE_STACK (*trail)); + CADICAL_assert (!cadical_kitten->level); + cadical_kitten->propagated = 0; + CLEAR_STACK (*trail); +} + +static int decide (cadical_kitten *cadical_kitten) { + if (!cadical_kitten->level && !EMPTY_STACK (cadical_kitten->trail)) + flush_trail (cadical_kitten); + + const value *const values = cadical_kitten->values; + unsigned decision = INVALID; + const size_t assumptions = SIZE_STACK (cadical_kitten->assumptions); + while (cadical_kitten->level < assumptions) { + unsigned assumption = PEEK_STACK (cadical_kitten->assumptions, cadical_kitten->level); + value value = values[assumption]; + if (value < 0) { + LOG ("found failing assumption %u", assumption); + failing (cadical_kitten); + return 20; + } else if (value > 0) { + + cadical_kitten->level++; + LOG ("pseudo decision level %u for already satisfied assumption %u", + cadical_kitten->level, assumption); + } else { + decision = assumption; + LOG ("using assumption %u as decision", decision); + break; + } + } + + if (!cadical_kitten->unassigned) + return 10; + + if (KITTEN_TICKS >= cadical_kitten->limits.ticks) { + LOG ("ticks limit %" PRIu64 " hit after %" PRIu64 " ticks", + cadical_kitten->limits.ticks, KITTEN_TICKS); + return -1; + } + + if (cadical_kitten->terminator && cadical_kitten->terminator (cadical_kitten->terminator_data)) { + LOG ("terminator requested termination"); + return -1; + } + + if (decision == INVALID) { + unsigned idx = cadical_kitten->queue.search; + const kink *const links = cadical_kitten->links; + for (;;) { + CADICAL_assert (idx != INVALID); + if (!values[2 * idx]) + break; + idx = links[idx].prev; + } + update_search (cadical_kitten, idx); + const unsigned phase = cadical_kitten->phases[idx]; + decision = 2 * idx + phase; + LOG ("decision %u variable %u phase %u", decision, idx, phase); + } + INC (cadical_kitten_decisions); + cadical_kitten->level++; + assign (cadical_kitten, decision, INVALID); + return 0; +} + +static void inconsistent (cadical_kitten *cadical_kitten, unsigned ref) { + CADICAL_assert (ref != INVALID); + CADICAL_assert (cadical_kitten->inconsistent == INVALID); + + if (!cadical_kitten->antecedents) { + cadical_kitten->inconsistent = ref; + ROG (ref, "registering inconsistent virtually empty"); + return; + } + + unsigneds *analyzed = &cadical_kitten->analyzed; + unsigneds *resolved = &cadical_kitten->resolved; + + CADICAL_assert (EMPTY_STACK (*analyzed)); + CADICAL_assert (EMPTY_STACK (*resolved)); + + value *marks = cadical_kitten->marks; + const kar *const vars = cadical_kitten->vars; + unsigned next = 0; + + for (;;) { + CADICAL_assert (ref != INVALID); + klause *c = dereference_klause (cadical_kitten, ref); + CADICAL_assert (c); + ROG (ref, "analyzing inconsistent"); + PUSH_STACK (*resolved, ref); + for (all_literals_in_klause (lit, c)) { + const unsigned idx = lit / 2; + CADICAL_assert (!vars[idx].level); + if (marks[idx]) + continue; + CADICAL_assert (cadical_kitten->values[lit] < 0); + LOG ("analyzed %u", lit); + marks[idx] = true; + PUSH_STACK (cadical_kitten->analyzed, idx); + } + if (next == SIZE_STACK (cadical_kitten->analyzed)) + break; + const unsigned idx = PEEK_STACK (cadical_kitten->analyzed, next); + next++; + const kar *const v = vars + idx; + CADICAL_assert (!v->level); + ref = v->reason; + } + CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); + ref = cadical_new_learned_klause (cadical_kitten); + ROG (ref, "registering final inconsistent empty"); + cadical_kitten->inconsistent = ref; + + for (all_stack (unsigned, idx, *analyzed)) + marks[idx] = 0; + + CLEAR_STACK (*analyzed); + CLEAR_STACK (*resolved); +} + +static int propagate_units (cadical_kitten *cadical_kitten) { + if (cadical_kitten->inconsistent != INVALID) + return 20; + + if (EMPTY_STACK (cadical_kitten->units)) { + LOG ("no root level unit clauses"); + return 0; + } + + LOG ("propagating %zu root level unit clauses", + SIZE_STACK (cadical_kitten->units)); + + const value *const values = cadical_kitten->values; + + for (size_t next = 0; next < SIZE_STACK (cadical_kitten->units); next++) { + const unsigned ref = PEEK_STACK (cadical_kitten->units, next); + CADICAL_assert (ref != INVALID); + klause *c = dereference_klause (cadical_kitten, ref); + CADICAL_assert (c->size == 1); + ROG (ref, "propagating unit"); + const unsigned unit = c->lits[0]; + const value value = values[unit]; + if (value > 0) + continue; + if (value < 0) { + inconsistent (cadical_kitten, ref); + return 20; + } + assign (cadical_kitten, unit, ref); + } + const unsigned conflict = propagate (cadical_kitten); + if (conflict == INVALID) + return 0; + inconsistent (cadical_kitten, conflict); + return 20; +} + +/*------------------------------------------------------------------------*/ + +static klause *begin_klauses (cadical_kitten *cadical_kitten) { + return (klause *) BEGIN_STACK (cadical_kitten->klauses); +} + +static klause *end_original_klauses (cadical_kitten *cadical_kitten) { + return (klause *) (BEGIN_STACK (cadical_kitten->klauses) + + cadical_kitten->end_original_ref); +} + +static klause *end_klauses (cadical_kitten *cadical_kitten) { + return (klause *) END_STACK (cadical_kitten->klauses); +} + +static klause *next_klause (cadical_kitten *cadical_kitten, klause *c) { + CADICAL_assert (begin_klauses (cadical_kitten) <= c); + CADICAL_assert (c < end_klauses (cadical_kitten)); + unsigned *res = c->lits + c->size; + if (cadical_kitten->antecedents && is_learned_klause (c)) + res += c->aux; + return (klause *) res; +} + +/*------------------------------------------------------------------------*/ + +static void reset_core (cadical_kitten *cadical_kitten) { + LOG ("resetting core clauses"); + size_t reset = 0; + for (all_klauses (c)) + if (is_core_klause (c)) + unset_core_klause (c), reset++; + LOG ("reset %zu core clauses", reset); + CLEAR_STACK (cadical_kitten->core); +} + +static void reset_assumptions (cadical_kitten *cadical_kitten) { + LOG ("reset %zu assumptions", SIZE_STACK (cadical_kitten->assumptions)); + while (!EMPTY_STACK (cadical_kitten->assumptions)) { + const unsigned assumption = POP_STACK (cadical_kitten->assumptions); + cadical_kitten->failed[assumption] = false; + } +#ifndef CADICAL_NDEBUG + for (size_t i = 0; i < cadical_kitten->size; i++) + CADICAL_assert (!cadical_kitten->failed[i]); +#endif + CLEAR_STACK (cadical_kitten->assumptions); + if (cadical_kitten->failing != INVALID) { + ROG (cadical_kitten->failing, "reset failed assumption reason"); + cadical_kitten->failing = INVALID; + } +} + +static void reset_incremental (cadical_kitten *cadical_kitten) { + // if (cadical_kitten->level) + cadical_completely_backtrack_to_root_level (cadical_kitten); + if (!EMPTY_STACK (cadical_kitten->assumptions)) + reset_assumptions (cadical_kitten); + else + CADICAL_assert (cadical_kitten->failing == INVALID); + if (cadical_kitten->status == 21) + reset_core (cadical_kitten); + UPDATE_STATUS (0); +} + +/*------------------------------------------------------------------------*/ + +static bool flip_literal (cadical_kitten *cadical_kitten, unsigned lit) { + INC (cadical_kitten_flip); + signed char *values = cadical_kitten->values; + if (values[lit] < 0) + lit ^= 1; + LOG ("trying to flip value of satisfied literal %u", lit); + CADICAL_assert (values[lit] > 0); + katches *watches = cadical_kitten->watches + lit; + unsigned *q = BEGIN_STACK (*watches); + const unsigned *const end_watches = END_STACK (*watches); + unsigned const *p = q; + uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; + bool res = true; + while (p != end_watches) { + const unsigned ref = *q++ = *p++; + klause *c = dereference_klause (cadical_kitten, ref); + unsigned *lits = c->lits; + const unsigned other = lits[0] ^ lits[1] ^ lit; + const value other_value = values[other]; + ticks++; + if (other_value > 0) + continue; + value replacement_value = -1; + unsigned replacement = INVALID; + const unsigned *const end_lits = lits + c->size; + unsigned *r; + for (r = lits + 2; r != end_lits; r++) { + replacement = *r; + CADICAL_assert (replacement != lit); + replacement_value = values[replacement]; + CADICAL_assert (replacement_value); + if (replacement_value > 0) + break; + } + if (replacement_value > 0) { + CADICAL_assert (replacement != INVALID); + ROG (ref, "unwatching %u in", lit); + lits[0] = other; + lits[1] = replacement; + *r = lit; + watch_klause (cadical_kitten, replacement, ref); + q--; + } else { + CADICAL_assert (replacement_value < 0); + ROG (ref, "single satisfied"); + res = false; + break; + } + } + while (p != end_watches) + *q++ = *p++; + SET_END_OF_STACK (*watches, q); + ADD (cadical_kitten_ticks, ticks); + if (res) { + LOG ("flipping value of %u", lit); + values[lit] = -1; + const unsigned not_lit = lit ^ 1; + values[not_lit] = 1; + INC (cadical_kitten_flipped); + } else + LOG ("failed to flip value of %u", lit); + return res; +} + +/*------------------------------------------------------------------------*/ + +// this cadical specific clause addition avoids copying clauses multiple +// times just to convert literals to unsigned representation. +// +static unsigned int2u (int lit) { + CADICAL_assert (lit != 0); + int idx = abs (lit) - 1; + return (lit < 0) + 2u * (unsigned) idx; +} + +void cadical_kitten_assume (cadical_kitten *cadical_kitten, unsigned elit) { + REQUIRE_INITIALIZED (); + if (cadical_kitten->status) + reset_incremental (cadical_kitten); + const unsigned ilit = import_literal (cadical_kitten, elit); + LOG ("registering assumption %u", ilit); + PUSH_STACK (cadical_kitten->assumptions, ilit); +} + +void cadical_kitten_assume_signed (cadical_kitten *cadical_kitten, int elit) { + unsigned kelit = int2u (elit); + cadical_kitten_assume (cadical_kitten, kelit); +} + +void cadical_kitten_clause_with_id_and_exception (cadical_kitten *cadical_kitten, unsigned id, + size_t size, + const unsigned *elits, + unsigned except) { + REQUIRE_INITIALIZED (); + if (cadical_kitten->status) + reset_incremental (cadical_kitten); + CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); + const unsigned *const end = elits + size; + for (const unsigned *p = elits; p != end; p++) { + const unsigned elit = *p; + if (elit == except) + continue; + const unsigned ilit = import_literal (cadical_kitten, elit); + CADICAL_assert (ilit < cadical_kitten->lits); + const unsigned iidx = ilit / 2; + if (cadical_kitten->marks[iidx]) + INVALID_API_USAGE ("variable '%u' of literal '%u' occurs twice", + elit / 2, elit); + cadical_kitten->marks[iidx] = true; + PUSH_STACK (cadical_kitten->klause, ilit); + } + for (unsigned *p = cadical_kitten->klause.begin; p != cadical_kitten->klause.end; p++) + cadical_kitten->marks[*p / 2] = false; + new_original_klause (cadical_kitten, id); + CLEAR_STACK (cadical_kitten->klause); +} + +void citten_clause_with_id_and_exception (cadical_kitten *cadical_kitten, unsigned id, + size_t size, const int *elits, + unsigned except) { + REQUIRE_INITIALIZED (); + if (cadical_kitten->status) + reset_incremental (cadical_kitten); + CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); + const int *const end = elits + size; + for (const int *p = elits; p != end; p++) { + const unsigned elit = int2u (*p); // this is the conversion + if (elit == except) + continue; + const unsigned ilit = import_literal (cadical_kitten, elit); + CADICAL_assert (ilit < cadical_kitten->lits); + const unsigned iidx = ilit / 2; + if (cadical_kitten->marks[iidx]) + INVALID_API_USAGE ("variable '%u' of literal '%u' occurs twice", + elit / 2, elit); + cadical_kitten->marks[iidx] = true; + PUSH_STACK (cadical_kitten->klause, ilit); + } + for (unsigned *p = cadical_kitten->klause.begin; p != cadical_kitten->klause.end; p++) + cadical_kitten->marks[*p / 2] = false; + new_original_klause (cadical_kitten, id); + CLEAR_STACK (cadical_kitten->klause); +} + +void citten_clause_with_id_and_equivalence (cadical_kitten *cadical_kitten, unsigned id, + size_t size, const int *elits, + unsigned lit, unsigned other) { + REQUIRE_INITIALIZED (); + if (cadical_kitten->status) + reset_incremental (cadical_kitten); + CADICAL_assert (EMPTY_STACK (cadical_kitten->klause)); + bool sat = false; + const int *const end = elits + size; + for (const int *p = elits; p != end; p++) { + const unsigned elit = int2u (*p); // this is the conversion + if (elit == (lit ^ 1u) || elit == (other ^ 1u)) + continue; + if (elit == lit || elit == other) { + sat = true; + break; + } + const unsigned ilit = import_literal (cadical_kitten, elit); + CADICAL_assert (ilit < cadical_kitten->lits); + const unsigned iidx = ilit / 2; + if (cadical_kitten->marks[iidx]) + INVALID_API_USAGE ("variable '%u' of literal '%u' occurs twice", + elit / 2, elit); + cadical_kitten->marks[iidx] = true; + PUSH_STACK (cadical_kitten->klause, ilit); + } + for (unsigned *p = cadical_kitten->klause.begin; p != cadical_kitten->klause.end; p++) + cadical_kitten->marks[*p / 2] = false; + if (!sat) + new_original_klause (cadical_kitten, id); + CLEAR_STACK (cadical_kitten->klause); +} + +void cadical_kitten_clause (cadical_kitten *cadical_kitten, size_t size, unsigned *elits) { + cadical_kitten_clause_with_id_and_exception (cadical_kitten, INVALID, size, elits, + INVALID); +} + +void citten_clause_with_id (cadical_kitten *cadical_kitten, unsigned id, size_t size, + int *elits) { + citten_clause_with_id_and_exception (cadical_kitten, id, size, elits, INVALID); +} + +void cadical_kitten_unit (cadical_kitten *cadical_kitten, unsigned lit) { + cadical_kitten_clause (cadical_kitten, 1, &lit); +} + +void cadical_kitten_binary (cadical_kitten *cadical_kitten, unsigned a, unsigned b) { + unsigned clause[2] = {a, b}; + cadical_kitten_clause (cadical_kitten, 2, clause); +} + +int cadical_kitten_solve (cadical_kitten *cadical_kitten) { + REQUIRE_INITIALIZED (); + if (cadical_kitten->status) + reset_incremental (cadical_kitten); + else // if (cadical_kitten->level) + cadical_completely_backtrack_to_root_level (cadical_kitten); + + LOG ("starting solving under %zu assumptions", + SIZE_STACK (cadical_kitten->assumptions)); + + INC (cadical_kitten_solved); + + int res = propagate_units (cadical_kitten); + while (!res) { + const unsigned conflict = propagate (cadical_kitten); + if (cadical_kitten->terminator && + cadical_kitten->terminator (cadical_kitten->terminator_data)) { + LOG ("terminator requested termination"); + res = -1; + break; + } + if (conflict != INVALID) { + if (cadical_kitten->level) + analyze (cadical_kitten, conflict); + else { + inconsistent (cadical_kitten, conflict); + res = 20; + } + } else + res = decide (cadical_kitten); + } + + if (res < 0) + res = 0; + + if (!res && !EMPTY_STACK (cadical_kitten->assumptions)) + reset_assumptions (cadical_kitten); + + UPDATE_STATUS (res); + + if (res == 10) + INC (cadical_kitten_sat); + else if (res == 20) + INC (cadical_kitten_unsat); + else + INC (cadical_kitten_unknown); + + LOG ("finished solving with result %d", res); + + return res; +} + +int cadical_kitten_status (cadical_kitten *cadical_kitten) { return cadical_kitten->status; } + +unsigned cadical_kitten_compute_clausal_core (cadical_kitten *cadical_kitten, + uint64_t *learned_ptr) { + REQUIRE_STATUS (20); + + if (!cadical_kitten->antecedents) + INVALID_API_USAGE ("antecedents not tracked"); + + LOG ("computing clausal core"); + + unsigneds *resolved = &cadical_kitten->resolved; + CADICAL_assert (EMPTY_STACK (*resolved)); + + unsigned original = 0; + uint64_t learned = 0; + + unsigned reason_ref = cadical_kitten->inconsistent; + + if (reason_ref == INVALID) { + CADICAL_assert (!EMPTY_STACK (cadical_kitten->assumptions)); + reason_ref = cadical_kitten->failing; + if (reason_ref == INVALID) { + LOG ("assumptions mutually inconsistent"); + //goto DONE; + if (learned_ptr) + *learned_ptr = learned; + + LOG ("clausal core of %u original clauses", original); + LOG ("clausal core of %" PRIu64 " learned clauses", learned); + cadical_kitten->statistics.original = original; + cadical_kitten->statistics.learned = 0; + UPDATE_STATUS (21); + + return original; + } + } + + PUSH_STACK (*resolved, reason_ref); + unsigneds *core = &cadical_kitten->core; + CADICAL_assert (EMPTY_STACK (*core)); + + while (!EMPTY_STACK (*resolved)) { + const unsigned c_ref = POP_STACK (*resolved); + if (c_ref == INVALID) { + const unsigned d_ref = POP_STACK (*resolved); + ROG (d_ref, "core[%zu]", SIZE_STACK (*core)); + PUSH_STACK (*core, d_ref); + klause *d = dereference_klause (cadical_kitten, d_ref); + CADICAL_assert (!is_core_klause (d)); + set_core_klause (d); + if (is_learned_klause (d)) + learned++; + else + original++; + } else { + klause *c = dereference_klause (cadical_kitten, c_ref); + if (is_core_klause (c)) + continue; + PUSH_STACK (*resolved, c_ref); + PUSH_STACK (*resolved, INVALID); + ROG (c_ref, "analyzing antecedent core"); + if (!is_learned_klause (c)) + continue; + for (all_antecedents (d_ref, c)) { + klause *d = dereference_klause (cadical_kitten, d_ref); + if (!is_core_klause (d)) + PUSH_STACK (*resolved, d_ref); + } + } + } + + //DONE: + + if (learned_ptr) + *learned_ptr = learned; + + LOG ("clausal core of %u original clauses", original); + LOG ("clausal core of %" PRIu64 " learned clauses", learned); + cadical_kitten->statistics.original = original; + cadical_kitten->statistics.learned = 0; + UPDATE_STATUS (21); + + return original; +} + +void cadical_kitten_traverse_core_ids (cadical_kitten *cadical_kitten, void *state, + void (*traverse) (void *, unsigned)) { + REQUIRE_STATUS (21); + + LOG ("traversing core of original clauses"); + + unsigned traversed = 0; + + for (all_original_klauses (c)) { + // only happens for 'true' incremental calls, i.e. if add happens after + // solve + if (is_learned_klause (c)) + continue; + if (!is_core_klause (c)) + continue; + ROG (reference_klause (cadical_kitten, c), "traversing"); + traverse (state, c->aux); + traversed++; + } + + LOG ("traversed %u original core clauses", traversed); + (void) traversed; + + CADICAL_assert (cadical_kitten->status == 21); +} + +void cadical_kitten_traverse_core_clauses (cadical_kitten *cadical_kitten, void *state, + void (*traverse) (void *, bool, size_t, + const unsigned *)) { + REQUIRE_STATUS (21); + + LOG ("traversing clausal core"); + + unsigned traversed = 0; + + for (all_stack (unsigned, c_ref, cadical_kitten->core)) { + klause *c = dereference_klause (cadical_kitten, c_ref); + CADICAL_assert (is_core_klause (c)); + const bool learned = is_learned_klause (c); + unsigneds *eclause = &cadical_kitten->eclause; + CADICAL_assert (EMPTY_STACK (*eclause)); + for (all_literals_in_klause (ilit, c)) { + const unsigned elit = export_literal (cadical_kitten, ilit); + PUSH_STACK (*eclause, elit); + } + const size_t size = SIZE_STACK (*eclause); + const unsigned *elits = eclause->begin; + ROG (reference_klause (cadical_kitten, c), "traversing"); + traverse (state, learned, size, elits); + CLEAR_STACK (*eclause); + traversed++; + } + + LOG ("traversed %u core clauses", traversed); + (void) traversed; + + CADICAL_assert (cadical_kitten->status == 21); +} + +void cadical_kitten_traverse_core_clauses_with_id ( + cadical_kitten *cadical_kitten, void *state, + void (*traverse) (void *state, unsigned, bool learned, size_t, + const unsigned *)) { + REQUIRE_STATUS (21); + + LOG ("traversing clausal core"); + + unsigned traversed = 0; + + for (all_stack (unsigned, c_ref, cadical_kitten->core)) { + klause *c = dereference_klause (cadical_kitten, c_ref); + CADICAL_assert (is_core_klause (c)); + const bool learned = is_learned_klause (c); + unsigneds *eclause = &cadical_kitten->eclause; + CADICAL_assert (EMPTY_STACK (*eclause)); + for (all_literals_in_klause (ilit, c)) { + const unsigned elit = export_literal (cadical_kitten, ilit); + PUSH_STACK (*eclause, elit); + } + const size_t size = SIZE_STACK (*eclause); + const unsigned *elits = eclause->begin; + ROG (reference_klause (cadical_kitten, c), "traversing"); + unsigned ctag = learned ? 0 : c->aux; + traverse (state, ctag, learned, size, elits); + CLEAR_STACK (*eclause); + traversed++; + } + + LOG ("traversed %u core clauses", traversed); + (void) traversed; + + CADICAL_assert (cadical_kitten->status == 21); +} + +void cadical_kitten_trace_core (cadical_kitten *cadical_kitten, void *state, + void (*trace) (void *, unsigned, unsigned, bool, + size_t, const unsigned *, size_t, + const unsigned *)) { + REQUIRE_STATUS (21); + + LOG ("tracing clausal core"); + + unsigned traced = 0; + + for (all_stack (unsigned, c_ref, cadical_kitten->core)) { + klause *c = dereference_klause (cadical_kitten, c_ref); + CADICAL_assert (is_core_klause (c)); + const bool learned = is_learned_klause (c); + unsigneds *eclause = &cadical_kitten->eclause; + CADICAL_assert (EMPTY_STACK (*eclause)); + for (all_literals_in_klause (ilit, c)) { + const unsigned elit = export_literal (cadical_kitten, ilit); + PUSH_STACK (*eclause, elit); + } + const size_t size = SIZE_STACK (*eclause); + const unsigned *elits = eclause->begin; + + unsigneds *resolved = &cadical_kitten->resolved; + CADICAL_assert (EMPTY_STACK (*resolved)); + if (learned) { + for (all_antecedents (ref, c)) { + PUSH_STACK (*resolved, ref); + } + } + const size_t rsize = SIZE_STACK (*resolved); + const unsigned *rids = resolved->begin; + + unsigned cid = reference_klause (cadical_kitten, c); + unsigned ctag = learned ? 0 : c->aux; + ROG (cid, "tracing"); + trace (state, cid, ctag, learned, size, elits, rsize, rids); + CLEAR_STACK (*eclause); + CLEAR_STACK (*resolved); + traced++; + } + + LOG ("traced %u core clauses", traced); + (void) traced; + + CADICAL_assert (cadical_kitten->status == 21); +} + +void cadical_kitten_shrink_to_clausal_core (cadical_kitten *cadical_kitten) { + REQUIRE_STATUS (21); + + LOG ("shrinking formula to core of original clauses"); + + CLEAR_STACK (cadical_kitten->trail); + + cadical_kitten->unassigned = cadical_kitten->lits / 2; + cadical_kitten->propagated = 0; + cadical_kitten->level = 0; + + update_search (cadical_kitten, cadical_kitten->queue.last); + + memset (cadical_kitten->values, 0, cadical_kitten->lits); + + for (all_kits (lit)) + CLEAR_STACK (KATCHES (lit)); + + CADICAL_assert (cadical_kitten->inconsistent != INVALID); + klause *inconsistent = dereference_klause (cadical_kitten, cadical_kitten->inconsistent); + if (is_learned_klause (inconsistent) || inconsistent->size) { + ROG (cadical_kitten->inconsistent, "resetting inconsistent"); + cadical_kitten->inconsistent = INVALID; + } else + ROG (cadical_kitten->inconsistent, "keeping inconsistent"); + + CLEAR_STACK (cadical_kitten->units); + + klause *begin = begin_klauses (cadical_kitten), *q = begin; + klause const *const end = end_original_klauses (cadical_kitten); +#ifdef LOGGING + unsigned original = 0; +#endif + for (klause *c = begin, *next; c != end; c = next) { + next = next_klause (cadical_kitten, c); + // CADICAL_assert (!is_learned_klause (c)); not necessarily true + if (is_learned_klause (c)) + continue; + if (!is_core_klause (c)) + continue; + unset_core_klause (c); + const unsigned dst = (unsigned *) q - (unsigned *) begin; + const unsigned size = c->size; + if (!size) { + if (cadical_kitten->inconsistent != INVALID) + cadical_kitten->inconsistent = dst; + } else if (size == 1) { + PUSH_STACK (cadical_kitten->units, dst); + ROG (dst, "keeping"); + } else { + watch_klause (cadical_kitten, c->lits[0], dst); + watch_klause (cadical_kitten, c->lits[1], dst); + } + if (c == q) + q = next; + else { + const size_t bytes = (char *) next - (char *) c; + memmove (q, c, bytes); + q = (klause *) ((char *) q + bytes); + } +#ifdef LOGGING + original++; +#endif + } + SET_END_OF_STACK (cadical_kitten->klauses, (unsigned *) q); + cadical_kitten->end_original_ref = SIZE_STACK (cadical_kitten->klauses); + LOG ("end of original clauses at %zu", cadical_kitten->end_original_ref); + LOG ("%u original clauses left", original); + + CLEAR_STACK (cadical_kitten->core); + + UPDATE_STATUS (0); +} + +signed char cadical_kitten_signed_value (cadical_kitten *cadical_kitten, int selit) { + REQUIRE_STATUS (10); + const unsigned elit = int2u (selit); + const unsigned eidx = elit / 2; + if (eidx >= cadical_kitten->evars) + return 0; + unsigned iidx = cadical_kitten->import[eidx]; + if (!iidx) + return 0; + const unsigned ilit = 2 * (iidx - 1) + (elit & 1); + return cadical_kitten->values[ilit]; +} + +signed char cadical_kitten_value (cadical_kitten *cadical_kitten, unsigned elit) { + REQUIRE_STATUS (10); + const unsigned eidx = elit / 2; + if (eidx >= cadical_kitten->evars) + return 0; + unsigned iidx = cadical_kitten->import[eidx]; + if (!iidx) + return 0; + const unsigned ilit = 2 * (iidx - 1) + (elit & 1); + return cadical_kitten->values[ilit]; +} + +signed char cadical_kitten_fixed (cadical_kitten *cadical_kitten, unsigned elit) { + const unsigned eidx = elit / 2; + if (eidx >= cadical_kitten->evars) + return 0; + unsigned iidx = cadical_kitten->import[eidx]; + if (!iidx) + return 0; + iidx--; + const unsigned ilit = 2 * iidx + (elit & 1); + signed char res = cadical_kitten->values[ilit]; + if (!res) + return 0; + kar *v = cadical_kitten->vars + iidx; + if (v->level) + return 0; + return res; +} + +signed char cadical_kitten_fixed_signed (cadical_kitten *cadical_kitten, int elit) { + unsigned kelit = int2u (elit); + return cadical_kitten_fixed (cadical_kitten, kelit); +} + +bool cadical_kitten_flip_literal (cadical_kitten *cadical_kitten, unsigned elit) { + REQUIRE_STATUS (10); + const unsigned eidx = elit / 2; + if (eidx >= cadical_kitten->evars) + return false; + unsigned iidx = cadical_kitten->import[eidx]; + if (!iidx) + return false; + const unsigned ilit = 2 * (iidx - 1) + (elit & 1); + if (cadical_kitten_fixed (cadical_kitten, elit)) + return false; + return flip_literal (cadical_kitten, ilit); +} + +bool cadical_kitten_flip_signed_literal (cadical_kitten *cadical_kitten, int elit) { + REQUIRE_STATUS (10); + unsigned kelit = int2u (elit); + return cadical_kitten_flip_literal (cadical_kitten, kelit); +} + +bool cadical_kitten_failed (cadical_kitten *cadical_kitten, unsigned elit) { + REQUIRE_STATUS (20); + const unsigned eidx = elit / 2; + if (eidx >= cadical_kitten->evars) + return false; + unsigned iidx = cadical_kitten->import[eidx]; + if (!iidx) + return false; + const unsigned ilit = 2 * (iidx - 1) + (elit & 1); + return cadical_kitten->failed[ilit]; +} + +// checks both watches for clauses with only one literal positively +// assigned. if such a clause is found, return false. Otherwise fix watch +// invariant and return true +static bool prime_propagate (cadical_kitten *cadical_kitten, const unsigned idx, + void *state, const bool ignoring, + bool (*ignore) (void *, unsigned)) { + unsigned lit = 2 * idx; + unsigned conflict = INVALID; + value *values = cadical_kitten->values; + for (int i = 0; i < 2; i++) { + if (conflict != INVALID) + break; + lit = lit ^ i; + const unsigned not_lit = lit ^ 1; + katches *watches = cadical_kitten->watches + not_lit; + unsigned *q = BEGIN_STACK (*watches); + const unsigned *const end_watches = END_STACK (*watches); + unsigned const *p = q; + uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; + while (p != end_watches) { + const unsigned ref = *q++ = *p++; + klause *c = dereference_klause (cadical_kitten, ref); + if (is_learned_klause (c) || ignore (state, c->aux) == ignoring) + continue; + CADICAL_assert (c->size > 1); + unsigned *lits = c->lits; + const unsigned other = lits[0] ^ lits[1] ^ not_lit; + const value other_value = values[other]; + ticks++; + if (other_value > 0) + continue; + value replacement_value = -1; + unsigned replacement = INVALID; + const unsigned *const end_lits = lits + c->size; + unsigned *r; + for (r = lits + 2; r != end_lits; r++) { + replacement = *r; + replacement_value = values[replacement]; + if (replacement_value > 0) + break; + } + if (replacement_value > 0) { + CADICAL_assert (replacement != INVALID); + ROG (ref, "unwatching %u in", not_lit); + lits[0] = other; + lits[1] = replacement; + *r = not_lit; + watch_klause (cadical_kitten, replacement, ref); + q--; + } else { + ROG (ref, "idx %u forced prime by", idx); + conflict = ref; + break; + } + } + while (p != end_watches) + *q++ = *p++; + SET_END_OF_STACK (*watches, q); + ADD (cadical_kitten_ticks, ticks); + } + return conflict == INVALID; +} + +void cadical_kitten_add_prime_implicant (cadical_kitten *cadical_kitten, void *state, int side, + void (*add_implicant) (void *, int, size_t, + const unsigned *)) { + REQUIRE_STATUS (11); + // might be possible in some edge cases + unsigneds *prime = &cadical_kitten->prime[side]; + unsigneds *prime2 = &cadical_kitten->prime[!side]; + CADICAL_assert (!EMPTY_STACK (*prime) || !EMPTY_STACK (*prime2)); + CLEAR_STACK (*prime2); + + for (all_stack (unsigned, lit, *prime)) { + const unsigned not_lit = lit ^ 1; + const unsigned elit = export_literal (cadical_kitten, not_lit); + PUSH_STACK (*prime2, elit); + } + + // adds a clause which will reset cadical_kitten status and backtrack + add_implicant (state, side, SIZE_STACK (*prime2), BEGIN_STACK (*prime2)); + CLEAR_STACK (*prime); + CLEAR_STACK (*prime2); +} + +// computes two prime implicants, only considering clauses based on ignore +// return -1 if no prime implicant has been computed, otherwise returns +// index of shorter implicant. +// TODO does not work if flip has been called beforehand +int cadical_kitten_compute_prime_implicant (cadical_kitten *cadical_kitten, void *state, + bool (*ignore) (void *, unsigned)) { + REQUIRE_STATUS (10); + + value *values = cadical_kitten->values; + kar *vars = cadical_kitten->vars; + unsigneds unassigned; + INIT_STACK (unassigned); + bool limit_hit = 0; + CADICAL_assert (EMPTY_STACK (cadical_kitten->prime[0]) && EMPTY_STACK (cadical_kitten->prime[1])); + for (int i = 0; i < 2; i++) { + const bool ignoring = i; + for (all_stack (unsigned, lit, cadical_kitten->trail)) { + if (KITTEN_TICKS >= cadical_kitten->limits.ticks) { + LOG ("ticks limit %" PRIu64 " hit after %" PRIu64 " ticks", + cadical_kitten->limits.ticks, KITTEN_TICKS); + limit_hit = 1; + break; + } + CADICAL_assert (values[lit] > 0); + const unsigned idx = lit / 2; + const unsigned ref = vars[idx].reason; + CADICAL_assert (vars[idx].level); + klause *c = 0; + if (ref != INVALID) + c = dereference_klause (cadical_kitten, ref); + if (ref == INVALID || is_learned_klause (c) || + ignore (state, c->aux) == ignoring) { + LOG ("non-prime candidate var %d", idx); + if (prime_propagate (cadical_kitten, idx, state, ignoring, ignore)) { + values[lit] = 0; + values[lit ^ 1] = 0; + PUSH_STACK (unassigned, lit); + } else + CADICAL_assert (values[lit] > 0); + } + } + unsigneds *prime = &cadical_kitten->prime[i]; + // push on prime implicant stack. + for (all_kits (lit)) { + if (values[lit] > 0) + PUSH_STACK (*prime, lit); + } + // reassign all literals on + for (all_stack (unsigned, lit, unassigned)) { + CADICAL_assert (!values[lit]); + values[lit] = 1; + values[lit ^ 1] = -1; + } + CLEAR_STACK (unassigned); + } + RELEASE_STACK (unassigned); + + if (limit_hit) { + CLEAR_STACK (cadical_kitten->prime[0]); + CLEAR_STACK (cadical_kitten->prime[1]); + return -1; + } + // the only case when one of the prime implicants is allowed to be empty + // is if ignore returns always true or always false. + CADICAL_assert (!EMPTY_STACK (cadical_kitten->prime[0]) || + !EMPTY_STACK (cadical_kitten->prime[1])); + UPDATE_STATUS (11); + + int res = SIZE_STACK (cadical_kitten->prime[0]) > SIZE_STACK (cadical_kitten->prime[1]); + return res; +} + +static bool contains_blit (cadical_kitten *cadical_kitten, klause *c, const unsigned blit) { + for (all_literals_in_klause (lit, c)) { + if (lit == blit) + return true; + } + return false; +} + +static bool prime_propagate_blit (cadical_kitten *cadical_kitten, const unsigned idx, + const unsigned blit) { + unsigned lit = 2 * idx; + unsigned conflict = INVALID; + value *values = cadical_kitten->values; + LOG ("prime propagating idx %u for blit %u", idx, blit); + for (int i = 0; i < 2; i++) { + if (conflict != INVALID) + break; + lit = lit ^ i; + if (lit == blit) + continue; + const unsigned not_lit = lit ^ 1; + katches *watches = cadical_kitten->watches + not_lit; + unsigned *q = BEGIN_STACK (*watches); + const unsigned *const end_watches = END_STACK (*watches); + unsigned const *p = q; + uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; + while (p != end_watches) { + const unsigned ref = *q++ = *p++; + klause *c = dereference_klause (cadical_kitten, ref); + if (is_learned_klause (c)) + continue; + ROG (ref, "checking with blit %u", blit); + CADICAL_assert (c->size > 1); + unsigned *lits = c->lits; + const unsigned other = lits[0] ^ lits[1] ^ not_lit; + const value other_value = values[other]; + ticks++; + bool use = other == blit || not_lit == blit; + if (other_value > 0) + continue; + value replacement_value = -1; + unsigned replacement = INVALID; + const unsigned *const end_lits = lits + c->size; + unsigned *r; + for (r = lits + 2; r != end_lits; r++) { + replacement = *r; + replacement_value = values[replacement]; + use = use || replacement == blit; + if (replacement_value > 0) + break; + } + if (replacement_value > 0) { + CADICAL_assert (replacement != INVALID); + ROG (ref, "unwatching %u in", not_lit); + lits[0] = other; + lits[1] = replacement; + *r = not_lit; + watch_klause (cadical_kitten, replacement, ref); + q--; + } else if (!use) { + continue; + } else { + ROG (ref, "idx %u forced prime by", idx); + conflict = ref; + break; + } + } + while (p != end_watches) + *q++ = *p++; + SET_END_OF_STACK (*watches, q); + ADD (cadical_kitten_ticks, ticks); + } + return conflict == INVALID; +} + +static int compute_prime_implicant_for (cadical_kitten *cadical_kitten, unsigned blit) { + value *values = cadical_kitten->values; + kar *vars = cadical_kitten->vars; + unsigneds unassigned; + INIT_STACK (unassigned); + bool limit_hit = false; + CADICAL_assert (EMPTY_STACK (cadical_kitten->prime[0]) && EMPTY_STACK (cadical_kitten->prime[1])); + for (int i = 0; i < 2; i++) { + const unsigned block = blit ^ i; + const bool ignoring = i; + if (prime_propagate_blit (cadical_kitten, block / 2, block)) { + value tmp = values[blit]; + CADICAL_assert (tmp); + values[blit] = 0; + values[blit ^ 1] = 0; + PUSH_STACK (unassigned, tmp > 0 ? blit : blit ^ 1); + PUSH_STACK (cadical_kitten->prime[i], block); // will be negated! + } else + CADICAL_assert (false); + for (all_stack (unsigned, lit, cadical_kitten->trail)) { + if (KITTEN_TICKS >= cadical_kitten->limits.ticks) { + LOG ("ticks limit %" PRIu64 " hit after %" PRIu64 " ticks", + cadical_kitten->limits.ticks, KITTEN_TICKS); + limit_hit = true; + break; + } + if (!values[lit]) + continue; + CADICAL_assert (values[lit]); // not true when flipping is involved + const unsigned idx = lit / 2; + const unsigned ref = vars[idx].reason; + CADICAL_assert (vars[idx].level); + LOG ("non-prime candidate var %d", idx); + if (prime_propagate_blit (cadical_kitten, idx, block)) { + value tmp = values[lit]; + CADICAL_assert (tmp); + values[lit] = 0; + values[lit ^ 1] = 0; + PUSH_STACK (unassigned, tmp > 0 ? lit : lit ^ 1); + } + } + unsigneds *prime = &cadical_kitten->prime[i]; + // push on prime implicant stack. + for (all_kits (lit)) { + if (values[lit] > 0) + PUSH_STACK (*prime, lit); + } + // reassign all literals on + for (all_stack (unsigned, lit, unassigned)) { + CADICAL_assert (!values[lit]); + values[lit] = 1; + values[lit ^ 1] = -1; + } + CLEAR_STACK (unassigned); + } + RELEASE_STACK (unassigned); + + if (limit_hit) { + CLEAR_STACK (cadical_kitten->prime[0]); + CLEAR_STACK (cadical_kitten->prime[1]); + return -1; + } + // the only case when one of the prime implicants is allowed to be empty + // is if ignore returns always true or always false. + CADICAL_assert (!EMPTY_STACK (cadical_kitten->prime[0]) || + !EMPTY_STACK (cadical_kitten->prime[1])); + LOGLITS (BEGIN_STACK (cadical_kitten->prime[0]), SIZE_STACK (cadical_kitten->prime[0]), + "first implicant %u", blit); + LOGLITS (BEGIN_STACK (cadical_kitten->prime[1]), SIZE_STACK (cadical_kitten->prime[1]), + "second implicant %u", blit ^ 1); + UPDATE_STATUS (11); + + int res = SIZE_STACK (cadical_kitten->prime[0]) > SIZE_STACK (cadical_kitten->prime[1]); + return res; +} + +int cadical_kitten_flip_and_implicant_for_signed_literal (cadical_kitten *cadical_kitten, + int elit) { + REQUIRE_STATUS (10); + unsigned kelit = int2u (elit); + if (!cadical_kitten_flip_literal (cadical_kitten, kelit)) { + return -2; + } + const unsigned eidx = kelit / 2; + unsigned iidx = cadical_kitten->import[eidx]; + CADICAL_assert (iidx); + const unsigned ilit = 2 * (iidx - 1) + (kelit & 1); + return compute_prime_implicant_for (cadical_kitten, ilit); +} + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/lidruptracer.cpp b/src/sat/cadical/cadical_lidruptracer.cpp similarity index 91% rename from src/sat/cadical/lidruptracer.cpp rename to src/sat/cadical/cadical_lidruptracer.cpp index 453f87df4..db07e36e6 100644 --- a/src/sat/cadical/lidruptracer.cpp +++ b/src/sat/cadical/cadical_lidruptracer.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -7,7 +11,7 @@ namespace CaDiCaL { LidrupTracer::LidrupTracer (Internal *i, File *f, bool b) : internal (i), file (f), binary (b), num_clauses (0), size_clauses (0), clauses (0), last_hash (0), last_id (0), last_clause (0) -#ifndef QUIET +#ifndef CADICAL_QUIET , added (0), deleted (0) #endif @@ -21,10 +25,10 @@ LidrupTracer::LidrupTracer (Internal *i, File *f, bool b) uint64_t nonce = random.next (); if (!(nonce & 1)) nonce++; - assert (nonce), assert (nonce & 1); + CADICAL_assert (nonce), CADICAL_assert (nonce & 1); nonces[n] = nonce; } -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG binary = b; #else (void) b; @@ -50,7 +54,7 @@ LidrupTracer::~LidrupTracer () { /*------------------------------------------------------------------------*/ void LidrupTracer::enlarge_clauses () { - assert (num_clauses == size_clauses); + CADICAL_assert (num_clauses == size_clauses); const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1; LOG ("LIDRUP Tracer enlarging clauses of tracer from %" PRIu64 " to %" PRIu64, @@ -88,13 +92,13 @@ LidrupClause *LidrupTracer::new_clause () { } void LidrupTracer::delete_clause (LidrupClause *c) { - assert (c); + CADICAL_assert (c); num_clauses--; delete c; } uint64_t LidrupTracer::reduce_hash (uint64_t hash, uint64_t size) { - assert (size > 0); + CADICAL_assert (size > 0); unsigned shift = 32; uint64_t res = hash; while ((((uint64_t) 1) << shift) > size) { @@ -102,12 +106,12 @@ uint64_t LidrupTracer::reduce_hash (uint64_t hash, uint64_t size) { shift >>= 1; } res &= size - 1; - assert (res < size); + CADICAL_assert (res < size); return res; } uint64_t LidrupTracer::compute_hash (const int64_t id) { - assert (id > 0); + CADICAL_assert (id > 0); unsigned j = id % num_nonces; uint64_t tmp = nonces[j] * (uint64_t) id; return last_hash = tmp; @@ -128,7 +132,7 @@ bool LidrupTracer::find_and_delete (const int64_t id) { } if (!c) return false; - assert (c && res); + CADICAL_assert (c && res); *res = c->next; for (auto &lit : c->literals) { imported_clause.push_back (lit); @@ -157,15 +161,15 @@ inline void LidrupTracer::flush_if_piping () { } inline void LidrupTracer::put_binary_zero () { - assert (binary); - assert (file); + CADICAL_assert (binary); + CADICAL_assert (file); file->put ((unsigned char) 0); } inline void LidrupTracer::put_binary_lit (int lit) { - assert (binary); - assert (file); - assert (lit != INT_MIN); + CADICAL_assert (binary); + CADICAL_assert (file); + CADICAL_assert (lit != INT_MIN); unsigned x = 2 * abs (lit) + (lit < 0); unsigned char ch; while (x & ~0x7f) { @@ -178,8 +182,8 @@ inline void LidrupTracer::put_binary_lit (int lit) { } inline void LidrupTracer::put_binary_id (int64_t id, bool can_be_negative) { - assert (binary); - assert (file); + CADICAL_assert (binary); + CADICAL_assert (file); uint64_t x = abs (id); if (can_be_negative) { x = 2 * x + (id < 0); @@ -256,7 +260,7 @@ void LidrupTracer::lidrup_add_original_clause (int64_t id, } void LidrupTracer::lidrup_batch_weaken_restore_and_delete () { - assert (batch_weaken.empty () || batch_delete.empty ()); + CADICAL_assert (batch_weaken.empty () || batch_delete.empty ()); if (!batch_weaken.empty ()) { if (binary) { file->put ('w'); @@ -274,7 +278,7 @@ void LidrupTracer::lidrup_batch_weaken_restore_and_delete () { put_binary_zero (); else file->put ("0\n"); -#ifndef QUIET +#ifndef CADICAL_QUIET batched++; #endif } @@ -295,7 +299,7 @@ void LidrupTracer::lidrup_batch_weaken_restore_and_delete () { put_binary_zero (); else file->put ("0\n"); -#ifndef QUIET +#ifndef CADICAL_QUIET batched++; #endif } @@ -316,7 +320,7 @@ void LidrupTracer::lidrup_batch_weaken_restore_and_delete () { put_binary_zero (); else file->put ("0\n"); -#ifndef QUIET +#ifndef CADICAL_QUIET batched++; #endif } @@ -341,8 +345,8 @@ void LidrupTracer::lidrup_conclude_and_delete ( else file->put ("u "); if (!find_and_delete (id)) { - assert (imported_clause.empty ()); - assert (conclusion.size () == 1); + CADICAL_assert (imported_clause.empty ()); + CADICAL_assert (conclusion.size () == 1); if (binary) { put_binary_zero (); put_binary_id (id); @@ -463,10 +467,10 @@ void LidrupTracer::add_derived_clause (int64_t id, bool, const vector<int64_t> &chain) { if (file->closed ()) return; - assert (imported_clause.empty ()); + CADICAL_assert (imported_clause.empty ()); LOG (clause, "LIDRUP TRACER tracing addition of derived clause"); lidrup_add_derived_clause (id, clause, chain); -#ifndef QUIET +#ifndef CADICAL_QUIET added++; #endif } @@ -476,7 +480,7 @@ void LidrupTracer::add_assumption_clause (int64_t id, const vector<int64_t> &chain) { if (file->closed ()) return; - assert (imported_clause.empty ()); + CADICAL_assert (imported_clause.empty ()); LOG (clause, "LIDRUP TRACER tracing addition of assumption clause[%" PRId64 "]", id); @@ -493,21 +497,21 @@ void LidrupTracer::add_assumption_clause (int64_t id, void LidrupTracer::delete_clause (int64_t id, bool, const vector<int> &) { if (file->closed ()) return; - assert (imported_clause.empty ()); + CADICAL_assert (imported_clause.empty ()); LOG ("LIDRUP TRACER tracing deletion of clause[%" PRId64 "]", id); if (find_and_delete (id)) { - assert (imported_clause.empty ()); + CADICAL_assert (imported_clause.empty ()); if (!batch_delete.empty () || !batch_restore.empty ()) lidrup_batch_weaken_restore_and_delete (); batch_weaken.push_back (id); -#ifndef QUIET +#ifndef CADICAL_QUIET weakened++; #endif } else { if (!batch_weaken.empty () || !batch_restore.empty ()) lidrup_batch_weaken_restore_and_delete (); batch_delete.push_back (id); -#ifndef QUIET +#ifndef CADICAL_QUIET deleted++; #endif } @@ -516,7 +520,7 @@ void LidrupTracer::delete_clause (int64_t id, bool, const vector<int> &) { void LidrupTracer::weaken_minus (int64_t id, const vector<int> &) { if (file->closed ()) return; - assert (imported_clause.empty ()); + CADICAL_assert (imported_clause.empty ()); LOG ("LIDRUP TRACER tracing weaken minus of clause[%" PRId64 "]", id); last_id = id; insert (); @@ -526,7 +530,7 @@ void LidrupTracer::conclude_unsat (ConclusionType, const vector<int64_t> &conclusion) { if (file->closed ()) return; - assert (imported_clause.empty ()); + CADICAL_assert (imported_clause.empty ()); LOG (conclusion, "LIDRUP TRACER tracing conclusion of clause(s)"); lidrup_conclude_and_delete (conclusion); } @@ -538,12 +542,12 @@ void LidrupTracer::add_original_clause (int64_t id, bool, return; if (!restored) { LOG (clause, "LIDRUP TRACER tracing addition of original clause"); -#ifndef QUIET +#ifndef CADICAL_QUIET original++; #endif return lidrup_add_original_clause (id, clause); } - assert (restored); + CADICAL_assert (restored); if (find_and_delete (id)) { LOG (clause, "LIDRUP TRACER the clause was not yet weakened, so no restore"); @@ -551,7 +555,7 @@ void LidrupTracer::add_original_clause (int64_t id, bool, } LOG (clause, "LIDRUP TRACER tracing addition of restored clause"); lidrup_add_restored_clause (id); -#ifndef QUIET +#ifndef CADICAL_QUIET restore++; #endif } @@ -582,7 +586,7 @@ void LidrupTracer::solve_query () { return; LOG (assumptions, "LIDRUP TRACER tracing solve query with assumptions"); lidrup_solve_query (); -#ifndef QUIET +#ifndef CADICAL_QUIET solved++; #endif } @@ -601,7 +605,7 @@ void LidrupTracer::reset_assumptions () { bool LidrupTracer::closed () { return file->closed (); } -#ifndef QUIET +#ifndef CADICAL_QUIET void LidrupTracer::print_statistics () { // TODO complete this. @@ -627,9 +631,9 @@ void LidrupTracer::print_statistics () { #endif void LidrupTracer::close (bool print) { - assert (!closed ()); + CADICAL_assert (!closed ()); file->close (); -#ifndef QUIET +#ifndef CADICAL_QUIET if (print) { MSG ("LIDRUP proof file '%s' closed", file->name ()); print_statistics (); @@ -640,10 +644,10 @@ void LidrupTracer::close (bool print) { } void LidrupTracer::flush (bool print) { - assert (!closed ()); + CADICAL_assert (!closed ()); lidrup_batch_weaken_restore_and_delete (); file->flush (); -#ifndef QUIET +#ifndef CADICAL_QUIET if (print) { MSG ("LIDRUP proof file '%s' flushed", file->name ()); print_statistics (); @@ -654,3 +658,5 @@ void LidrupTracer::flush (bool print) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/limit.cpp b/src/sat/cadical/cadical_limit.cpp similarity index 97% rename from src/sat/cadical/limit.cpp rename to src/sat/cadical/cadical_limit.cpp index 8a5259b2a..69417a473 100644 --- a/src/sat/cadical/limit.cpp +++ b/src/sat/cadical/cadical_limit.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { Limit::Limit () { memset (this, 0, sizeof *this); } @@ -127,3 +131,5 @@ void Internal::reset_limits () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/logging.cpp b/src/sat/cadical/cadical_logging.cpp similarity index 98% rename from src/sat/cadical/logging.cpp rename to src/sat/cadical/cadical_logging.cpp index a677a99e1..589bf097e 100644 --- a/src/sat/cadical/logging.cpp +++ b/src/sat/cadical/cadical_logging.cpp @@ -1,7 +1,11 @@ +#include "global.h" + #ifdef LOGGING #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { void Logger::print_log_prefix (Internal *internal) { @@ -212,4 +216,6 @@ string Logger::loglit (Internal *internal, int lit) { } } // namespace CaDiCaL +ABC_NAMESPACE_IMPL_END + #endif diff --git a/src/sat/cadical/lookahead.cpp b/src/sat/cadical/cadical_lookahead.cpp similarity index 96% rename from src/sat/cadical/lookahead.cpp rename to src/sat/cadical/cadical_lookahead.cpp index 3d60a9a01..de84858cc 100644 --- a/src/sat/cadical/lookahead.cpp +++ b/src/sat/cadical/cadical_lookahead.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { struct literal_occ { @@ -93,7 +97,7 @@ struct probe_negated_noccs_rank { void Internal::lookahead_flush_probes () { - assert (!probes.empty ()); + CADICAL_assert (!probes.empty ()); init_noccs (); for (const auto &c : clauses) { @@ -116,14 +120,14 @@ void Internal::lookahead_flush_probes () { continue; if (have_pos_bin_occs) lit = -lit; - assert (!noccs (lit)), assert (noccs (-lit) > 0); + CADICAL_assert (!noccs (lit)), CADICAL_assert (noccs (-lit) > 0); if (propfixed (lit) >= stats.all.fixed) continue; MSG ("keeping probe %d negated occs %" PRId64 "", lit, noccs (-lit)); *j++ = lit; } size_t remain = j - probes.begin (); -#ifndef QUIET +#ifndef CADICAL_QUIET size_t flushed = probes.size () - remain; #endif probes.resize (remain); @@ -140,7 +144,7 @@ void Internal::lookahead_flush_probes () { void Internal::lookahead_generate_probes () { - assert (probes.empty ()); + CADICAL_assert (probes.empty ()); // First determine all the literals which occur in binary clauses. It is // way faster to go over the clauses once, instead of walking the watch @@ -294,7 +298,7 @@ int Internal::lookahead_probing () { termination_forced = false; -#ifndef QUIET +#ifndef CADICAL_QUIET int old_failed = stats.failed; int64_t old_probed = stats.probed; #endif @@ -335,7 +339,7 @@ int Internal::lookahead_probing () { for (int idx = 1; idx <= max_var; idx++) propfixed (idx) = propfixed (-idx) = -1; - assert (unsat || propagated == trail.size ()); + CADICAL_assert (unsat || propagated == trail.size ()); propagated = propagated2 = trail.size (); int probe; @@ -382,7 +386,7 @@ int Internal::lookahead_probing () { sort_watches (); } -#ifndef QUIET +#ifndef CADICAL_QUIET int failed = stats.failed - old_failed; int64_t probed = stats.probed - old_probed; #endif @@ -435,12 +439,12 @@ CubesWithStatus Internal::generate_cubes (int depth, int min_depth) { reset_limits (); MSG ("generate cubes with %zu assumptions\n", assumptions.size ()); - assert (ntab.empty ()); + CADICAL_assert (ntab.empty ()); std::vector<int> current_assumptions{assumptions}; std::vector<std::vector<int>> cubes{{assumptions}}; auto loccs{lookahead_populate_locc ()}; LOG ("loccs populated\n"); - assert (ntab.empty ()); + CADICAL_assert (ntab.empty ()); for (int i = 0; i < depth; ++i) { LOG ("Probing at depth %i, currently %zu have been generated", i, @@ -449,8 +453,8 @@ CubesWithStatus Internal::generate_cubes (int depth, int min_depth) { cubes.clear (); for (size_t j = 0; j < cubes2.size (); ++j) { - assert (ntab.empty ()); - assert (!unsat); + CADICAL_assert (ntab.empty ()); + CADICAL_assert (!unsat); reset_assumptions (); for (auto lit : cubes2[j]) assume (lit); @@ -478,7 +482,7 @@ CubesWithStatus Internal::generate_cubes (int depth, int min_depth) { continue; } - assert (res != 0); + CADICAL_assert (res != 0); LOG ("splitting on lit %i", res); std::vector<int> cube1{cubes2[j]}; cube1.push_back (res); @@ -492,7 +496,7 @@ CubesWithStatus Internal::generate_cubes (int depth, int min_depth) { break; } - assert (std::for_each ( + CADICAL_assert (std::for_each ( std::begin (cubes), std::end (cubes), [] (std::vector<int> cube) { return non_tautological_cube (cube); })); reset_assumptions (); @@ -518,3 +522,5 @@ CubesWithStatus Internal::generate_cubes (int depth, int min_depth) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/lratchecker.cpp b/src/sat/cadical/cadical_lratchecker.cpp similarity index 94% rename from src/sat/cadical/lratchecker.cpp rename to src/sat/cadical/cadical_lratchecker.cpp index 1dcead375..5f342a370 100644 --- a/src/sat/cadical/lratchecker.cpp +++ b/src/sat/cadical/cadical_lratchecker.cpp @@ -1,12 +1,16 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ inline unsigned LratChecker::l2u (int lit) { - assert (lit); - assert (lit != INT_MIN); + CADICAL_assert (lit); + CADICAL_assert (lit != INT_MIN); unsigned res = 2 * (abs (lit) - 1); if (lit < 0) res++; @@ -15,13 +19,13 @@ inline unsigned LratChecker::l2u (int lit) { signed char &LratChecker::mark (int lit) { const unsigned u = l2u (lit); - assert (u < marks.size ()); + CADICAL_assert (u < marks.size ()); return marks[u]; } signed char &LratChecker::checked_lit (int lit) { const unsigned u = l2u (lit); - assert (u < checked_lits.size ()); + CADICAL_assert (u < checked_lits.size ()); return checked_lits[u]; } @@ -29,7 +33,7 @@ signed char &LratChecker::checked_lit (int lit) { LratCheckerClause *LratChecker::new_clause () { const size_t size = imported_clause.size (); - assert (size <= UINT_MAX); + CADICAL_assert (size <= UINT_MAX); const int off = size ? 1 : 0; const size_t bytes = sizeof (LratCheckerClause) + (size - off) * sizeof (int); @@ -42,9 +46,9 @@ LratCheckerClause *LratChecker::new_clause () { res->used = false; res->tautological = false; int *literals = res->literals, *p = literals; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG for (auto &b : checked_lits) - assert (!b); // = false; + CADICAL_assert (!b); // = false; #endif for (const auto &lit : imported_clause) { *p++ = lit; @@ -62,19 +66,19 @@ LratCheckerClause *LratChecker::new_clause () { } void LratChecker::delete_clause (LratCheckerClause *c) { - assert (c); + CADICAL_assert (c); if (!c->garbage) { - assert (num_clauses); + CADICAL_assert (num_clauses); num_clauses--; } else { - assert (num_garbage); + CADICAL_assert (num_garbage); num_garbage--; } delete[] (char *) c; } void LratChecker::enlarge_clauses () { - assert (num_clauses == size_clauses); + CADICAL_assert (num_clauses == size_clauses); const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1; LOG ("LRAT CHECKER enlarging clauses of checker from %" PRIu64 " to %" PRIu64, @@ -107,7 +111,7 @@ void LratChecker::collect_garbage_clauses () { for (LratCheckerClause *c = garbage, *next; c; c = next) next = c->next, delete_clause (c); - assert (!num_garbage); + CADICAL_assert (!num_garbage); garbage = 0; } @@ -125,7 +129,7 @@ LratChecker::LratChecker (Internal *i) uint64_t nonce = random.next (); if (!(nonce & 1)) nonce++; - assert (nonce), assert (nonce & 1); + CADICAL_assert (nonce), CADICAL_assert (nonce & 1); nonces[n] = nonce; } @@ -151,7 +155,7 @@ LratChecker::~LratChecker () { void LratChecker::enlarge_vars (int64_t idx) { - assert (0 < idx), assert (idx <= INT_MAX); + CADICAL_assert (0 < idx), CADICAL_assert (idx <= INT_MAX); int64_t new_size_vars = size_vars ? 2 * size_vars : 2; while (idx >= new_size_vars) @@ -163,13 +167,13 @@ void LratChecker::enlarge_vars (int64_t idx) { marks.resize (2 * new_size_vars); checked_lits.resize (2 * new_size_vars); - assert (idx < new_size_vars); + CADICAL_assert (idx < new_size_vars); size_vars = new_size_vars; } inline void LratChecker::import_literal (int lit) { - assert (lit); - assert (lit != INT_MIN); + CADICAL_assert (lit); + CADICAL_assert (lit != INT_MIN); int idx = abs (lit); if (idx >= size_vars) enlarge_vars (idx); @@ -184,7 +188,7 @@ void LratChecker::import_clause (const vector<int> &c) { /*------------------------------------------------------------------------*/ uint64_t LratChecker::reduce_hash (uint64_t hash, uint64_t size) { - assert (size > 0); + CADICAL_assert (size > 0); unsigned shift = 32; uint64_t res = hash; while ((((uint64_t) 1) << shift) > size) { @@ -192,12 +196,12 @@ uint64_t LratChecker::reduce_hash (uint64_t hash, uint64_t size) { shift >>= 1; } res &= size - 1; - assert (res < size); + CADICAL_assert (res < size); return res; } uint64_t LratChecker::compute_hash (const int64_t id) { - assert (id > 0); + CADICAL_assert (id > 0); unsigned j = id % num_nonces; uint64_t tmp = nonces[j] * (uint64_t) id; return last_hash = tmp; @@ -236,23 +240,23 @@ bool LratChecker::check_resolution (vector<int64_t> proof_chain) { return true; } // LOG (imported_clause, "LRAT CHECKER checking clause with resolution"); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG for (auto &b : checked_lits) - assert (!b); // = false; + CADICAL_assert (!b); // = false; #endif if (!proof_chain.size () || proof_chain.back () < 0) return false; LratCheckerClause *c = *find (proof_chain.back ()); - assert (c); + CADICAL_assert (c); for (int *i = c->literals; i < c->literals + c->size; i++) { int lit = *i; checked_lit (lit) = true; - assert (!checked_lit (-lit)); + CADICAL_assert (!checked_lit (-lit)); } for (auto p = proof_chain.end () - 2; p >= proof_chain.begin (); p--) { auto &id = *p; c = *find (id); - assert (c); // since this is checked in check already + CADICAL_assert (c); // since this is checked in check already for (int *i = c->literals; i < c->literals + c->size; i++) { int lit = *i; if (!checked_lit (-lit)) @@ -298,16 +302,16 @@ bool LratChecker::check_resolution (vector<int64_t> proof_chain) { bool LratChecker::check (vector<int64_t> proof_chain) { LOG (imported_clause, "LRAT CHECKER checking clause"); stats.checks++; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG for (auto &b : checked_lits) - assert (!b); // = false; + CADICAL_assert (!b); // = false; #endif bool taut = false; for (const auto &lit : imported_clause) { // tautological clauses checked_lit (-lit) = true; if (checked_lit (lit)) { LOG (imported_clause, "LRAT CHECKER clause tautological"); - assert (!proof_chain.size ()); // would be unnecessary hence a bug + CADICAL_assert (!proof_chain.size ()); // would be unnecessary hence a bug taut = true; } } @@ -468,7 +472,7 @@ void LratChecker::add_original_clause (int64_t id, bool, fatal_message_end (); } } - assert (id); + CADICAL_assert (id); insert (); imported_clause.clear (); STOP (checking); @@ -483,7 +487,7 @@ void LratChecker::add_derived_clause (int64_t id, bool, stats.derived++; import_clause (c); last_id = id; - assert (id == current_id + 1); + CADICAL_assert (id == current_id + 1); current_id = id; if (size_clauses) { LratCheckerClause **p = find (id), *d = *p; @@ -495,7 +499,7 @@ void LratChecker::add_derived_clause (int64_t id, bool, fatal_message_end (); } } - assert (id); + CADICAL_assert (id); bool failed = true; if (check (proof_chain) && check_resolution (proof_chain)) { failed = false; @@ -547,7 +551,7 @@ void LratChecker::add_assumption (int a) { assumptions.push_back (a); } void LratChecker::add_constraint (const vector<int> &c) { constraint.clear (); for (auto &lit : c) { - assert (lit); + CADICAL_assert (lit); if (std::find (constraint.begin (), constraint.end (), lit) != constraint.end ()) continue; @@ -591,7 +595,7 @@ void LratChecker::conclude_unsat (ConclusionType conclusion, } return; } else { - assert (conclusion == CONSTRAINT); + CADICAL_assert (conclusion == CONSTRAINT); if (constraint.size () != ids.size ()) { fatal_message_start (); fputs ("not complete conclusion given for constraint\n", stderr); @@ -646,7 +650,7 @@ void LratChecker::delete_clause (int64_t id, bool, const vector<int> &c) { // Remove from hash table, mark as garbage, connect to garbage list. num_garbage++; - assert (num_clauses); + CADICAL_assert (num_clauses); num_clauses--; *p = d->next; d->next = garbage; @@ -676,7 +680,7 @@ void LratChecker::weaken_minus (int64_t id, const vector<int> &c) { LOG (c, "LRAT CHECKER saving clause[%" PRId64 "] to restore later", id); import_clause (c); - assert (id <= current_id); + CADICAL_assert (id <= current_id); last_id = id; LratCheckerClause **p = find (id), *d = *p; if (d) { @@ -756,7 +760,7 @@ void LratChecker::finalize_clause (int64_t id, const vector<int> &c) { stats.finalized++; num_finalized++; import_clause (c); - assert (id <= current_id); + CADICAL_assert (id <= current_id); last_id = id; LratCheckerClause **p = find (id), *d = *p; if (d) { @@ -827,3 +831,5 @@ void LratChecker::dump () { void LratChecker::begin_proof (int64_t id) { current_id = id; } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/lrattracer.cpp b/src/sat/cadical/cadical_lrattracer.cpp similarity index 89% rename from src/sat/cadical/lrattracer.cpp rename to src/sat/cadical/cadical_lrattracer.cpp index 0bcd9a35b..4da728e18 100644 --- a/src/sat/cadical/lrattracer.cpp +++ b/src/sat/cadical/cadical_lrattracer.cpp @@ -1,14 +1,18 @@ +#include "global.h" + #include "internal.hpp" #include <limits.h> +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ LratTracer::LratTracer (Internal *i, File *f, bool b) : internal (i), file (f), binary (b) -#ifndef QUIET +#ifndef CADICAL_QUIET , added (0), deleted (0) #endif @@ -31,17 +35,17 @@ LratTracer::~LratTracer () { /*------------------------------------------------------------------------*/ inline void LratTracer::put_binary_zero () { - assert (binary); - assert (file); + CADICAL_assert (binary); + CADICAL_assert (file); file->put ((unsigned char) 0); } inline void LratTracer::put_binary_lit (int lit) { - assert (binary); - assert (file); - assert (lit != INT_MIN); + CADICAL_assert (binary); + CADICAL_assert (file); + CADICAL_assert (lit != INT_MIN); unsigned idx = abs (lit); - assert (idx < (1u << 31)); + CADICAL_assert (idx < (1u << 31)); unsigned x = 2 * idx + (lit < 0); unsigned char ch; while (x & ~0x7f) { @@ -54,8 +58,8 @@ inline void LratTracer::put_binary_lit (int lit) { } inline void LratTracer::put_binary_id (int64_t id) { - assert (binary); - assert (file); + CADICAL_assert (binary); + CADICAL_assert (file); uint64_t x = abs (id); x = 2 * x + (id < 0); unsigned char ch; @@ -130,7 +134,7 @@ void LratTracer::add_derived_clause (int64_t id, bool, return; LOG ("LRAT TRACER tracing addition of derived clause"); lrat_add_clause (id, clause, chain); -#ifndef QUIET +#ifndef CADICAL_QUIET added++; #endif } @@ -140,7 +144,7 @@ void LratTracer::delete_clause (int64_t id, bool, const vector<int> &) { return; LOG ("LRAT TRACER tracing deletion of clause"); lrat_delete_clause (id); -#ifndef QUIET +#ifndef CADICAL_QUIET deleted++; #endif } @@ -156,7 +160,7 @@ void LratTracer::begin_proof (int64_t id) { bool LratTracer::closed () { return file->closed (); } -#ifndef QUIET +#ifndef CADICAL_QUIET void LratTracer::print_statistics () { uint64_t bytes = file->bytes (); @@ -172,9 +176,9 @@ void LratTracer::print_statistics () { #endif void LratTracer::close (bool print) { - assert (!closed ()); + CADICAL_assert (!closed ()); file->close (); -#ifndef QUIET +#ifndef CADICAL_QUIET if (print) { MSG ("LRAT proof file '%s' closed", file->name ()); print_statistics (); @@ -185,9 +189,9 @@ void LratTracer::close (bool print) { } void LratTracer::flush (bool print) { - assert (!closed ()); + CADICAL_assert (!closed ()); file->flush (); -#ifndef QUIET +#ifndef CADICAL_QUIET if (print) { MSG ("LRAT proof file '%s' flushed", file->name ()); print_statistics (); @@ -198,3 +202,5 @@ void LratTracer::flush (bool print) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/lucky.cpp b/src/sat/cadical/cadical_lucky.cpp similarity index 89% rename from src/sat/cadical/lucky.cpp rename to src/sat/cadical/cadical_lucky.cpp index 19da3ee28..d67782db4 100644 --- a/src/sat/cadical/lucky.cpp +++ b/src/sat/cadical/cadical_lucky.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { // It turns out that even in the competition there are formulas which are @@ -29,8 +33,8 @@ int Internal::unlucky (int res) { int Internal::trivially_false_satisfiable () { LOG ("checking that all clauses contain a negative literal"); - assert (!level); - assert (assumptions.empty ()); + CADICAL_assert (!level); + CADICAL_assert (assumptions.empty ()); for (const auto &c : clauses) { if (terminated_asynchronously (100)) return unlucky (-1); @@ -66,7 +70,7 @@ int Internal::trivially_false_satisfiable () { search_assume_decision (-idx); if (propagate ()) continue; - assert (level > 0); + CADICAL_assert (level > 0); LOG ("propagation failed including redundant clauses"); return unlucky (0); } @@ -76,8 +80,8 @@ int Internal::trivially_false_satisfiable () { int Internal::trivially_true_satisfiable () { LOG ("checking that all clauses contain a positive literal"); - assert (!level); - assert (assumptions.empty ()); + CADICAL_assert (!level); + CADICAL_assert (assumptions.empty ()); for (const auto &c : clauses) { if (terminated_asynchronously (100)) return unlucky (-1); @@ -113,7 +117,7 @@ int Internal::trivially_true_satisfiable () { search_assume_decision (idx); if (propagate ()) continue; - assert (level > 0); + CADICAL_assert (level > 0); LOG ("propagation failed including redundant clauses"); return unlucky (0); } @@ -136,7 +140,7 @@ inline bool Internal::lucky_propagate_discrepency (int dec) { return true; } else { analyze (); - assert (!level); + CADICAL_assert (!level); no_conflict = propagate (); if (!no_conflict) { analyze (); @@ -149,9 +153,9 @@ inline bool Internal::lucky_propagate_discrepency (int dec) { int Internal::forward_false_satisfiable () { LOG ("checking increasing variable index false assignment"); - assert (!unsat); - assert (!level); - assert (assumptions.empty ()); + CADICAL_assert (!unsat); + CADICAL_assert (!level); + CADICAL_assert (assumptions.empty ()); for (auto idx : vars) { START: if (terminated_asynchronously (100)) @@ -167,16 +171,16 @@ int Internal::forward_false_satisfiable () { goto START; } VERBOSE (1, "forward assuming variables false satisfies formula"); - assert (satisfied ()); + CADICAL_assert (satisfied ()); stats.lucky.forward.zero++; return 10; } int Internal::forward_true_satisfiable () { LOG ("checking increasing variable index true assignment"); - assert (!unsat); - assert (!level); - assert (assumptions.empty ()); + CADICAL_assert (!unsat); + CADICAL_assert (!level); + CADICAL_assert (assumptions.empty ()); for (auto idx : vars) { START: if (terminated_asynchronously (10)) @@ -192,7 +196,7 @@ int Internal::forward_true_satisfiable () { goto START; } VERBOSE (1, "forward assuming variables true satisfies formula"); - assert (satisfied ()); + CADICAL_assert (satisfied ()); stats.lucky.forward.one++; return 10; } @@ -201,9 +205,9 @@ int Internal::forward_true_satisfiable () { int Internal::backward_false_satisfiable () { LOG ("checking decreasing variable index false assignment"); - assert (!unsat); - assert (!level); - assert (assumptions.empty ()); + CADICAL_assert (!unsat); + CADICAL_assert (!level); + CADICAL_assert (assumptions.empty ()); for (int idx = max_var; idx > 0; idx--) { START: if (terminated_asynchronously (10)) @@ -219,16 +223,16 @@ int Internal::backward_false_satisfiable () { goto START; } VERBOSE (1, "backward assuming variables false satisfies formula"); - assert (satisfied ()); + CADICAL_assert (satisfied ()); stats.lucky.backward.zero++; return 10; } int Internal::backward_true_satisfiable () { LOG ("checking decreasing variable index true assignment"); - assert (!unsat); - assert (!level); - assert (assumptions.empty ()); + CADICAL_assert (!unsat); + CADICAL_assert (!level); + CADICAL_assert (assumptions.empty ()); for (int idx = max_var; idx > 0; idx--) { START: if (terminated_asynchronously (10)) @@ -244,7 +248,7 @@ int Internal::backward_true_satisfiable () { goto START; } VERBOSE (1, "backward assuming variables true satisfies formula"); - assert (satisfied ()); + CADICAL_assert (satisfied ()); stats.lucky.backward.one++; return 10; } @@ -259,8 +263,8 @@ int Internal::backward_true_satisfiable () { int Internal::positive_horn_satisfiable () { LOG ("checking that all clauses are positive horn satisfiable"); - assert (!level); - assert (assumptions.empty ()); + CADICAL_assert (!level); + CADICAL_assert (assumptions.empty ()); for (const auto &c : clauses) { if (terminated_asynchronously (10)) return unlucky (-1); @@ -289,7 +293,7 @@ int Internal::positive_horn_satisfiable () { LOG (c, "no positive unassigned literal in"); return unlucky (0); } - assert (positive_literal > 0); + CADICAL_assert (positive_literal > 0); LOG (c, "found positive literal %d in", positive_literal); search_assume_decision (positive_literal); if (propagate ()) @@ -310,16 +314,16 @@ int Internal::positive_horn_satisfiable () { return unlucky (0); } VERBOSE (1, "clauses are positive horn satisfied"); - assert (!conflict); - assert (satisfied ()); + CADICAL_assert (!conflict); + CADICAL_assert (satisfied ()); stats.lucky.horn.positive++; return 10; } int Internal::negative_horn_satisfiable () { LOG ("checking that all clauses are negative horn satisfiable"); - assert (!level); - assert (assumptions.empty ()); + CADICAL_assert (!level); + CADICAL_assert (assumptions.empty ()); for (const auto &c : clauses) { if (terminated_asynchronously (10)) return unlucky (-1); @@ -350,7 +354,7 @@ int Internal::negative_horn_satisfiable () { LOG (c, "no negative unassigned literal in"); return unlucky (0); } - assert (negative_literal < 0); + CADICAL_assert (negative_literal < 0); LOG (c, "found negative literal %d in", negative_literal); search_assume_decision (negative_literal); if (propagate ()) @@ -371,8 +375,8 @@ int Internal::negative_horn_satisfiable () { return unlucky (0); } VERBOSE (1, "clauses are negative horn satisfied"); - assert (!conflict); - assert (satisfied ()); + CADICAL_assert (!conflict); + CADICAL_assert (satisfied ()); stats.lucky.horn.negative++; return 10; } @@ -380,7 +384,7 @@ int Internal::negative_horn_satisfiable () { /*------------------------------------------------------------------------*/ int Internal::lucky_phases () { - assert (!level); + CADICAL_assert (!level); require_mode (SEARCH); if (!opts.lucky) return 0; @@ -394,7 +398,7 @@ int Internal::lucky_phases () { START (search); START (lucky); - assert (!searching_lucky_phases); + CADICAL_assert (!searching_lucky_phases); searching_lucky_phases = true; stats.lucky.tried++; const int64_t active_before = stats.active; @@ -414,11 +418,11 @@ int Internal::lucky_phases () { if (!res) res = negative_horn_satisfiable (); if (res < 0) - assert (termination_forced), res = 0; + CADICAL_assert (termination_forced), res = 0; if (res == 10) stats.lucky.succeeded++; report ('l', !res); - assert (searching_lucky_phases); + CADICAL_assert (searching_lucky_phases); const int64_t units = active_before - stats.active; @@ -432,3 +436,5 @@ int Internal::lucky_phases () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/message.cpp b/src/sat/cadical/cadical_message.cpp similarity index 97% rename from src/sat/cadical/message.cpp rename to src/sat/cadical/cadical_message.cpp index 9f9a212ab..42d8c0e48 100644 --- a/src/sat/cadical/message.cpp +++ b/src/sat/cadical/cadical_message.cpp @@ -1,9 +1,13 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ -#ifndef QUIET +#ifndef CADICAL_QUIET /*------------------------------------------------------------------------*/ void Internal::print_prefix () { fputs (prefix.c_str (), stdout); } @@ -130,7 +134,7 @@ void Internal::phase (const char *phase, int64_t count, const char *fmt, } /*------------------------------------------------------------------------*/ -#endif // ifndef QUIET +#endif // ifndef CADICAL_QUIET /*------------------------------------------------------------------------*/ void Internal::warning (const char *fmt, ...) { @@ -210,3 +214,5 @@ void fatal (const char *fmt, ...) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/minimize.cpp b/src/sat/cadical/cadical_minimize.cpp similarity index 90% rename from src/sat/cadical/minimize.cpp rename to src/sat/cadical/cadical_minimize.cpp index ff9b89df4..97af5fbe6 100644 --- a/src/sat/cadical/minimize.cpp +++ b/src/sat/cadical/cadical_minimize.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { // Functions for learned clause minimization. We only have the recursive @@ -16,7 +20,7 @@ namespace CaDiCaL { bool Internal::minimize_literal (int lit, int depth) { LOG ("attempt to minimize lit %d at depth %d", lit, depth); - assert (val (lit) > 0); + CADICAL_assert (val (lit) > 0); Flags &f = flags (lit); Var &v = var (lit); if (!v.level || f.removable || f.keep) @@ -31,18 +35,18 @@ bool Internal::minimize_literal (int lit, int depth) { if (depth > opts.minimizedepth) return false; bool res = true; - assert (v.reason); + CADICAL_assert (v.reason); if (opts.minimizeticks) stats.ticks.search[stable]++; if (v.reason == external_reason) { - assert (!opts.exteagerreasons); + CADICAL_assert (!opts.exteagerreasons); v.reason = learn_external_reason_clause (lit, 0, true); if (!v.reason) { - assert (!v.level); + CADICAL_assert (!v.level); return true; } } - assert (v.reason != external_reason); + CADICAL_assert (v.reason != external_reason); const const_literal_iterator end = v.reason->end (); const_literal_iterator i; for (i = v.reason->begin (); res && i != end; i++) { @@ -71,7 +75,7 @@ struct minimize_trail_positive_rank { minimize_trail_positive_rank (Internal *s) : internal (s) {} typedef unsigned Type; Type operator() (const int &a) const { - assert (internal->val (a)); + CADICAL_assert (internal->val (a)); return (unsigned) internal->var (a).trail; } }; @@ -89,7 +93,7 @@ struct minimize_trail_level_positive_rank { minimize_trail_level_positive_rank (Internal *s) : internal (s) {} typedef uint64_t Type; Type operator() (const int &a) const { - assert (internal->val (a)); + CADICAL_assert (internal->val (a)); Var &v = internal->var (a); uint64_t res = v.level; res <<= 32; @@ -114,15 +118,15 @@ void Internal::minimize_clause () { external->check_learned_clause (); // check 1st UIP learned clause first minimize_sort_clause (); - assert (minimized.empty ()); - assert (minimize_chain.empty ()); + CADICAL_assert (minimized.empty ()); + CADICAL_assert (minimize_chain.empty ()); const auto end = clause.end (); auto j = clause.begin (), i = j; std::vector<int> stack; for (; i != end; i++) { if (minimize_literal (-*i)) { if (lrat) { - assert (mini_chain.empty ()); + CADICAL_assert (mini_chain.empty ()); calculate_minimize_chain (-*i, stack); for (auto p : mini_chain) { minimize_chain.push_back (p); @@ -153,19 +157,19 @@ void Internal::minimize_clause () { // minimize version. Unlike the minimize version, we have to keep literals // on the stack in order to push its reason later. void Internal::calculate_minimize_chain (int lit, std::vector<int> &stack) { - assert (stack.empty ()); + CADICAL_assert (stack.empty ()); stack.push_back (vidx (lit)); while (!stack.empty ()) { const int idx = stack.back (); - assert (idx); + CADICAL_assert (idx); stack.pop_back (); if (idx < 0) { Var &v = var (idx); mini_chain.push_back (v.reason->id); continue; } - assert (idx); + CADICAL_assert (idx); Flags &f = flags (idx); Var &v = var (idx); if (f.keep || f.added || f.poison) { @@ -182,7 +186,7 @@ void Internal::calculate_minimize_chain (int lit, std::vector<int> &stack) { continue; } f.added = true; - assert (v.reason && f.removable); + CADICAL_assert (v.reason && f.removable); const const_literal_iterator end = v.reason->end (); const_literal_iterator i; LOG (v.reason, "LRAT chain for lit %d at depth %zd by going over", lit, @@ -195,7 +199,7 @@ void Internal::calculate_minimize_chain (int lit, std::vector<int> &stack) { stack.push_back (vidx (other)); } } - assert (stack.empty ()); + CADICAL_assert (stack.empty ()); } // Sort the literals in reverse assignment order (thus trail order) to @@ -215,10 +219,12 @@ void Internal::clear_minimized_literals () { f.poison = f.removable = f.shrinkable = f.added = false; } for (const auto &lit : clause) - assert (!flags (lit).shrinkable), flags (lit).keep = + CADICAL_assert (!flags (lit).shrinkable), flags (lit).keep = flags (lit).shrinkable = flags (lit).added = false; minimized.clear (); } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/occs.cpp b/src/sat/cadical/cadical_occs.cpp similarity index 80% rename from src/sat/cadical/occs.cpp rename to src/sat/cadical/cadical_occs.cpp index c1d987416..f79d7a690 100644 --- a/src/sat/cadical/occs.cpp +++ b/src/sat/cadical/cadical_occs.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -13,13 +17,13 @@ void Internal::init_occs () { } void Internal::reset_occs () { - assert (occurring ()); + CADICAL_assert (occurring ()); erase_vector (otab); LOG ("reset occurrence lists"); } void Internal::clear_occs () { - assert (occurring ()); + CADICAL_assert (occurring ()); for (auto &occ : otab) occ.clear (); LOG ("clear occurrence lists"); @@ -30,23 +34,25 @@ void Internal::clear_occs () { // One-sided occurrence counter (each literal has its own counter). void Internal::init_noccs () { - assert (ntab.empty ()); + CADICAL_assert (ntab.empty ()); if (ntab.size () < 2 * vsize) ntab.resize (2 * vsize, 0); LOG ("initialized two-sided occurrence counters"); } void Internal::clear_noccs () { - assert (!ntab.empty ()); + CADICAL_assert (!ntab.empty ()); for (auto &nt : ntab) nt = 0; LOG ("clear two-sided occurrence counters"); } void Internal::reset_noccs () { - assert (!max_var || !ntab.empty ()); + CADICAL_assert (!max_var || !ntab.empty ()); erase_vector (ntab); LOG ("reset two-sided occurrence counters"); } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/options.cpp b/src/sat/cadical/cadical_options.cpp similarity index 93% rename from src/sat/cadical/options.cpp rename to src/sat/cadical/cadical_options.cpp index 26dcc3d54..88eba53ab 100644 --- a/src/sat/cadical/options.cpp +++ b/src/sat/cadical/cadical_options.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -81,12 +85,12 @@ void Options::initialize_from_environment (int &val, const char *name, const int L, const int H) { char key[80], *q; const char *p; - assert (strlen (name) + strlen ("CADICAL_") + 1 < sizeof (key)); + CADICAL_assert (strlen (name) + strlen ("CADICAL_") + 1 < sizeof (key)); for (p = "CADICAL_", q = key; *p; p++) *q++ = *p; for (p = name; *p; p++) *q++ = toupper (*p); - assert (q < key + sizeof (key)); + CADICAL_assert (q < key + sizeof (key)); *q = 0; const char *val_str = getenv (key); if (!val_str) @@ -102,7 +106,7 @@ void Options::initialize_from_environment (int &val, const char *name, // Initialize all the options to their default value 'V'. Options::Options (Internal *s) : internal (s) { - assert (number_of_options == sizeof Options::table / sizeof (Option)); + CADICAL_assert (number_of_options == sizeof Options::table / sizeof (Option)); // First initialize them according to defaults in 'options.hpp'. // @@ -119,12 +123,12 @@ Options::Options (Internal *s) : internal (s) { if (strcmp (prev, #N) > 0) \ FATAL ("'%s' ordered before '" #N "' in 'options.hpp'", prev); \ N = (int) (V); \ - assert (&val (i) == &N); \ + CADICAL_assert (&val (i) == &N); \ /* The order of initializing static data is undefined and thus */ \ /* it might be the case that the 'table' is not initialized yet. */ \ /* Thus this construction just reinitializes the table too even */ \ /* though it might not be necessary. */ \ - assert (!table[i].name || !strcmp (table[i].name, #N)); \ + CADICAL_assert (!table[i].name || !strcmp (table[i].name, #N)); \ table[i] = {#N, (int) (V), (int) (L), (int) (H), \ (int) (O), (bool) (P), D}; \ prev = #N; \ @@ -135,11 +139,11 @@ Options::Options (Internal *s) : internal (s) { // Check consistency in debugging mode. // -#ifndef NDEBUG - assert (i == number_of_options); - assert (!has ("aaaaa")); - assert (!has ("non-existing-option")); - assert (!has ("zzzzz")); +#ifndef CADICAL_NDEBUG + CADICAL_assert (i == number_of_options); + CADICAL_assert (!has ("aaaaa")); + CADICAL_assert (!has ("non-existing-option")); + CADICAL_assert (!has ("zzzzz")); #endif // Now overwrite default options with environment values. @@ -153,7 +157,7 @@ Options::Options (Internal *s) : internal (s) { /*------------------------------------------------------------------------*/ void Options::set (Option *o, int new_val) { - assert (o); + CADICAL_assert (o); int &val = o->val (this), old_val = val; if (old_val == new_val) { LOG ("keeping value '%d' of option '%s'", old_val, o->name); @@ -193,7 +197,7 @@ int Options::get (const char *name) { void Options::print () { unsigned different = 0; -#ifdef QUIET +#ifdef CADICAL_QUIET const bool verbose = false; #endif char buffer[256]; @@ -278,7 +282,7 @@ void Options::optimize (int val) { LOG ("optimization mode '%d' for '%s' " \ "gives '%" PRId64 "' instead of '%d", \ val, #N, new_val, (int) (V)); \ - assert (new_val <= INT_MAX); \ + CADICAL_assert (new_val <= INT_MAX); \ N = (int) new_val; \ increased++; \ } while (0); @@ -299,8 +303,8 @@ void Options::disable_preprocessing () { if (!(N)) \ break; \ LOG ("plain mode disables '%s'", #N); \ - assert ((L) == 0); \ - assert ((H) == 1); \ + CADICAL_assert ((L) == 0); \ + CADICAL_assert ((H) == 1); \ count++; \ N = 0; \ } while (0); @@ -357,3 +361,5 @@ void Options::copy (Options &other) const { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/parse.cpp b/src/sat/cadical/cadical_parse.cpp similarity index 96% rename from src/sat/cadical/parse.cpp rename to src/sat/cadical/cadical_parse.cpp index 25334385e..0668aca7d 100644 --- a/src/sat/cadical/parse.cpp +++ b/src/sat/cadical/cadical_parse.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + /*------------------------------------------------------------------------*/ namespace CaDiCaL { @@ -37,7 +41,7 @@ inline const char *Parser::parse_string (const char *str, char prev) { inline const char *Parser::parse_positive_int (int &ch, int &res, const char *name) { - assert (isdigit (ch)); + CADICAL_assert (isdigit (ch)); res = ch - '0'; while (isdigit (ch = parse_char ())) { int digit = ch - '0'; @@ -90,7 +94,7 @@ inline const char *Parser::parse_lit (int &ch, int &lit, int &vars, const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { -#ifndef QUIET +#ifndef CADICAL_QUIET double start = internal->time (); #endif @@ -143,7 +147,7 @@ const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { // or 'p inccnf' of incremental 'INCCNF' file. // if (ch == 'c') { - assert (!found_inccnf_header); + CADICAL_assert (!found_inccnf_header); if (strict == STRICT) { const char *err = parse_string ("nf ", 'c'); if (err) @@ -266,19 +270,19 @@ const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { if (!found_inccnf_header && parsed < clauses && strict != FORCED) PER ("clause missing"); -#ifndef QUIET +#ifndef CADICAL_QUIET double end = internal->time (); MSG ("parsed %d clauses in %.2f seconds %s time", parsed, end - start, internal->opts.realtime ? "real" : "process"); #endif -#ifndef QUIET +#ifndef CADICAL_QUIET start = end; size_t num_cubes = 0; #endif if (ch == 'a') { - assert (parse_inccnf_too); - assert (found_inccnf_header); + CADICAL_assert (parse_inccnf_too); + CADICAL_assert (found_inccnf_header); if (!*parse_inccnf_too) *parse_inccnf_too = true; for (;;) { @@ -305,7 +309,7 @@ const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { if (cubes) cubes->push_back (lit); if (!lit) { -#ifndef QUIET +#ifndef CADICAL_QUIET num_cubes++; #endif for (;;) { @@ -332,7 +336,7 @@ const char *Parser::parse_dimacs_non_profiled (int &vars, int strict) { if (lit) PER ("last cube without terminating '0'"); } -#ifndef QUIET +#ifndef CADICAL_QUIET if (found_inccnf_header) { double end = internal->time (); MSG ("parsed %zd cubes in %.2f seconds %s time", num_cubes, end - start, @@ -372,7 +376,7 @@ const char *Parser::parse_solution_non_profiled () { ch = parse_char (); if (ch != '\n') PER ("expected new-line after 's SATISFIABLE'"); -#ifndef QUIET +#ifndef CADICAL_QUIET int count = 0; #endif for (;;) { @@ -399,7 +403,7 @@ const char *Parser::parse_solution_non_profiled () { PER ("variable %d occurs twice", abs (lit)); LOG ("solution %d", lit); external->solution[abs (lit)] = sign (lit); -#ifndef QUIET +#ifndef CADICAL_QUIET count++; #endif if (ch == '\r') @@ -419,7 +423,7 @@ const char *Parser::parse_solution_non_profiled () { // implicit 'return' in PER in the non-profiled versions. const char *Parser::parse_dimacs (int &vars, int strict) { - assert (strict == FORCED || strict == RELAXED || strict == STRICT); + CADICAL_assert (strict == FORCED || strict == RELAXED || strict == STRICT); START (parse); const char *err = parse_dimacs_non_profiled (vars, strict); STOP (parse); @@ -434,3 +438,5 @@ const char *Parser::parse_solution () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/phases.cpp b/src/sat/cadical/cadical_phases.cpp similarity index 94% rename from src/sat/cadical/phases.cpp rename to src/sat/cadical/cadical_phases.cpp index 92edd28a0..f7e4eaffd 100644 --- a/src/sat/cadical/phases.cpp +++ b/src/sat/cadical/cadical_phases.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { void Internal::copy_phases (vector<signed char> &dst) { @@ -42,3 +46,5 @@ void Internal::unphase (int lit) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/probe.cpp b/src/sat/cadical/cadical_probe.cpp similarity index 90% rename from src/sat/cadical/probe.cpp rename to src/sat/cadical/cadical_probe.cpp index acab6405b..19ff0961f 100644 --- a/src/sat/cadical/probe.cpp +++ b/src/sat/cadical/cadical_probe.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -19,7 +23,7 @@ bool Internal::inprobing () { if (!preprocessing && !opts.inprocessing) return false; if (preprocessing) - assert (lim.preprocessing); + CADICAL_assert (lim.preprocessing); if (stats.inprobingphases && last.inprobe.reductions == stats.reductions) return false; return lim.inprobe <= stats.conflicts; @@ -86,9 +90,9 @@ void Internal::init_probehbr_lrat () { void Internal::get_probehbr_lrat (int lit, int uip) { if (!lrat || opts.probehbr) return; - assert (lit); - assert (lrat_chain.empty ()); - assert (val (uip) < 0); + CADICAL_assert (lit); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (val (uip) < 0); lrat_chain = probehbr_chains[vlit (lit)][vlit (uip)]; int64_t id = unit_id (-uip); lrat_chain.push_back (id); @@ -100,9 +104,9 @@ void Internal::get_probehbr_lrat (int lit, int uip) { void Internal::set_probehbr_lrat (int lit, int uip) { if (!lrat || opts.probehbr) return; - assert (lit); - assert (lrat_chain.size ()); - assert (probehbr_chains[vlit (lit)][vlit (uip)].empty ()); + CADICAL_assert (lit); + CADICAL_assert (lrat_chain.size ()); + CADICAL_assert (probehbr_chains[vlit (lit)][vlit (uip)].empty ()); probehbr_chains[vlit (lit)][vlit (uip)] = lrat_chain; lrat_chain.clear (); } @@ -151,20 +155,20 @@ int Internal::probe_dominator (int a, int b) { require_mode (PROBE); int l = a, k = b; Var *u = &var (l), *v = &var (k); - assert (val (l) > 0), assert (val (k) > 0); - assert (u->level == 1), assert (v->level == 1); + CADICAL_assert (val (l) > 0), CADICAL_assert (val (k) > 0); + CADICAL_assert (u->level == 1), CADICAL_assert (v->level == 1); while (l != k) { if (u->trail > v->trail) swap (l, k), swap (u, v); if (!get_parent_reason_literal (l)) return l; int parent = get_parent_reason_literal (k); - assert (parent), assert (val (parent) > 0); + CADICAL_assert (parent), CADICAL_assert (val (parent) > 0); v = &var (k = parent); - assert (v->level == 1); + CADICAL_assert (v->level == 1); } LOG ("dominator %d of %d and %d", l, a, b); - assert (val (l) > 0); + CADICAL_assert (val (l) > 0); return l; } @@ -217,17 +221,17 @@ int Internal::probe_dominator (int a, int b) { inline int Internal::hyper_binary_resolve (Clause *reason) { require_mode (PROBE); - assert (level == 1); - assert (reason->size > 2); + CADICAL_assert (level == 1); + CADICAL_assert (reason->size > 2); const const_literal_iterator end = reason->end (); const int *lits = reason->literals; const_literal_iterator k; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG // First literal unassigned, all others false. - assert (!val (lits[0])); + CADICAL_assert (!val (lits[0])); for (k = lits + 1; k != end; k++) - assert (val (*k) < 0); - assert (var (lits[1]).level == 1); + CADICAL_assert (val (*k) < 0); + CADICAL_assert (var (lits[1]).level == 1); #endif LOG (reason, "hyper binary resolving"); stats.hbrs++; @@ -236,7 +240,7 @@ inline int Internal::hyper_binary_resolve (Clause *reason) { int dom = -lit, non_root_level_literals = 0; for (k = lits + 2; k != end; k++) { const int other = -*k; - assert (val (other) > 0); + CADICAL_assert (val (other) > 0); if (!var (other).level) continue; dom = probe_dominator (dom, other); @@ -252,7 +256,7 @@ inline int Internal::hyper_binary_resolve (Clause *reason) { stats.hbreds++; LOG ("new %s hyper binary resolvent %d %d", (red ? "redundant" : "irredundant"), -dom, lits[0]); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); clause.push_back (-dom); clause.push_back (lits[0]); probe_dominator_lrat (dom, reason); @@ -271,7 +275,7 @@ inline int Internal::hyper_binary_resolve (Clause *reason) { } } else if (non_root_level_literals && lrat) { // still calculate LRAT and remember for later - assert (!opts.probehbr); + CADICAL_assert (!opts.probehbr); probe_dominator_lrat (dom, reason); clear_analyzed_literals (); set_probehbr_lrat (dom, lits[0]); @@ -292,13 +296,13 @@ inline int Internal::hyper_binary_resolve (Clause *reason) { inline void Internal::probe_assign (int lit, int parent) { require_mode (PROBE); int idx = vidx (lit); - assert (!val (idx)); - assert (!flags (idx).eliminated () || !parent); - assert (!parent || val (parent) > 0); + CADICAL_assert (!val (idx)); + CADICAL_assert (!flags (idx).eliminated () || !parent); + CADICAL_assert (!parent || val (parent) > 0); Var &v = var (idx); v.level = level; v.trail = (int) trail.size (); - assert ((int) num_assigned < max_var); + CADICAL_assert ((int) num_assigned < max_var); num_assigned++; v.reason = level ? probe_reason : 0; probe_reason = 0; @@ -306,11 +310,11 @@ inline void Internal::probe_assign (int lit, int parent) { if (!level) learn_unit_clause (lit); else - assert (level == 1); + CADICAL_assert (level == 1); const signed char tmp = sign (lit); set_val (idx, tmp); - assert (val (lit) > 0); - assert (val (-lit) < 0); + CADICAL_assert (val (lit) > 0); + CADICAL_assert (val (-lit) < 0); trail.push_back (lit); // Do not save the current phase during inprocessing but remember the @@ -331,8 +335,8 @@ inline void Internal::probe_assign (int lit, int parent) { void Internal::probe_assign_decision (int lit) { require_mode (PROBE); - assert (!level); - assert (propagated == trail.size ()); + CADICAL_assert (!level); + CADICAL_assert (propagated == trail.size ()); level++; control.push_back (Level (lit, trail.size ())); probe_assign (lit, 0); @@ -340,8 +344,8 @@ void Internal::probe_assign_decision (int lit) { void Internal::probe_assign_unit (int lit) { require_mode (PROBE); - assert (!level); - assert (active (lit)); + CADICAL_assert (!level); + CADICAL_assert (active (lit)); probe_assign (lit, 0); } @@ -355,12 +359,12 @@ inline void Internal::probe_lrat_for_units (int lit) { if (level) return; // not decision level 0 LOG ("building chain for units"); - assert (lrat_chain.empty ()); - assert (probe_reason); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (probe_reason); for (auto &reason_lit : *probe_reason) { if (lit == reason_lit) continue; - assert (val (reason_lit)); + CADICAL_assert (val (reason_lit)); if (!val (reason_lit)) continue; const int signed_reason_lit = val (reason_lit) * reason_lit; @@ -398,8 +402,8 @@ inline void Internal::probe_propagate2 () { if (b < 0) conflict = w.clause; // but continue else { - assert (lrat_chain.empty ()); - assert (!probe_reason); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (!probe_reason); probe_reason = w.clause; probe_lrat_for_units (w.blit); probe_assign (w.blit, -lit); @@ -411,7 +415,7 @@ inline void Internal::probe_propagate2 () { bool Internal::probe_propagate () { require_mode (PROBE); - assert (!unsat); + CADICAL_assert (!unsat); START (propagate); int64_t before = propagated2 = propagated; int64_t &ticks = stats.ticks.probe; @@ -452,12 +456,12 @@ bool Internal::probe_propagate () { k++; if (v < 0) { k = lits + 2; - assert (w.clause->pos <= size); + CADICAL_assert (w.clause->pos <= size); while (k != middle && (v = val (r = *k)) < 0) k++; } w.clause->pos = k - lits; - assert (lits + 2 <= k), assert (k <= w.clause->end ()); + CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ()); if (v > 0) ws[j - 1].blit = r; else if (!v) { @@ -472,14 +476,14 @@ bool Internal::probe_propagate () { ticks++; if (level == 1) { lits[0] = other, lits[1] = lit; - assert (lrat_chain.empty ()); - assert (!probe_reason); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (!probe_reason); int dom = hyper_binary_resolve (w.clause); probe_assign (other, dom); } else { ticks++; - assert (lrat_chain.empty ()); - assert (!probe_reason); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (!probe_reason); probe_reason = w.clause; probe_lrat_for_units (other); probe_assign_unit (other); @@ -516,11 +520,11 @@ void Internal::failed_literal (int failed) { stats.failed++; stats.probefailed++; - assert (!unsat); - assert (conflict); - assert (level == 1); - assert (analyzed.empty ()); - assert (lrat_chain.empty ()); + CADICAL_assert (!unsat); + CADICAL_assert (conflict); + CADICAL_assert (level == 1); + CADICAL_assert (analyzed.empty ()); + CADICAL_assert (lrat_chain.empty ()); START (analyze); @@ -530,7 +534,7 @@ void Internal::failed_literal (int failed) { for (const auto &lit : *conflict) { const int other = -lit; if (!var (other).level) { - assert (val (other) > 0); + CADICAL_assert (val (other) > 0); continue; } uip = uip ? probe_dominator (uip, other) : other; @@ -540,7 +544,7 @@ void Internal::failed_literal (int failed) { clear_analyzed_literals (); LOG ("found probing UIP %d", uip); - assert (uip); + CADICAL_assert (uip); vector<int> work; @@ -548,14 +552,14 @@ void Internal::failed_literal (int failed) { while (parent != failed) { const int next = get_parent_reason_literal (parent); parent = next; - assert (parent); + CADICAL_assert (parent); work.push_back (parent); } backtrack (); conflict = 0; - assert (!val (uip)); + CADICAL_assert (!val (uip)); probe_assign_unit (-uip); lrat_chain.clear (); @@ -564,16 +568,16 @@ void Internal::failed_literal (int failed) { size_t j = 0; while (!unsat && j < work.size ()) { - // assert (!opts.probehbr); assertion fails ... + // CADICAL_assert (!opts.probehbr); CADICAL_assertion fails ... const int parent = work[j++]; const signed char tmp = val (parent); if (tmp > 0) { - assert (!opts.probehbr); // ... assertion should hold here + CADICAL_assert (!opts.probehbr); // ... CADICAL_assertion should hold here get_probehbr_lrat (parent, uip); LOG ("clashing failed parent %d", parent); learn_empty_clause (); } else if (tmp == 0) { - assert (!opts.probehbr); // ... and here + CADICAL_assert (!opts.probehbr); // ... and here LOG ("found unassigned failed parent %d", parent); get_probehbr_lrat (parent, uip); // this is computed during probe_assign_unit (-parent); // propagation and can include @@ -588,13 +592,13 @@ void Internal::failed_literal (int failed) { STOP (analyze); - assert (unsat || val (failed) < 0); + CADICAL_assert (unsat || val (failed) < 0); } /*------------------------------------------------------------------------*/ bool Internal::is_binary_clause (Clause *c, int &a, int &b) { - assert (!level); + CADICAL_assert (!level); if (c->garbage) return false; int first = 0, second = 0; @@ -632,7 +636,7 @@ struct probe_negated_noccs_rank { void Internal::generate_probes () { - assert (probes.empty ()); + CADICAL_assert (probes.empty ()); int64_t &ticks = stats.ticks.probe; @@ -697,7 +701,7 @@ void Internal::generate_probes () { void Internal::flush_probes () { - assert (!probes.empty ()); + CADICAL_assert (!probes.empty ()); int64_t &ticks = stats.ticks.probe; init_noccs (); @@ -724,14 +728,14 @@ void Internal::flush_probes () { continue; if (have_pos_bin_occs) lit = -lit; - assert (!noccs (lit)), assert (noccs (-lit) > 0); + CADICAL_assert (!noccs (lit)), CADICAL_assert (noccs (-lit) > 0); if (propfixed (lit) >= stats.all.fixed) continue; LOG ("keeping probe %d negated occs %" PRId64 "", lit, noccs (-lit)); *j++ = lit; } size_t remain = j - probes.begin (); -#ifndef QUIET +#ifndef CADICAL_QUIET size_t flushed = probes.size () - remain; #endif probes.resize (remain); @@ -808,7 +812,7 @@ bool Internal::probe () { "probing limit of %" PRId64 " propagations ", limit); int old_failed = stats.failed; -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t old_probed = stats.probed; #endif int64_t old_hbrs = stats.hbrs; @@ -823,7 +827,7 @@ bool Internal::probe () { for (auto idx : vars) propfixed (idx) = propfixed (-idx) = -1; - assert (unsat || propagated == trail.size ()); + CADICAL_assert (unsat || propagated == trail.size ()); propagated = propagated2 = trail.size (); int probe; @@ -853,7 +857,7 @@ bool Internal::probe () { } int failed = stats.failed - old_failed; -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t probed = stats.probed - old_probed; #endif int64_t hbrs = stats.hbrs - old_hbrs; @@ -924,7 +928,7 @@ void CaDiCaL::Internal::inprobe (bool update_limits) { stats.inprobingphases++; if (external_prop) { - assert (!level); + CADICAL_assert (!level); private_steps = true; } const int before = active (); @@ -950,7 +954,7 @@ void CaDiCaL::Internal::inprobe (bool update_limits) { } if (external_prop) { - assert (!level); + CADICAL_assert (!level); private_steps = false; } @@ -960,9 +964,9 @@ void CaDiCaL::Internal::inprobe (bool update_limits) { const int after = active (); const int after_extended = stats.variables_extension; const int diff_extended = after_extended - before_extended; - assert (diff_extended >= 0); + CADICAL_assert (diff_extended >= 0); const int removed = before - after + diff_extended; - assert (removed >= 0); + CADICAL_assert (removed >= 0); if (removed) { stats.inprobesuccess++; @@ -985,3 +989,5 @@ void CaDiCaL::Internal::inprobe (bool update_limits) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/profile.cpp b/src/sat/cadical/cadical_profile.cpp similarity index 87% rename from src/sat/cadical/profile.cpp rename to src/sat/cadical/cadical_profile.cpp index 68d80cbed..a95c97661 100644 --- a/src/sat/cadical/profile.cpp +++ b/src/sat/cadical/cadical_profile.cpp @@ -1,7 +1,11 @@ -#ifndef QUIET +#include "global.h" + +#ifndef CADICAL_QUIET #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { // Initialize all profile counters with constant name and profiling level. @@ -15,15 +19,15 @@ Profiles::Profiles (Internal *s) } void Internal::start_profiling (Profile &profile, double s) { - assert (profile.level <= opts.profile); - assert (!profile.active); + CADICAL_assert (profile.level <= opts.profile); + CADICAL_assert (!profile.active); profile.started = s; profile.active = true; } void Internal::stop_profiling (Profile &profile, double s) { - assert (profile.level <= opts.profile); - assert (profile.active); + CADICAL_assert (profile.level <= opts.profile); + CADICAL_assert (profile.active); profile.value += s - profile.started; profile.active = false; } @@ -34,7 +38,7 @@ double Internal::update_profiles () { do { \ Profile &profile = profiles.NAME; \ if (profile.active) { \ - assert (profile.level <= opts.profile); \ + CADICAL_assert (profile.level <= opts.profile); \ profile.value += now - profile.started; \ profile.started = now; \ } \ @@ -77,7 +81,7 @@ void Internal::print_profile () { PROFILES #undef PROFILE - assert (n <= size); + CADICAL_assert (n <= size); // Explicit bubble sort to avoid heap allocation since 'print_profile' // is also called during catching a signal after out of heap memory. @@ -104,4 +108,6 @@ void Internal::print_profile () { } // namespace CaDiCaL -#endif // ifndef QUIET +ABC_NAMESPACE_IMPL_END + +#endif // ifndef CADICAL_QUIET diff --git a/src/sat/cadical/proof.cpp b/src/sat/cadical/cadical_proof.cpp similarity index 92% rename from src/sat/cadical/proof.cpp rename to src/sat/cadical/cadical_proof.cpp index 36b629423..13c9a1a5d 100644 --- a/src/sat/cadical/proof.cpp +++ b/src/sat/cadical/cadical_proof.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { using namespace std; @@ -23,7 +27,7 @@ void Internal::resize_unit_clauses_idx () { void Internal::force_lrat () { if (lrat) return; - assert (proof); + CADICAL_assert (proof); lrat = true; } @@ -83,7 +87,7 @@ bool Internal::disconnect_proof_tracer (Tracer *tracer) { auto it = std::find (tracers.begin (), tracers.end (), tracer); if (it != tracers.end ()) { tracers.erase (it); - assert (proof); + CADICAL_assert (proof); proof->disconnect (tracer); return true; } @@ -94,7 +98,7 @@ bool Internal::disconnect_proof_tracer (StatTracer *tracer) { auto it = std::find (stat_tracers.begin (), stat_tracers.end (), tracer); if (it != stat_tracers.end ()) { stat_tracers.erase (it); - assert (proof); + CADICAL_assert (proof); proof->disconnect (tracer); return true; } @@ -105,7 +109,7 @@ bool Internal::disconnect_proof_tracer (FileTracer *tracer) { auto it = std::find (file_tracers.begin (), file_tracers.end (), tracer); if (it != file_tracers.end ()) { file_tracers.erase (it); - assert (proof); + CADICAL_assert (proof); proof->disconnect (tracer); return true; } @@ -230,7 +234,7 @@ void Proof::add_external_original_clause (int64_t id, bool r, const vector<int> &c, bool restore) { // literals of c are already external - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); for (auto const &lit : c) clause.push_back (lit); clause_id = id; @@ -241,7 +245,7 @@ void Proof::add_external_original_clause (int64_t id, bool r, void Proof::delete_external_original_clause (int64_t id, bool r, const vector<int> &c) { // literals of c are already external - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); for (auto const &lit : c) clause.push_back (lit); clause_id = id; @@ -252,8 +256,8 @@ void Proof::delete_external_original_clause (int64_t id, bool r, void Proof::add_derived_empty_clause (int64_t id, const vector<int64_t> &chain) { LOG ("PROOF adding empty clause"); - assert (clause.empty ()); - assert (proof_chain.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (proof_chain.empty ()); for (const auto &cid : chain) proof_chain.push_back (cid); clause_id = id; @@ -264,8 +268,8 @@ void Proof::add_derived_empty_clause (int64_t id, void Proof::add_derived_unit_clause (int64_t id, int internal_unit, const vector<int64_t> &chain) { LOG ("PROOF adding unit clause %d", internal_unit); - assert (proof_chain.empty ()); - assert (clause.empty ()); + CADICAL_assert (proof_chain.empty ()); + CADICAL_assert (clause.empty ()); add_literal (internal_unit); for (const auto &cid : chain) proof_chain.push_back (cid); @@ -278,8 +282,8 @@ void Proof::add_derived_unit_clause (int64_t id, int internal_unit, void Proof::add_derived_clause (Clause *c, const vector<int64_t> &chain) { LOG (c, "PROOF adding to proof derived"); - assert (clause.empty ()); - assert (proof_chain.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (proof_chain.empty ()); add_literals (c); for (const auto &cid : chain) proof_chain.push_back (cid); @@ -291,8 +295,8 @@ void Proof::add_derived_clause (Clause *c, const vector<int64_t> &chain) { void Proof::add_derived_clause (int64_t id, bool r, const vector<int> &c, const vector<int64_t> &chain) { LOG (c, "PROOF adding derived clause"); - assert (clause.empty ()); - assert (proof_chain.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (proof_chain.empty ()); for (const auto &lit : c) add_literal (lit); for (const auto &cid : chain) @@ -305,8 +309,8 @@ void Proof::add_derived_clause (int64_t id, bool r, const vector<int> &c, void Proof::add_assumption_clause (int64_t id, const vector<int> &c, const vector<int64_t> &chain) { // literals of c are already external - assert (clause.empty ()); - assert (proof_chain.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (proof_chain.empty ()); for (const auto &lit : c) clause.push_back (lit); for (const auto &cid : chain) @@ -317,16 +321,16 @@ void Proof::add_assumption_clause (int64_t id, const vector<int> &c, void Proof::add_assumption (int a) { // a is already external - assert (clause.empty ()); - assert (proof_chain.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (proof_chain.empty ()); clause.push_back (a); add_assumption (); } void Proof::add_constraint (const vector<int> &c) { // literals of c are already external - assert (clause.empty ()); - assert (proof_chain.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (proof_chain.empty ()); for (const auto &lit : c) clause.push_back (lit); add_constraint (); @@ -334,8 +338,8 @@ void Proof::add_constraint (const vector<int> &c) { void Proof::add_assumption_clause (int64_t id, int lit, const vector<int64_t> &chain) { - assert (clause.empty ()); - assert (proof_chain.empty ()); + CADICAL_assert (clause.empty ()); + CADICAL_assert (proof_chain.empty ()); clause.push_back (lit); for (const auto &cid : chain) proof_chain.push_back (cid); @@ -354,7 +358,7 @@ void Proof::delete_clause (Clause *c) { void Proof::delete_clause (int64_t id, bool r, const vector<int> &c) { LOG (c, "PROOF deleting from proof"); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); add_literals (c); clause_id = id; redundant = r; @@ -363,7 +367,7 @@ void Proof::delete_clause (int64_t id, bool r, const vector<int> &c) { void Proof::weaken_minus (Clause *c) { LOG (c, "PROOF weaken minus of"); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); add_literals (c); clause_id = c->id; weaken_minus (); @@ -371,7 +375,7 @@ void Proof::weaken_minus (Clause *c) { void Proof::weaken_minus (int64_t id, const vector<int> &c) { LOG (c, "PROOF deleting from proof"); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); add_literals (c); clause_id = id; weaken_minus (); @@ -389,7 +393,7 @@ void Proof::weaken_plus (int64_t id, const vector<int> &c) { void Proof::delete_unit_clause (int64_t id, const int lit) { LOG ("PROOF deleting unit from proof %d", lit); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); add_literal (lit); clause_id = id; redundant = false; @@ -398,7 +402,7 @@ void Proof::delete_unit_clause (int64_t id, const int lit) { void Proof::finalize_clause (Clause *c) { LOG (c, "PROOF finalizing clause"); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); add_literals (c); clause_id = c->id; finalize_clause (); @@ -406,7 +410,7 @@ void Proof::finalize_clause (Clause *c) { void Proof::finalize_clause (int64_t id, const vector<int> &c) { LOG (c, "PROOF finalizing clause"); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); for (const auto &lit : c) add_literal (lit); clause_id = id; @@ -415,7 +419,7 @@ void Proof::finalize_clause (int64_t id, const vector<int> &c) { void Proof::finalize_unit (int64_t id, int lit) { LOG ("PROOF finalizing clause %d", lit); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); add_literal (lit); clause_id = id; finalize_clause (); @@ -423,7 +427,7 @@ void Proof::finalize_unit (int64_t id, int lit) { void Proof::finalize_external_unit (int64_t id, int lit) { LOG ("PROOF finalizing clause %d", lit); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); clause.push_back (lit); clause_id = id; finalize_clause (); @@ -437,7 +441,7 @@ void Proof::finalize_external_unit (int64_t id, int lit) { void Proof::flush_clause (Clause *c) { LOG (c, "PROOF flushing falsified literals in"); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); const bool antecedents = (internal->lrat || internal->frat); for (int i = 0; i < c->size; i++) { int internal_lit = c->literals[i]; @@ -468,7 +472,7 @@ void Proof::flush_clause (Clause *c) { void Proof::strengthen_clause (Clause *c, int remove, const vector<int64_t> &chain) { LOG (c, "PROOF strengthen by removing %d in", remove); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); for (int i = 0; i < c->size; i++) { int internal_lit = c->literals[i]; if (internal_lit == remove) @@ -488,7 +492,7 @@ void Proof::strengthen_clause (Clause *c, int remove, void Proof::otfs_strengthen_clause (Clause *c, const std::vector<int> &old, const vector<int64_t> &chain) { LOG (c, "PROOF otfs strengthen"); - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); for (int i = 0; i < c->size; i++) { int internal_lit = c->literals[i]; add_literal (internal_lit); @@ -512,7 +516,7 @@ void Proof::strengthen (int64_t id) { void Proof::add_original_clause (bool restore) { LOG (clause, "PROOF adding original external clause"); - assert (clause_id); + CADICAL_assert (clause_id); for (auto &tracer : tracers) { tracer->add_original_clause (clause_id, false, clause, restore); @@ -524,7 +528,7 @@ void Proof::add_original_clause (bool restore) { void Proof::add_derived_clause () { LOG (clause, "PROOF adding derived external clause (redundant: %d)", redundant); - assert (clause_id); + CADICAL_assert (clause_id); for (auto &tracer : tracers) { tracer->add_derived_clause (clause_id, redundant, clause, proof_chain); } @@ -544,7 +548,7 @@ void Proof::delete_clause () { void Proof::demote_clause () { LOG (clause, "PROOF demoting external clause"); - assert (!redundant); + CADICAL_assert (!redundant); for (auto &tracer : tracers) { tracer->demote_clause (clause_id, clause); } @@ -589,7 +593,7 @@ void Proof::add_assumption_clause () { void Proof::add_assumption () { LOG (clause, "PROOF adding assumption"); - assert (clause.size () == 1); + CADICAL_assert (clause.size () == 1); for (auto &tracer : tracers) { tracer->add_assumption (clause.back ()); } @@ -655,3 +659,5 @@ void Proof::conclude_unknown (const vector<int> &trail) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/propagate.cpp b/src/sat/cadical/cadical_propagate.cpp similarity index 90% rename from src/sat/cadical/propagate.cpp rename to src/sat/cadical/cadical_propagate.cpp index de3dfbb45..a4f21ee6a 100644 --- a/src/sat/cadical/propagate.cpp +++ b/src/sat/cadical/cadical_propagate.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -37,7 +41,7 @@ static Clause *decision_reason = &decision_reason_clause; inline int Internal::assignment_level (int lit, Clause *reason) { - assert (opts.chrono || external_prop); + CADICAL_assert (opts.chrono || external_prop); if (!reason || reason == external_reason) return level; @@ -46,7 +50,7 @@ inline int Internal::assignment_level (int lit, Clause *reason) { for (const auto &other : *reason) { if (other == lit) continue; - assert (val (other)); + CADICAL_assert (val (other)); int tmp = var (other).level; if (tmp > res) res = tmp; @@ -65,11 +69,11 @@ void Internal::build_chain_for_units (int lit, Clause *reason, return; else if (!opts.chrono && level && !forced) return; // not decision level 0 - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); for (auto &reason_lit : *reason) { if (lit == reason_lit) continue; - assert (val (reason_lit)); + CADICAL_assert (val (reason_lit)); if (!val (reason_lit)) continue; const int signed_reason_lit = val (reason_lit) * reason_lit; @@ -85,12 +89,12 @@ void Internal::build_chain_for_units (int lit, Clause *reason, void Internal::build_chain_for_empty () { if (!lrat || !lrat_chain.empty ()) return; - assert (!level); - assert (lrat_chain.empty ()); - assert (conflict); + CADICAL_assert (!level); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (conflict); LOG (conflict, "lrat for global empty clause with conflict"); for (auto &lit : *conflict) { - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); int64_t id = unit_id (-lit); lrat_chain.push_back (id); } @@ -106,12 +110,12 @@ inline void Internal::search_assign (int lit, Clause *reason) { const int idx = vidx (lit); const bool from_external = reason == external_reason; - assert (!val (idx)); - assert (!flags (idx).eliminated () || reason == decision_reason || + CADICAL_assert (!val (idx)); + CADICAL_assert (!flags (idx).eliminated () || reason == decision_reason || reason == external_reason); Var &v = var (idx); int lit_level; - assert (!lrat || level || reason == external_reason || + CADICAL_assert (!lrat || level || reason == external_reason || reason == decision_reason || !lrat_chain.empty ()); // The following cases are explained in the two comments above before // 'decision_reason' and 'assignment_level'. @@ -137,16 +141,16 @@ inline void Internal::search_assign (int lit, Clause *reason) { v.level = lit_level; v.trail = trail.size (); v.reason = reason; - assert ((int) num_assigned < max_var); - assert (num_assigned == trail.size ()); + CADICAL_assert ((int) num_assigned < max_var); + CADICAL_assert (num_assigned == trail.size ()); num_assigned++; if (!lit_level && !from_external) learn_unit_clause (lit); // increases 'stats.fixed' - assert (lit_level || !from_external); + CADICAL_assert (lit_level || !from_external); const signed char tmp = sign (lit); set_val (idx, tmp); - assert (val (lit) > 0); // Just a bit paranoid but useful. - assert (val (-lit) < 0); // Ditto. + CADICAL_assert (val (lit) > 0); // Just a bit paranoid but useful. + CADICAL_assert (val (-lit) < 0); // Ditto. if (!searching_lucky_phases) phases.saved[idx] = tmp; // phase saving during search trail.push_back (lit); @@ -161,7 +165,9 @@ inline void Internal::search_assign (int lit, Clause *reason) { const Watches &ws = watches (-lit); if (!ws.empty ()) { const Watch &w = ws[0]; +#ifndef WIN32 __builtin_prefetch (&w, 0, 1); +#endif } } lrat_chain.clear (); @@ -176,7 +182,7 @@ inline void Internal::search_assign (int lit, Clause *reason) { // which is called directly in 'propagate' below and thus is inlined. void Internal::assign_unit (int lit) { - assert (!level); + CADICAL_assert (!level); search_assign (lit, 0); } @@ -185,7 +191,7 @@ void Internal::assign_unit (int lit) { void Internal::search_assume_decision (int lit) { require_mode (SEARCH); - assert (propagated == trail.size ()); + CADICAL_assert (propagated == trail.size ()); new_trail_level (lit); notify_decision (); LOG ("search decide %d", lit); @@ -227,7 +233,7 @@ bool Internal::propagate () { if (level) require_mode (SEARCH); - assert (!unsat); + CADICAL_assert (!unsat); LOG ("starting propagate"); START (propagate); @@ -259,7 +265,7 @@ bool Internal::propagate () { if (w.binary ()) { - // assert (w.clause->redundant || !w.clause->garbage); + // CADICAL_assert (w.clause->redundant || !w.clause->garbage); // In principle we can ignore garbage binary clauses too, but that // would require to dereference the clause pointer all the time with @@ -296,7 +302,7 @@ bool Internal::propagate () { } } else { - assert (w.clause->size > 2); + CADICAL_assert (w.clause->size > 2); if (conflict) break; // Stop if there was a binary conflict already. @@ -355,14 +361,14 @@ bool Internal::propagate () { if (v < 0) { // need second search starting at the head? k = lits + 2; - assert (w.clause->pos <= size); + CADICAL_assert (w.clause->pos <= size); while (k != middle && (v = val (r = *k)) < 0) k++; } w.clause->pos = k - lits; // always save position - assert (lits + 2 <= k), assert (k <= w.clause->end ()); + CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ()); if (v > 0) { @@ -388,7 +394,7 @@ bool Internal::propagate () { } else if (!u) { - assert (v < 0); + 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. @@ -414,7 +420,7 @@ bool Internal::propagate () { // another literal in the clause at that higher assignment // level and watch that instead of 'lit'. - assert (size > 2); + CADICAL_assert (size > 2); int pos, s = 0; @@ -422,8 +428,8 @@ bool Internal::propagate () { if (var (s = lits[pos]).level == other_level) break; - assert (s); - assert (pos < size); + CADICAL_assert (s); + CADICAL_assert (pos < size); LOG (w.clause, "unwatch %d in", lit); lits[pos] = lit; @@ -436,8 +442,8 @@ bool Internal::propagate () { } } else { - assert (u < 0); - assert (v < 0); + CADICAL_assert (u < 0); + CADICAL_assert (v < 0); // The other watch is assigned false ('u < 0') and all other // literals as well (still 'v < 0'), thus we found a conflict. @@ -495,8 +501,8 @@ bool Internal::propagate () { void Internal::propergate () { - assert (!conflict); - assert (propagated == trail.size ()); + CADICAL_assert (!conflict); + CADICAL_assert (propagated == trail.size ()); while (propergated != trail.size ()) { @@ -513,7 +519,7 @@ void Internal::propergate () { const Watch w = *j++ = *i++; if (w.binary ()) { - assert (val (w.blit) > 0); + CADICAL_assert (val (w.blit) > 0); continue; } if (w.clause->garbage) { @@ -529,7 +535,7 @@ void Internal::propergate () { // TODO: check if u == 0 can happen. if (u > 0) continue; - assert (u < 0); + CADICAL_assert (u < 0); const int size = w.clause->size; const literal_iterator middle = lits + w.clause->pos; @@ -544,15 +550,15 @@ void Internal::propergate () { if (v < 0) { k = lits + 2; - assert (w.clause->pos <= size); + CADICAL_assert (w.clause->pos <= size); while (k != middle && (v = val (r = *k)) < 0) k++; } - assert (lits + 2 <= k), assert (k <= w.clause->end ()); + CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ()); w.clause->pos = k - lits; - assert (v > 0); + CADICAL_assert (v > 0); LOG (w.clause, "unwatch %d in", lit); @@ -576,3 +582,5 @@ void Internal::propergate () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/queue.cpp b/src/sat/cadical/cadical_queue.cpp similarity index 87% rename from src/sat/cadical/queue.cpp rename to src/sat/cadical/cadical_queue.cpp index cd2b19491..c8b5b8030 100644 --- a/src/sat/cadical/queue.cpp +++ b/src/sat/cadical/cadical_queue.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { // Slightly different than 'bump_variable' since the variable is not @@ -10,15 +14,15 @@ inline void Internal::init_enqueue (int idx) { if (opts.reverse) { l.prev = 0; if (queue.first) { - assert (!links[queue.first].prev); + CADICAL_assert (!links[queue.first].prev); links[queue.first].prev = idx; btab[idx] = btab[queue.first] - 1; } else { - assert (!queue.last); + CADICAL_assert (!queue.last); queue.last = idx; btab[idx] = 0; } - assert (btab[idx] <= stats.bumped); + CADICAL_assert (btab[idx] <= stats.bumped); l.next = queue.first; queue.first = idx; if (!queue.unassigned) @@ -26,10 +30,10 @@ inline void Internal::init_enqueue (int idx) { } else { l.next = 0; if (queue.last) { - assert (!links[queue.last].next); + CADICAL_assert (!links[queue.last].next); links[queue.last].next = idx; } else { - assert (!queue.first); + CADICAL_assert (!queue.first); queue.first = idx; } btab[idx] = ++stats.bumped; @@ -47,7 +51,7 @@ inline void Internal::init_enqueue (int idx) { void Internal::init_queue (int old_max_var, int new_max_var) { LOG ("initializing VMTF queue from %d to %d", old_max_var + 1, new_max_var); - assert (old_max_var < new_max_var); + CADICAL_assert (old_max_var < new_max_var); // New variables can be created that can invoke enlarge anytime (eg via // calls during ipasir-up call-backs), thus assuming (!level) is not // correct @@ -88,3 +92,5 @@ void Internal::shuffle_queue () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/random.cpp b/src/sat/cadical/cadical_random.cpp similarity index 94% rename from src/sat/cadical/random.cpp rename to src/sat/cadical/cadical_random.cpp index b5f02e71e..85d022e8f 100644 --- a/src/sat/cadical/random.cpp +++ b/src/sat/cadical/cadical_random.cpp @@ -1,3 +1,5 @@ +#include "global.h" + #include "internal.hpp" /*------------------------------------------------------------------------*/ @@ -46,6 +48,8 @@ // does not have any effect. TODO: add a similar machine identity hashing // function for other operating systems (Windows and macOS). +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { static uint64_t hash_machine_identifier () { @@ -55,7 +59,7 @@ static uint64_t hash_machine_identifier () { char buffer[128]; memset (buffer, 0, sizeof buffer); size_t bytes = fread (buffer, 1, sizeof buffer - 1, file); - assert (bytes); + CADICAL_assert (bytes); fclose (file); if (bytes && bytes < sizeof buffer) { buffer[bytes] = 0; @@ -68,6 +72,8 @@ static uint64_t hash_machine_identifier () { } // namespace CaDiCaL +ABC_NAMESPACE_IMPL_END + /*------------------------------------------------------------------------*/ // On our Linux cluster where we used an NFS mounted root disk the @@ -78,7 +84,7 @@ static uint64_t hash_machine_identifier () { // work. As an additional measure to increase the possibility to get // different seeds we are now also using network addresses (explicitly). -#ifndef __WIN32 +#ifndef WIN32 extern "C" { #include <ifaddrs.h> @@ -91,6 +97,8 @@ extern "C" { #endif +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { static uint64_t hash_network_addresses () { @@ -102,7 +110,7 @@ static uint64_t hash_network_addresses () { // you really need to run 'mobical' on a Windows cluster where each node // has identical IP addresses. -#ifndef __WIN32 +#ifndef WIN32 struct ifaddrs *addrs; if (!getifaddrs (&addrs)) { for (struct ifaddrs *addr = addrs; addr; addr = addr->ifa_next) { @@ -135,6 +143,8 @@ static uint64_t hash_network_addresses () { } // namespace CaDiCaL +ABC_NAMESPACE_IMPL_END + /*------------------------------------------------------------------------*/ // Hash the current wall-clock time in seconds. @@ -143,6 +153,8 @@ extern "C" { #include <time.h> } +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { static uint64_t hash_time () { @@ -153,15 +165,23 @@ static uint64_t hash_time () { } // namespace CaDiCaL +ABC_NAMESPACE_IMPL_END + /*------------------------------------------------------------------------*/ // Hash the process identified. extern "C" { #include <sys/types.h> +#ifdef WIN32 +#include <process.h> +#else #include <unistd.h> +#endif } +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { static uint64_t hash_process () { @@ -172,12 +192,16 @@ static uint64_t hash_process () { } // namespace CaDiCaL +ABC_NAMESPACE_IMPL_END + /*------------------------------------------------------------------------*/ // Hash the current number of clock cycles. #include <ctime> +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { static uint64_t hash_clock_cycles () { @@ -205,3 +229,5 @@ Random::Random () : state (1) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/reap.cpp b/src/sat/cadical/cadical_reap.cpp similarity index 70% rename from src/sat/cadical/reap.cpp rename to src/sat/cadical/cadical_reap.cpp index 8c383cb66..4625c44db 100644 --- a/src/sat/cadical/reap.cpp +++ b/src/sat/cadical/cadical_reap.cpp @@ -1,15 +1,28 @@ +#include "global.h" + #include "reap.hpp" #include <cassert> #include <climits> #include <cstring> +#ifdef _MSC_VER +#include <intrin.h> +static inline int __builtin_clz(unsigned x) { + unsigned long r; + _BitScanReverse(&r, x); + return (int)(r ^ 31); +} +#endif + +ABC_NAMESPACE_IMPL_START + void Reap::init () { for (auto &bucket : buckets) bucket = {0}; - assert (!num_elements); - assert (!last_deleted); + CADICAL_assert (!num_elements); + CADICAL_assert (!last_deleted); min_bucket = 32; - assert (!max_bucket); + CADICAL_assert (!max_bucket); } void Reap::release () { @@ -31,7 +44,7 @@ static inline unsigned leading_zeroes_of_unsigned (unsigned x) { } void Reap::push (unsigned e) { - assert (last_deleted <= e); + CADICAL_assert (last_deleted <= e); const unsigned diff = e ^ last_deleted; const unsigned bucket = 32 - leading_zeroes_of_unsigned (diff); buckets[bucket].push_back (e); @@ -39,16 +52,16 @@ void Reap::push (unsigned e) { min_bucket = bucket; if (max_bucket < bucket) max_bucket = bucket; - assert (num_elements != UINT_MAX); + CADICAL_assert (num_elements != UINT_MAX); num_elements++; } unsigned Reap::pop () { - assert (num_elements > 0); + CADICAL_assert (num_elements > 0); unsigned i = min_bucket; for (;;) { - assert (i < 33); - assert (i <= max_bucket); + CADICAL_assert (i < 33); + CADICAL_assert (i <= max_bucket); std::vector<unsigned> &s = buckets[i]; if (s.empty ()) { min_bucket = ++i; @@ -60,7 +73,7 @@ unsigned Reap::pop () { const auto begin = std::begin (s); const auto end = std::end (s); auto q = std::begin (s); - assert (begin < end); + CADICAL_assert (begin < end); for (auto p = begin; p != end; ++p) { const unsigned tmp = *p; if (tmp >= res) @@ -74,9 +87,9 @@ unsigned Reap::pop () { continue; const unsigned other = *p; const unsigned diff = other ^ res; - assert (sizeof (unsigned) == 4); + CADICAL_assert (sizeof (unsigned) == 4); const unsigned j = 32 - leading_zeroes_of_unsigned (diff); - assert (j < i); + CADICAL_assert (j < i); buckets[j].push_back (other); if (min_bucket > j) min_bucket = j; @@ -85,31 +98,31 @@ unsigned Reap::pop () { s.clear (); if (i && max_bucket == i) { -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG for (unsigned j = i + 1; j < 33; j++) - assert (buckets[j].empty ()); + CADICAL_assert (buckets[j].empty ()); #endif if (s.empty ()) max_bucket = i - 1; } } else { res = last_deleted; - assert (!buckets[0].empty ()); - assert (buckets[0].at (0) == res); + CADICAL_assert (!buckets[0].empty ()); + CADICAL_assert (buckets[0].at (0) == res); buckets[0].pop_back (); } if (min_bucket == i) { -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG for (unsigned j = 0; j < i; j++) - assert (buckets[j].empty ()); + CADICAL_assert (buckets[j].empty ()); #endif if (s.empty ()) min_bucket = std::min ((int) (i + 1), 32); } --num_elements; - assert (last_deleted <= res); + CADICAL_assert (last_deleted <= res); last_deleted = res; return res; @@ -117,7 +130,7 @@ unsigned Reap::pop () { } void Reap::clear () { - assert (max_bucket <= 32); + CADICAL_assert (max_bucket <= 32); for (unsigned i = 0; i < 33; i++) buckets[i].clear (); num_elements = 0; @@ -125,3 +138,5 @@ void Reap::clear () { min_bucket = 32; max_bucket = 0; } + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/reduce.cpp b/src/sat/cadical/cadical_reduce.cpp similarity index 96% rename from src/sat/cadical/reduce.cpp rename to src/sat/cadical/cadical_reduce.cpp index 632db3673..cc6f35228 100644 --- a/src/sat/cadical/reduce.cpp +++ b/src/sat/cadical/cadical_reduce.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -114,7 +118,7 @@ void Internal::mark_useless_redundant_clauses_as_garbage () { if (c->glue <= tier2limit && used >= max_used - 1) continue; if (c->hyper) { // Hyper binary and ternary resolvents - assert (c->size <= 3); // are only kept for one reduce round + CADICAL_assert (c->size <= 3); // are only kept for one reduce round if (!used) mark_garbage (c); // unless continue; // used recently. @@ -167,7 +171,7 @@ void Internal::mark_useless_redundant_clauses_as_garbage () { // If chronological backtracking produces out-of-order assigned units, then // it is necessary to completely propagate them at the root level in order // to derive all implied units. Otherwise the blocking literals in -// 'flush_watches' are messed up and assertion 'FW1' fails. +// 'flush_watches' are messed up and CADICAL_assertion 'FW1' fails. bool Internal::propagate_out_of_order_units () { if (!level) @@ -175,7 +179,7 @@ bool Internal::propagate_out_of_order_units () { int oou = 0; for (size_t i = control[1].trail; !oou && i < trail.size (); i++) { const int lit = trail[i]; - assert (val (lit) > 0); + CADICAL_assert (val (lit) > 0); if (var (lit).level) continue; LOG ("found out-of-order assigned unit %d", oou); @@ -183,7 +187,7 @@ bool Internal::propagate_out_of_order_units () { } if (!oou) return true; - assert (opts.chrono || external_prop); + CADICAL_assert (opts.chrono || external_prop); backtrack (0); if (propagate ()) return true; @@ -276,3 +280,5 @@ DONE: } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/rephase.cpp b/src/sat/cadical/cadical_rephase.cpp similarity index 98% rename from src/sat/cadical/rephase.cpp rename to src/sat/cadical/cadical_rephase.cpp index 74ebb988e..3b2e62526 100644 --- a/src/sat/cadical/rephase.cpp +++ b/src/sat/cadical/cadical_rephase.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -281,7 +285,7 @@ void Internal::rephase () { break; } } else { - assert (!stable && opts.walk && opts.walknonstable); + CADICAL_assert (!stable && opts.walk && opts.walknonstable); // flipping,(random,best,walk,flipping,best,walk)^\omega if (!count) type = rephase_flipping (); @@ -310,7 +314,7 @@ void Internal::rephase () { break; } } - assert (type); + CADICAL_assert (type); int64_t delta = opts.rephaseint * (stats.rephased.total + 1); lim.rephase = stats.conflicts + delta; @@ -334,3 +338,5 @@ void Internal::rephase () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/report.cpp b/src/sat/cadical/cadical_report.cpp similarity index 97% rename from src/sat/cadical/report.cpp rename to src/sat/cadical/cadical_report.cpp index cda252edb..18271d17e 100644 --- a/src/sat/cadical/report.cpp +++ b/src/sat/cadical/cadical_report.cpp @@ -1,8 +1,12 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { -#ifndef QUIET +#ifndef CADICAL_QUIET /*------------------------------------------------------------------------*/ @@ -167,7 +171,7 @@ void Internal::report (char type, int verbose) { if (opts.quiet || (verbose > opts.verbose)) return; if (!reported) { - assert (!lim.report); + CADICAL_assert (!lim.report); reported = true; MSG ("%stime measured in %s time %s%s", tout.magenta_code (), internal->opts.realtime ? "real" : "process", @@ -177,7 +181,7 @@ void Internal::report (char type, int verbose) { Report reports[num_reports]; int n = 0; #define REPORT(HEAD, PREC, MIN, EXPR) \ - assert (n < num_reports); \ + CADICAL_assert (n < num_reports); \ reports[n++] = Report (HEAD, PREC, MIN, (double) (EXPR)); REPORTS #undef REPORT @@ -287,10 +291,12 @@ void Internal::report (char type, int verbose) { fflush (stdout); } -#else // ifndef QUIET +#else // ifndef CADICAL_QUIET void Internal::report (char, int) {} #endif } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/resources.cpp b/src/sat/cadical/cadical_resources.cpp similarity index 97% rename from src/sat/cadical/resources.cpp rename to src/sat/cadical/cadical_resources.cpp index 712a8ec67..9a2b421e5 100644 --- a/src/sat/cadical/resources.cpp +++ b/src/sat/cadical/cadical_resources.cpp @@ -1,3 +1,5 @@ +#include "global.h" + #include "internal.hpp" /*------------------------------------------------------------------------*/ @@ -9,7 +11,7 @@ extern "C" { -#ifdef __WIN32 +#ifdef WIN32 #ifndef __WIN32_WINNT #define __WIN32_WINNT 0x0600 @@ -37,11 +39,13 @@ extern "C" { #include <string.h> } +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ -#ifdef __WIN32 +#ifdef WIN32 double absolute_real_time () { FILETIME f; @@ -104,7 +108,7 @@ double Internal::process_time () const { /*------------------------------------------------------------------------*/ -#ifdef __WIN32 +#ifdef WIN32 uint64_t current_resident_set_size () { PROCESS_MEMORY_COUNTERS pmc; @@ -158,3 +162,5 @@ uint64_t current_resident_set_size () { /*------------------------------------------------------------------------*/ } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/restart.cpp b/src/sat/cadical/cadical_restart.cpp similarity index 97% rename from src/sat/cadical/restart.cpp rename to src/sat/cadical/cadical_restart.cpp index 799df94e9..1c6dc44c8 100644 --- a/src/sat/cadical/restart.cpp +++ b/src/sat/cadical/cadical_restart.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { // As observed by Chanseok Oh and implemented in MapleSAT solvers too, @@ -21,7 +25,7 @@ bool Internal::stabilizing () { if (stable && opts.stabilizeonly) return true; if (!inc.stabilize) { - assert (!stable); + CADICAL_assert (!stable); if (stats.conflicts <= lim.stabilize) return false; } else if (stats.ticks.search[stable] <= lim.stabilize) @@ -112,7 +116,7 @@ int Internal::reuse_trail () { if (!opts.restartreusetrail) return trivial_decisions; int next_decision = next_decision_variable (); - assert (1 <= next_decision); + CADICAL_assert (1 <= next_decision); int res = trivial_decisions; if (use_scores ()) { while (res < level) { @@ -157,3 +161,5 @@ void Internal::restart () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/restore.cpp b/src/sat/cadical/cadical_restore.cpp similarity index 90% rename from src/sat/cadical/restore.cpp rename to src/sat/cadical/cadical_restore.cpp index cd1c9721d..f9551fb5e 100644 --- a/src/sat/cadical/restore.cpp +++ b/src/sat/cadical/cadical_restore.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -51,14 +55,14 @@ void External::restore_clause (const vector<int>::const_iterator &begin, const vector<int>::const_iterator &end, const int64_t id) { LOG (begin, end, "restoring external clause[%" PRId64 "]", id); - assert (eclause.empty ()); - assert (id); + CADICAL_assert (eclause.empty ()); + CADICAL_assert (id); for (auto p = begin; p != end; p++) { eclause.push_back (*p); if (internal->proof && internal->lrat) { const auto &elit = *p; unsigned eidx = (elit > 0) + 2u * (unsigned) abs (elit); - assert ((size_t) eidx < ext_units.size ()); + CADICAL_assert ((size_t) eidx < ext_units.size ()); const int64_t id = ext_units[eidx]; bool added = ext_flags[abs (elit)]; if (id && !added) { @@ -83,7 +87,7 @@ void External::restore_clause (const vector<int>::const_iterator &begin, void External::restore_clauses () { - assert (internal->opts.restoreall == 2 || !tainted.empty ()); + CADICAL_assert (internal->opts.restoreall == 2 || !tainted.empty ()); START (restore); internal->stats.restorations++; @@ -97,7 +101,7 @@ void External::restore_clauses () { PHASE ("restore", internal->stats.restorations, "forced to restore all clauses"); -#ifndef QUIET +#ifndef CADICAL_QUIET { unsigned numtainted = 0; for (const auto b : tainted) @@ -120,7 +124,7 @@ void External::restore_clauses () { clauses.weakened++; - assert (!*p); + CADICAL_assert (!*p); const auto saved = q; // Save old start. *q++ = *p++; // Copy zero '0'. @@ -129,7 +133,7 @@ void External::restore_clauses () { int tlit = 0; // Negation tainted. int elit; // - assert (p != end_of_extension); + CADICAL_assert (p != end_of_extension); // while ((elit = *q++ = *p++)) { @@ -138,7 +142,7 @@ void External::restore_clauses () { LOG ("negation of witness literal %d tainted", tlit); } - assert (p != end_of_extension); + CADICAL_assert (p != end_of_extension); } // now copy the id of the clause @@ -146,8 +150,8 @@ void External::restore_clauses () { LOG ("id is %" PRId64, id); *q++ = *p++; *q++ = *p++; - assert (id); - assert (!*p); + CADICAL_assert (id); + CADICAL_assert (!*p); *q++ = *p++; // Now find 'end_of_clause' (clause starts at 'p') and at the same time @@ -160,7 +164,7 @@ void External::restore_clauses () { satisfied = elit; end_of_clause++; } - assert (id); + CADICAL_assert (id); // Do not apply our 'FLUSH' rule to remove satisfied (implied) clauses // if the corresponding option is set simply by resetting 'satisfied'. @@ -199,7 +203,7 @@ void External::restore_clauses () { extension.resize (q - extension.begin ()); shrink_vector (extension); -#ifndef QUIET +#ifndef CADICAL_QUIET if (clauses.satisfied) PHASE ("restore", internal->stats.restorations, "removed %" PRId64 " satisfied %.0f%% of %" PRId64 @@ -244,20 +248,20 @@ void External::restore_clauses () { p = extension.end (); while (p != begin_of_extension) { while (*--p) - assert (p != begin_of_extension); + CADICAL_assert (p != begin_of_extension); int elit; - assert (p != begin_of_extension); + CADICAL_assert (p != begin_of_extension); --p; - assert (p != begin_of_extension); - assert (*p || *(p - 1)); + CADICAL_assert (p != begin_of_extension); + CADICAL_assert (*p || *(p - 1)); --p; - assert (p != begin_of_extension); - assert (!*p); + CADICAL_assert (p != begin_of_extension); + CADICAL_assert (!*p); --p; - assert (p != begin_of_extension); + CADICAL_assert (p != begin_of_extension); while ((elit = *--p)) { mark (witness, elit); - assert (p != begin_of_extension); + CADICAL_assert (p != begin_of_extension); } } @@ -265,3 +269,5 @@ void External::restore_clauses () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/score.cpp b/src/sat/cadical/cadical_score.cpp similarity index 92% rename from src/sat/cadical/score.cpp rename to src/sat/cadical/cadical_score.cpp index 578d553a8..420357929 100644 --- a/src/sat/cadical/score.cpp +++ b/src/sat/cadical/cadical_score.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { // This initializes variables on the binary 'scores' heap also with @@ -20,7 +24,7 @@ void Internal::shuffle_scores () { return; if (!opts.shufflescores) return; - assert (!level); + CADICAL_assert (!level); stats.shuffled++; LOG ("shuffling scores"); vector<int> shuffle; @@ -49,3 +53,5 @@ void Internal::shuffle_scores () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/shrink.cpp b/src/sat/cadical/cadical_shrink.cpp similarity index 82% rename from src/sat/cadical/shrink.cpp rename to src/sat/cadical/cadical_shrink.cpp index e61935117..201c220e7 100644 --- a/src/sat/cadical/shrink.cpp +++ b/src/sat/cadical/cadical_shrink.cpp @@ -1,6 +1,10 @@ +#include "global.h" + #include "internal.hpp" #include "reap.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { void Internal::reset_shrinkable () { @@ -10,7 +14,7 @@ void Internal::reset_shrinkable () { for (const auto &lit : shrinkable) { LOG ("resetting lit %i", lit); Flags &f = flags (lit); - assert (f.shrinkable); + CADICAL_assert (f.shrinkable); f.shrinkable = false; #ifdef LOGGING ++reset; @@ -24,14 +28,14 @@ void Internal::mark_shrinkable_as_removable ( #ifdef LOGGING size_t marked = 0, reset = 0; #endif -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG unsigned kept = 0, minireset = 0; for (; minimized_start < minimized.size (); ++minimized_start) { const int lit = minimized[minimized_start]; Flags &f = flags (lit); const Var &v = var (lit); if (v.level == blevel) { - assert (!f.poison); + CADICAL_assert (!f.poison); ++minireset; } else ++kept; @@ -45,8 +49,8 @@ void Internal::mark_shrinkable_as_removable ( for (const int lit : shrinkable) { Flags &f = flags (lit); - assert (f.shrinkable); - assert (!f.poison); + CADICAL_assert (f.shrinkable); + CADICAL_assert (!f.poison); f.shrinkable = false; #ifdef LOGGING ++reset; @@ -65,11 +69,11 @@ void Internal::mark_shrinkable_as_removable ( int inline Internal::shrink_literal (int lit, int blevel, unsigned max_trail) { - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); Flags &f = flags (lit); Var &v = var (lit); - assert (v.level <= blevel); + CADICAL_assert (v.level <= blevel); if (!v.level) { LOG ("skipping root level assigned %d", (lit)); @@ -77,14 +81,14 @@ int inline Internal::shrink_literal (int lit, int blevel, } if (v.reason == external_reason) { - assert (!opts.exteagerreasons); + CADICAL_assert (!opts.exteagerreasons); v.reason = learn_external_reason_clause (-lit, 0, true); if (!v.reason) { - assert (!v.level); + CADICAL_assert (!v.level); return 0; } } - assert (v.reason != external_reason); + CADICAL_assert (v.reason != external_reason); if (f.shrinkable) { LOG ("skipping already shrinkable literal %d", (lit)); return 0; @@ -110,7 +114,7 @@ int inline Internal::shrink_literal (int lit, int blevel, f.poison = false; shrinkable.push_back (lit); if (opts.shrinkreap) { - assert (max_trail < trail.size ()); + CADICAL_assert (max_trail < trail.size ()); const unsigned dist = max_trail - v.trail; reap.push (dist); } @@ -121,11 +125,11 @@ unsigned Internal::shrunken_block_uip ( int uip, int blevel, std::vector<int>::reverse_iterator &rbegin_block, std::vector<int>::reverse_iterator &rend_block, std::vector<int>::size_type minimized_start, const int uip0) { - assert (clause[0] == uip0); + CADICAL_assert (clause[0] == uip0); LOG ("UIP on level %u, uip: %i (replacing by %i)", blevel, uip, uip0); - assert (rend_block > rbegin_block); - assert (rend_block < clause.rend ()); + CADICAL_assert (rend_block > rbegin_block); + CADICAL_assert (rend_block < clause.rend ()); unsigned block_shrunken = 0; *rbegin_block = -uip; Var &v = var (-uip); @@ -147,10 +151,10 @@ unsigned Internal::shrunken_block_uip ( *p = uip0; // if (lit == -uip) continue; ++block_shrunken; - assert (clause[0] == uip0); + CADICAL_assert (clause[0] == uip0); } mark_shrinkable_as_removable (blevel, minimized_start); - assert (clause[0] == uip0); + CADICAL_assert (clause[0] == uip0); return block_shrunken; } @@ -160,18 +164,18 @@ void inline Internal::shrunken_block_no_uip ( unsigned &block_minimized, const int uip0) { STOP (shrink); START (minimize); - assert (rend_block > rbegin_block); + CADICAL_assert (rend_block > rbegin_block); LOG ("no UIP found, now minimizing"); for (auto p = rbegin_block; p != rend_block; ++p) { - assert (p != clause.rend () - 1); + CADICAL_assert (p != clause.rend () - 1); const int lit = *p; if (opts.minimize && minimize_literal (-lit)) { - assert (!flags (lit).keep); + CADICAL_assert (!flags (lit).keep); ++block_minimized; *p = uip0; } else { flags (lit).keep = true; - assert (flags (lit).keep); + CADICAL_assert (flags (lit).keep); } } STOP (minimize); @@ -182,17 +186,17 @@ void Internal::push_literals_of_block ( const std::vector<int>::reverse_iterator &rbegin_block, const std::vector<int>::reverse_iterator &rend_block, int blevel, unsigned max_trail) { - assert (rbegin_block < rend_block); + CADICAL_assert (rbegin_block < rend_block); for (auto p = rbegin_block; p != rend_block; ++p) { - assert (p != clause.rend () - 1); - assert (!flags (*p).keep); + CADICAL_assert (p != clause.rend () - 1); + CADICAL_assert (!flags (*p).keep); const int lit = *p; LOG ("pushing lit %i of blevel %i", lit, var (lit).level); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG int tmp = #endif shrink_literal (lit, blevel, max_trail); - assert (tmp > 0); + CADICAL_assert (tmp > 0); } } @@ -200,24 +204,24 @@ unsigned inline Internal::shrink_next (int blevel, unsigned &open, unsigned &max_trail) { const auto &t = &trail; if (opts.shrinkreap) { - assert (!reap.empty ()); + CADICAL_assert (!reap.empty ()); const unsigned dist = reap.pop (); --open; - assert (dist <= max_trail); + CADICAL_assert (dist <= max_trail); const unsigned pos = max_trail - dist; - assert (pos < t->size ()); + CADICAL_assert (pos < t->size ()); const int uip = (*t)[pos]; - assert (val (uip) > 0); + CADICAL_assert (val (uip) > 0); LOG ("trying to shrink literal %d at trail[%u] and level %d", uip, pos, blevel); return uip; } else { int uip; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG unsigned init_max_trail = max_trail; #endif do { - assert (max_trail <= init_max_trail); + CADICAL_assert (max_trail <= init_max_trail); uip = (*t)[max_trail--]; } while (!flags (uip).shrinkable); --open; @@ -233,14 +237,14 @@ unsigned inline Internal::shrink_along_reason (int uip, int blevel, unsigned max_trail) { LOG ("shrinking along the reason of lit %i", uip); unsigned open = 0; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG const Flags &f = flags (uip); #endif const Var &v = var (uip); - assert (f.shrinkable); - assert (v.level == blevel); - assert (v.reason); + CADICAL_assert (f.shrinkable); + CADICAL_assert (v.level == blevel); + CADICAL_assert (v.reason); if (opts.minimizeticks) stats.ticks.search[stable]++; @@ -251,7 +255,7 @@ unsigned inline Internal::shrink_along_reason (int uip, int blevel, for (int lit : c) { if (lit == uip) continue; - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); int tmp = shrink_literal (lit, blevel, max_trail); if (tmp < 0) { failed_ptr = true; @@ -273,13 +277,13 @@ Internal::shrink_block (std::vector<int>::reverse_iterator &rbegin_lits, int blevel, unsigned &open, unsigned &block_minimized, const int uip0, unsigned max_trail) { - assert (shrinkable.empty ()); - assert (blevel <= this->level); - assert (open < clause.size ()); - assert (rbegin_lits >= clause.rbegin ()); - assert (rend_block < clause.rend ()); - assert (rbegin_lits < rend_block); - assert (opts.shrink); + CADICAL_assert (shrinkable.empty ()); + CADICAL_assert (blevel <= this->level); + CADICAL_assert (open < clause.size ()); + CADICAL_assert (rbegin_lits >= clause.rbegin ()); + CADICAL_assert (rend_block < clause.rend ()); + CADICAL_assert (rbegin_lits < rend_block); + CADICAL_assert (opts.shrink); #ifdef LOGGING @@ -301,18 +305,18 @@ Internal::shrink_block (std::vector<int>::reverse_iterator &rbegin_lits, if (!failed) { push_literals_of_block (rbegin_lits, rend_block, blevel, max_trail); - assert (!opts.shrinkreap || reap.size () == open); + CADICAL_assert (!opts.shrinkreap || reap.size () == open); - assert (open > 0); + CADICAL_assert (open > 0); while (!failed) { - assert (!opts.shrinkreap || reap.size () == open); + CADICAL_assert (!opts.shrinkreap || reap.size () == open); uip = shrink_next (blevel, open, max_trail); if (open == 0) { break; } open += shrink_along_reason (uip, blevel, resolve_large_clauses, failed, max_trail2); - assert (open >= 1); + CADICAL_assert (open >= 1); } if (!failed) @@ -370,7 +374,7 @@ std::vector<int>::reverse_iterator Internal::minimize_and_shrink_block ( { LOG ("shrinking block"); - assert (rbegin_block < clause.rend () - 1); + CADICAL_assert (rbegin_block < clause.rend () - 1); int blevel; unsigned open = 0; unsigned max_trail; @@ -378,7 +382,7 @@ std::vector<int>::reverse_iterator Internal::minimize_and_shrink_block ( // find begining of block; std::vector<int>::reverse_iterator rend_block; { - assert (rbegin_block <= clause.rend ()); + CADICAL_assert (rbegin_block <= clause.rend ()); const int lit = *rbegin_block; const int idx = vidx (lit); blevel = vtab[idx].level; @@ -388,7 +392,7 @@ std::vector<int>::reverse_iterator Internal::minimize_and_shrink_block ( rend_block = rbegin_block; bool finished; do { - assert (rend_block < clause.rend () - 1); + CADICAL_assert (rend_block < clause.rend () - 1); const int lit = *(++rend_block); const int idx = vidx (lit); finished = (blevel != vtab[idx].level); @@ -401,10 +405,10 @@ std::vector<int>::reverse_iterator Internal::minimize_and_shrink_block ( } while (!finished); } - assert (open > 0); - assert (open < clause.size ()); - assert (rbegin_block < clause.rend ()); - assert (rend_block < clause.rend ()); + CADICAL_assert (open > 0); + CADICAL_assert (open < clause.size ()); + CADICAL_assert (rbegin_block < clause.rend ()); + CADICAL_assert (rend_block < clause.rend ()); unsigned block_shrunken = 0, block_minimized = 0; if (open < 2) { @@ -424,7 +428,7 @@ std::vector<int>::reverse_iterator Internal::minimize_and_shrink_block ( } void Internal::shrink_and_minimize_clause () { - assert (opts.minimize || opts.shrink > 0); + CADICAL_assert (opts.minimize || opts.shrink > 0); LOG (clause, "shrink first UIP clause"); START (shrink); @@ -434,7 +438,7 @@ void Internal::shrink_and_minimize_clause () { unsigned total_shrunken = 0; unsigned total_minimized = 0; - LOG (clause, "shrink first UIP clause (asserting lit: %i)", clause[0]); + LOG (clause, "shrink first UIP clause (CADICAL_asserting lit: %i)", clause[0]); auto rend_lits = clause.rend () - 1; auto rend_block = clause.rbegin (); @@ -442,7 +446,7 @@ void Internal::shrink_and_minimize_clause () { // for direct LRAT we remember how the clause used to look vector<int> old_clause_lrat; - assert (minimize_chain.empty ()); + CADICAL_assert (minimize_chain.empty ()); if (lrat) for (auto &i : clause) old_clause_lrat.push_back (i); @@ -455,18 +459,18 @@ void Internal::shrink_and_minimize_clause () { LOG (clause, "post shrink pass (with uips, not removed) first UIP clause"); LOG (old_clause_lrat, "(used for lratdirect) before shrink: clause"); -#if defined(LOGGING) || !defined(NDEBUG) +#if defined(LOGGING) || !defined(CADICAL_NDEBUG) const unsigned old_size = clause.size (); #endif std::vector<int> stack; { std::vector<int>::size_type i = 1; for (std::vector<int>::size_type j = 1; j < clause.size (); ++j) { - assert (i <= j); + CADICAL_assert (i <= j); clause[i] = clause[j]; if (lrat) { - assert (j < old_clause_lrat.size ()); - assert (mini_chain.empty ()); + CADICAL_assert (j < old_clause_lrat.size ()); + CADICAL_assert (mini_chain.empty ()); if (clause[j] != old_clause_lrat[j]) { calculate_minimize_chain (-old_clause_lrat[j], stack); for (auto p : mini_chain) { @@ -478,13 +482,13 @@ void Internal::shrink_and_minimize_clause () { if (clause[j] == uip0) { continue; } - assert (flags (clause[i]).keep); + CADICAL_assert (flags (clause[i]).keep); ++i; LOG ("keeping literal %i", clause[j]); } clause.resize (i); } - assert (old_size == + CADICAL_assert (old_size == (unsigned) clause.size () + total_shrunken + total_minimized); LOG (clause, "after shrinking first UIP clause"); LOG ("clause shrunken by %zd literals (including %u minimized)", @@ -505,3 +509,5 @@ void Internal::shrink_and_minimize_clause () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/signal.cpp b/src/sat/cadical/cadical_signal.cpp similarity index 90% rename from src/sat/cadical/signal.cpp rename to src/sat/cadical/cadical_signal.cpp index 384ba986f..099277f03 100644 --- a/src/sat/cadical/signal.cpp +++ b/src/sat/cadical/cadical_signal.cpp @@ -1,3 +1,5 @@ +#include "global.h" + #include "signal.hpp" #include "cadical.hpp" #include "resources.hpp" @@ -9,9 +11,13 @@ /*------------------------------------------------------------------------*/ +#ifndef WIN32 extern "C" { #include <unistd.h> } +#endif + +ABC_NAMESPACE_IMPL_START /*------------------------------------------------------------------------*/ @@ -22,7 +28,7 @@ namespace CaDiCaL { static volatile bool caught_signal = false; static Handler *signal_handler; -#ifndef __WIN32 +#ifndef WIN32 static volatile bool caught_alarm = false; static volatile bool alarm_set = false; @@ -42,7 +48,7 @@ void Handler::catch_alarm () { catch_signal (SIGALRM); } SIGNALS #undef SIGNAL -#ifndef __WIN32 +#ifndef WIN32 static void (*SIGALRM_handler) (int); @@ -65,7 +71,7 @@ void Signal::reset () { SIG##_handler = 0; SIGNALS #undef SIGNAL -#ifndef __WIN32 +#ifndef WIN32 reset_alarm (); #endif caught_signal = false; @@ -77,7 +83,7 @@ const char *Signal::name (int sig) { return #SIG; SIGNALS #undef SIGNAL -#ifndef __WIN32 +#ifndef WIN32 if (sig == SIGALRM) return "SIGALRM"; #endif @@ -91,7 +97,7 @@ const char *Signal::name (int sig) { // exclusive access to. All these solutions are painful and not elegant. static void catch_signal (int sig) { -#ifndef __WIN32 +#ifndef WIN32 if (sig == SIGALRM && absolute_real_time () >= alarm_time) { if (!caught_alarm) { caught_alarm = true; @@ -119,12 +125,12 @@ void Signal::set (Handler *h) { #undef SIGNAL } -#ifndef __WIN32 +#ifndef WIN32 void Signal::alarm (int seconds) { - assert (seconds >= 0); - assert (!alarm_set); - assert (alarm_time < 0); + CADICAL_assert (seconds >= 0); + CADICAL_assert (!alarm_set); + CADICAL_assert (alarm_time < 0); SIGALRM_handler = signal (SIGALRM, catch_signal); alarm_set = true; alarm_time = absolute_real_time () + seconds; @@ -134,3 +140,5 @@ void Signal::alarm (int seconds) { #endif } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/solution.cpp b/src/sat/cadical/cadical_solution.cpp similarity index 89% rename from src/sat/cadical/solution.cpp rename to src/sat/cadical/cadical_solution.cpp index 8427d14e3..1d7514e42 100644 --- a/src/sat/cadical/solution.cpp +++ b/src/sat/cadical/cadical_solution.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { // Sam Buss suggested to debug the case where a solver incorrectly claims @@ -11,7 +15,7 @@ namespace CaDiCaL { // a symbolic debugger immediately. void External::check_solution_on_learned_clause () { - assert (solution); + CADICAL_assert (solution); for (const auto &lit : internal->clause) if (sol (internal->externalize (lit)) == lit) return; @@ -24,7 +28,7 @@ void External::check_solution_on_learned_clause () { } void External::check_solution_on_shrunken_clause (Clause *c) { - assert (solution); + CADICAL_assert (solution); for (const auto &lit : *c) if (sol (internal->externalize (lit)) == lit) return; @@ -36,15 +40,17 @@ void External::check_solution_on_shrunken_clause (Clause *c) { } void External::check_no_solution_after_learning_empty_clause () { - assert (solution); + CADICAL_assert (solution); FATAL ("learned empty clause but got solution"); } void External::check_solution_on_learned_unit_clause (int unit) { - assert (solution); + CADICAL_assert (solution); if (sol (internal->externalize (unit)) == unit) return; FATAL ("learned unit %d contradicts solution", unit); } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/solver.cpp b/src/sat/cadical/cadical_solver.cpp similarity index 97% rename from src/sat/cadical/solver.cpp rename to src/sat/cadical/cadical_solver.cpp index f125cc32f..df051f4de 100644 --- a/src/sat/cadical/solver.cpp +++ b/src/sat/cadical/cadical_solver.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + /*------------------------------------------------------------------------*/ namespace CaDiCaL { @@ -29,7 +33,7 @@ namespace CaDiCaL { #define STATE(S) \ do { \ - assert (is_power_of_two (S)); \ + CADICAL_assert (is_power_of_two (S)); \ if (_state == S) \ break; \ _state = S; \ @@ -249,7 +253,7 @@ static void log_api_call_returns (Internal *internal, const char *name, /*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/ -#ifndef NTRACING +#ifndef CADICAL_NTRACING /*------------------------------------------------------------------------*/ #define TRACE(...) \ @@ -264,28 +268,28 @@ static void log_api_call_returns (Internal *internal, const char *name, } while (0) void Solver::trace_api_call (const char *s0) const { - assert (trace_api_file); + CADICAL_assert (trace_api_file); LOG ("TRACE %s", s0); fprintf (trace_api_file, "%s\n", s0); fflush (trace_api_file); } void Solver::trace_api_call (const char *s0, int i1) const { - assert (trace_api_file); + CADICAL_assert (trace_api_file); LOG ("TRACE %s %d", s0, i1); fprintf (trace_api_file, "%s %d\n", s0, i1); fflush (trace_api_file); } void Solver::trace_api_call (const char *s0, const char *s1) const { - assert (trace_api_file); + CADICAL_assert (trace_api_file); LOG ("TRACE %s %s", s0, s1); fprintf (trace_api_file, "%s %s\n", s0, s1); fflush (trace_api_file); } void Solver::trace_api_call (const char *s0, const char *s1, int i2) const { - assert (trace_api_file); + CADICAL_assert (trace_api_file); LOG ("TRACE %s %s %d", s0, s1, i2); fprintf (trace_api_file, "%s %s %d\n", s0, s1, i2); fflush (trace_api_file); @@ -306,7 +310,7 @@ void Solver::trace_api_call (const char *s0, const char *s1, int i2) const { static bool tracing_api_calls_through_environment_variable_method; /*------------------------------------------------------------------------*/ -#else // NTRACING +#else // CADICAL_NTRACING /*------------------------------------------------------------------------*/ #define TRACE(...) \ @@ -321,7 +325,7 @@ static bool tracing_nb_lidrup_env_var_method = false; Solver::Solver () { -#ifndef NTRACING +#ifndef CADICAL_NTRACING const char *path = getenv ("CADICAL_API_TRACE"); if (!path) path = getenv ("CADICALAPITRACE"); @@ -351,7 +355,7 @@ Solver::Solver () { external = new External (internal); DeferDeletePtr<External> delete_external (external); STATE (CONFIGURING); -#ifndef NTRACING +#ifndef CADICAL_NTRACING if (tracing_api_calls_through_environment_variable_method) message ("tracing API calls to '%s'", path); #endif @@ -399,11 +403,11 @@ Solver::~Solver () { delete internal; delete external; -#ifndef NTRACING +#ifndef CADICAL_NTRACING if (close_trace_api_file) { close_trace_api_file = false; - assert (trace_api_file); - assert (tracing_api_calls_through_environment_variable_method); + CADICAL_assert (trace_api_file); + CADICAL_assert (tracing_api_calls_through_environment_variable_method); fclose (trace_api_file); tracing_api_calls_through_environment_variable_method = false; } @@ -454,7 +458,7 @@ int Solver::reserve_difference (int number_of_vars) { } /*------------------------------------------------------------------------*/ -#ifndef NTRACING +#ifndef CADICAL_NTRACING void Solver::trace_api_calls (FILE *file) { LOG_API_CALL_BEGIN ("trace_api_calls"); @@ -739,7 +743,7 @@ void Solver::implied (std::vector<int> &entrailed) { int Solver::call_external_solve_and_check_results (bool preprocess_only) { transition_to_steady_state (); - assert (state () & READY); + CADICAL_assert (state () & READY); STATE (SOLVING); const int res = external->solve (preprocess_only); if (res == 10) @@ -812,8 +816,8 @@ int Solver::val (int lit) { external->conclude_sat (); int res = external->ival (lit); LOG_API_CALL_RETURNS ("val", lit, res); - assert (state () == SATISFIED); - assert (res == lit || res == -lit); + CADICAL_assert (state () == SATISFIED); + CADICAL_assert (res == lit || res == -lit); return res; } @@ -826,7 +830,7 @@ bool Solver::flip (int lit) { "can only flip when no external propagator is present"); bool res = external->flip (lit); LOG_API_CALL_RETURNS ("flip", lit, res); - assert (state () == SATISFIED); + CADICAL_assert (state () == SATISFIED); return res; } @@ -839,7 +843,7 @@ bool Solver::flippable (int lit) { "can only flip when no external propagator is present"); bool res = external->flippable (lit); LOG_API_CALL_RETURNS ("flippable", lit, res); - assert (state () == SATISFIED); + CADICAL_assert (state () == SATISFIED); return res; } @@ -851,7 +855,7 @@ bool Solver::failed (int lit) { "can only get failed assumptions in unsatisfied state"); bool res = external->failed (lit); LOG_API_CALL_RETURNS ("failed", lit, res); - assert (state () == UNSATISFIED); + CADICAL_assert (state () == UNSATISFIED); return res; } @@ -862,7 +866,7 @@ bool Solver::constraint_failed () { "can only determine if constraint failed in unsatisfied state"); bool res = external->failed_constraint (); LOG_API_CALL_RETURNS ("constraint_failed", res); - assert (state () == UNSATISFIED); + CADICAL_assert (state () == UNSATISFIED); return res; } @@ -1125,7 +1129,7 @@ bool Solver::trace_proof (FILE *external_file, const char *name) { "can only start proof tracing to '%s' right after initialization", name); File *internal_file = File::write (internal, external_file, name); - assert (internal_file); + CADICAL_assert (internal_file); internal->trace (internal_file); LOG_API_CALL_RETURNS ("trace_proof", name, true); return true; @@ -1252,7 +1256,7 @@ void Solver::conclude () { external->conclude_sat (); else if (state () == INCONCLUSIVE) external->conclude_unknown (); - assert (state () == UNSATISFIED || state () == SATISFIED || + CADICAL_assert (state () == UNSATISFIED || state () == SATISFIED || state () == INCONCLUSIVE); LOG_API_CALL_END ("conclude"); } @@ -1261,7 +1265,7 @@ void Solver::conclude () { void Solver::build (FILE *file, const char *prefix) { - assert (file == stdout || file == stderr); + CADICAL_assert (file == stdout || file == stderr); Terminal *terminal; @@ -1278,7 +1282,7 @@ void Solver::build (FILE *file, const char *prefix) { const char *b = date (); const char *f = flags (); - assert (v); + CADICAL_assert (v); fputs (prefix, file); if (terminal) @@ -1375,7 +1379,7 @@ const char *Solver::read_dimacs (FILE *external_file, const char *name, REQUIRE (state () == CONFIGURING, "can only read DIMACS file right after initialization"); File *file = File::read (internal, external_file, name); - assert (file); + CADICAL_assert (file); const char *err = read_dimacs (file, vars, strict); delete file; LOG_API_CALL_RETURNS ("read_dimacs", name, err); @@ -1405,7 +1409,7 @@ const char *Solver::read_dimacs (FILE *external_file, const char *name, REQUIRE (state () == CONFIGURING, "can only read DIMACS file right after initialization"); File *file = File::read (internal, external_file, name); - assert (file); + CADICAL_assert (file); const char *err = read_dimacs (file, vars, strict, &incremental, &cubes); delete file; LOG_API_CALL_RETURNS ("read_dimacs", name, err); @@ -1532,7 +1536,7 @@ public: ClauseCounter () : vars (0), clauses (0) {} bool clause (const vector<int> &c) { for (const auto &lit : c) { - assert (lit != INT_MIN); + CADICAL_assert (lit != INT_MIN); int idx = abs (lit); if (idx > vars) vars = idx; @@ -1561,7 +1565,7 @@ public: const char *Solver::write_dimacs (const char *path, int min_max_var) { LOG_API_CALL_BEGIN ("write_dimacs", path, min_max_var); REQUIRE_VALID_STATE (); -#ifndef QUIET +#ifndef CADICAL_QUIET const double start = internal->time (); #endif internal->restore_clauses (); @@ -1588,7 +1592,7 @@ const char *Solver::write_dimacs (const char *path, int min_max_var) { } else res = internal->error_message.init ( "failed to open DIMACS file '%s' for writing", path); -#ifndef QUIET +#ifndef CADICAL_QUIET if (!res) { const double end = internal->time (); MSG ("wrote %" PRId64 " clauses in %.2f seconds %s time", @@ -1633,7 +1637,7 @@ const char *Solver::write_extension (const char *path) { LOG_API_CALL_BEGIN ("write_extension", path); REQUIRE_VALID_STATE (); const char *res = 0; -#ifndef QUIET +#ifndef CADICAL_QUIET const double start = internal->time (); #endif File *file = File::write (internal, path); @@ -1646,7 +1650,7 @@ const char *Solver::write_extension (const char *path) { } else res = internal->error_message.init ( "failed to open extension file '%s' for writing", path); -#ifndef QUIET +#ifndef CADICAL_QUIET if (!res) { const double end = internal->time (); MSG ("wrote %" PRId64 " witnesses in %.2f seconds %s time", @@ -1700,7 +1704,7 @@ void Solver::copy (Solver &other) const { void Solver::section (const char *title) { if (state () == DELETING) return; -#ifdef QUIET +#ifdef CADICAL_QUIET (void) title; #endif REQUIRE_INITIALIZED (); @@ -1710,7 +1714,7 @@ void Solver::section (const char *title) { void Solver::message (const char *fmt, ...) { if (state () == DELETING) return; -#ifdef QUIET +#ifdef CADICAL_QUIET (void) fmt; #else REQUIRE_INITIALIZED (); @@ -1725,7 +1729,7 @@ void Solver::message () { if (state () == DELETING) return; REQUIRE_INITIALIZED (); -#ifndef QUIET +#ifndef CADICAL_QUIET internal->message (); #endif } @@ -1734,7 +1738,7 @@ void Solver::verbose (int level, const char *fmt, ...) { if (state () == DELETING) return; REQUIRE_VALID_OR_SOLVING_STATE (); -#ifdef QUIET +#ifdef CADICAL_QUIET (void) level; (void) fmt; #else @@ -1756,3 +1760,5 @@ void Solver::error (const char *fmt, ...) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/stable.cpp b/src/sat/cadical/cadical_stable.cpp similarity index 67% rename from src/sat/cadical/stable.cpp rename to src/sat/cadical/cadical_stable.cpp index 44c9d1021..f177139ac 100644 --- a/src/sat/cadical/stable.cpp +++ b/src/sat/cadical/cadical_stable.cpp @@ -1,11 +1,15 @@ +#include "global.h" + #ifdef PROFILE_MODE #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { bool Internal::propagate_stable () { - assert (stable); + CADICAL_assert (stable); START (propstable); bool res = propagate (); STOP (propstable); @@ -13,19 +17,23 @@ bool Internal::propagate_stable () { } void Internal::analyze_stable () { - assert (stable); + CADICAL_assert (stable); START (analyzestable); analyze (); STOP (analyzestable); } int Internal::decide_stable () { - assert (stable); + CADICAL_assert (stable); return decide (); } }; // namespace CaDiCaL +ABC_NAMESPACE_IMPL_END + #else +ABC_NAMESPACE_IMPL_START int stable_if_not_profile_mode_dummy; +ABC_NAMESPACE_IMPL_END #endif diff --git a/src/sat/cadical/stats.cpp b/src/sat/cadical/cadical_stats.cpp similarity index 99% rename from src/sat/cadical/stats.cpp rename to src/sat/cadical/cadical_stats.cpp index e390e1d40..8b07745d6 100644 --- a/src/sat/cadical/stats.cpp +++ b/src/sat/cadical/cadical_stats.cpp @@ -1,7 +1,11 @@ // vim: set tw=300: set VIM text width to 300 characters for this file. +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -28,7 +32,7 @@ Stats::Stats () { void Stats::print (Internal *internal) { -#ifdef QUIET +#ifdef CADICAL_QUIET (void) internal; #else @@ -684,7 +688,7 @@ void Stats::print (Internal *internal) { if (all || stats.walk.count) { PRT ("walked: %15" PRId64 " %10.2f interval", stats.walk.count, relative (stats.conflicts, stats.walk.count)); -#ifndef QUIET +#ifndef CADICAL_QUIET if (internal->profiles.walk.value > 0) PRT (" flips: %15" PRId64 " %10.2f M per second", stats.walk.flips, @@ -746,11 +750,11 @@ void Stats::print (Internal *internal) { tout.magenta_code (), internal->opts.realtime ? "real" : "process", tout.normal_code ()); -#endif // ifndef QUIET +#endif // ifndef CADICAL_QUIET } void Internal::print_resource_usage () { -#ifndef QUIET +#ifndef CADICAL_QUIET SECTION ("resources"); uint64_t m = maximum_resident_set_size (); MSG ("total process time since initialization: %12.2f seconds", @@ -818,3 +822,5 @@ void LratChecker::print_stats () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/subsume.cpp b/src/sat/cadical/cadical_subsume.cpp similarity index 95% rename from src/sat/cadical/subsume.cpp rename to src/sat/cadical/cadical_subsume.cpp index a125a10f4..b75040ca8 100644 --- a/src/sat/cadical/subsume.cpp +++ b/src/sat/cadical/cadical_subsume.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -72,16 +76,16 @@ namespace CaDiCaL { // removed is returned. inline int Internal::subsume_check (Clause *subsuming, Clause *subsumed) { -#ifdef NDEBUG +#ifdef CADICAL_NDEBUG (void) subsumed; #endif - // Only use 'subsumed' for these following assertion checks. Otherwise we + // Only use 'subsumed' for these following CADICAL_assertion checks. Otherwise we // only require that 'subsumed' has all its literals marked. // - assert (!subsumed->garbage); - assert (!subsuming->garbage); - assert (subsuming != subsumed); - assert (subsuming->size <= subsumed->size); + CADICAL_assert (!subsumed->garbage); + CADICAL_assert (!subsuming->garbage); + CADICAL_assert (subsuming != subsumed); + CADICAL_assert (subsuming->size <= subsumed->size); stats.subchecks++; if (subsuming->size == 2) @@ -104,8 +108,8 @@ inline int Internal::subsume_check (Clause *subsuming, Clause *subsumed) { else flipped = lit; } - assert (prev); - assert (!subsuming->literals[0]); + CADICAL_assert (prev); + CADICAL_assert (!subsuming->literals[0]); subsuming->literals[0] = prev; if (failed) return 0; @@ -124,7 +128,7 @@ inline int Internal::subsume_check (Clause *subsuming, Clause *subsumed) { inline void Internal::subsume_clause (Clause *subsuming, Clause *subsumed) { stats.subsumed++; - assert (subsuming->size <= subsumed->size); + CADICAL_assert (subsuming->size <= subsumed->size); LOG (subsumed, "subsumed"); if (subsumed->redundant) stats.subred++; @@ -142,9 +146,9 @@ inline void Internal::subsume_clause (Clause *subsuming, Clause *subsumed) { stats.current.irredundant++; stats.added.irredundant++; stats.irrlits += subsuming->size; - assert (stats.current.redundant > 0); + CADICAL_assert (stats.current.redundant > 0); stats.current.redundant--; - assert (stats.added.redundant > 0); + CADICAL_assert (stats.added.redundant > 0); stats.added.redundant--; // ... and keep 'stats.added.total'. } @@ -157,7 +161,7 @@ void Internal::strengthen_clause (Clause *c, int lit) { if (opts.check && is_external_forgettable (c->id)) mark_garbage_external_forgettable (c->id); stats.strengthened++; - assert (c->size > 2); + CADICAL_assert (c->size > 2); LOG (c, "removing %d in", lit); if (proof) { LOG (lrat_chain, "strengthening clause with chain"); @@ -166,7 +170,7 @@ void Internal::strengthen_clause (Clause *c, int lit) { if (!c->redundant) mark_removed (lit); auto new_end = remove (c->begin (), c->end (), lit); - assert (new_end + 1 == c->end ()), (void) new_end; + CADICAL_assert (new_end + 1 == c->end ()), (void) new_end; (void) shrink_clause (c, c->size - 1); // bump_clause2 (c); LOG (c, "strengthened"); @@ -185,7 +189,7 @@ inline int Internal::try_to_subsume_clause (Clause *c, vector<Clause *> &shrunken) { stats.subtried++; - assert (!level); + CADICAL_assert (!level); LOG (c, "trying to subsume"); mark (c); // signed! @@ -262,7 +266,7 @@ inline int Internal::try_to_subsume_clause (Clause *c, // const Occs &os = occs (sign * lit); for (const auto &e : os) { - assert (!e->garbage); // sanity check + CADICAL_assert (!e->garbage); // sanity check if (e->garbage) continue; // defensive: not needed flipped = subsume_check (e, c); @@ -288,7 +292,7 @@ inline int Internal::try_to_subsume_clause (Clause *c, if (flipped) { LOG (d, "strengthening"); if (lrat) { - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); lrat_chain.push_back (c->id); lrat_chain.push_back (d->id); } @@ -296,7 +300,7 @@ inline int Internal::try_to_subsume_clause (Clause *c, c->used = d->used; strengthen_clause (c, -flipped); lrat_chain.clear (); - assert (likely_to_be_kept_clause (c)); + CADICAL_assert (likely_to_be_kept_clause (c)); shrunken.push_back (c); return -1; } @@ -369,7 +373,7 @@ bool Internal::subsume_round () { int old_marked_candidate_variables_for_elimination = stats.mark.elim; - assert (!level); + CADICAL_assert (!level); // Allocate schedule and occurrence lists. // @@ -432,7 +436,7 @@ bool Internal::subsume_round () { if (cs.clause->size > 2) cs.clause->subsume = true; -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t scheduled = schedule.size (); int64_t total = stats.current.irredundant + stats.current.redundant; PHASE ("subsume-round", stats.subsumerounds, @@ -461,7 +465,7 @@ bool Internal::subsume_round () { break; Clause *c = s.clause; - assert (!c->garbage); + CADICAL_assert (!c->garbage); checked++; @@ -621,7 +625,7 @@ void Internal::subsume () { stats.subsumephases++; if (external_prop) { - assert (!level); + CADICAL_assert (!level); private_steps = true; } @@ -638,9 +642,11 @@ void Internal::subsume () { transred (); if (external_prop) { - assert (!level); + CADICAL_assert (!level); private_steps = false; } } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/sweep.cpp b/src/sat/cadical/cadical_sweep.cpp similarity index 86% rename from src/sat/cadical/sweep.cpp rename to src/sat/cadical/cadical_sweep.cpp index 508315736..4dca2b4bc 100644 --- a/src/sat/cadical/sweep.cpp +++ b/src/sat/cadical/cadical_sweep.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { Sweeper::Sweeper (Internal *i) @@ -19,9 +23,9 @@ Sweeper::~Sweeper () { int Internal::sweep_solve () { START (sweepsolve); - kitten_randomize_phases (citten); + cadical_kitten_randomize_phases (citten); stats.sweep_solved++; - int res = kitten_solve (citten); + int res = cadical_kitten_solve (citten); if (res == 10) stats.sweep_sat++; if (res == 20) @@ -32,25 +36,25 @@ int Internal::sweep_solve () { bool Internal::sweep_flip (int lit) { START (sweepflip); - bool res = kitten_flip_signed_literal (citten, lit); + bool res = cadical_kitten_flip_signed_literal (citten, lit); STOP (sweepflip); return res; } int Internal::sweep_flip_and_implicant (int lit) { START (sweepimplicant); - int res = kitten_flip_and_implicant_for_signed_literal (citten, lit); + int res = cadical_kitten_flip_and_implicant_for_signed_literal (citten, lit); STOP (sweepimplicant); return res; } -void Internal::sweep_set_kitten_ticks_limit (Sweeper &sweeper) { +void Internal::sweep_set_cadical_kitten_ticks_limit (Sweeper &sweeper) { uint64_t remaining = 0; const uint64_t current = sweeper.current_ticks; if (current < sweeper.limit.ticks) remaining = sweeper.limit.ticks - current; - LOG ("'kitten_ticks' remaining %" PRIu64, remaining); - kitten_set_ticks_limit (citten, remaining); + LOG ("'cadical_kitten_ticks' remaining %" PRIu64, remaining); + cadical_kitten_set_ticks_limit (citten, remaining); } void Internal::sweep_update_noccs (Clause *c) { @@ -133,7 +137,7 @@ void Internal::sweep_dense_propagate (Sweeper &sweeper) { while (i < work.size ()) { int lit = work[i++]; LOG ("sweeping propagation of %d", lit); - assert (val (lit) > 0); + CADICAL_assert (val (lit) > 0); ticks += 1 + cache_lines (occs (-lit).size (), sizeof (Clause *)); const Occs &ns = occs (-lit); for (const auto &c : ns) { @@ -194,11 +198,11 @@ void Internal::sweep_dense_propagate (Sweeper &sweeper) { work.clear (); } -bool Internal::kitten_ticks_limit_hit (Sweeper &sweeper, const char *when) { +bool Internal::cadical_kitten_ticks_limit_hit (Sweeper &sweeper, const char *when) { const uint64_t current = - kitten_current_ticks (citten) + sweeper.current_ticks; + cadical_kitten_current_ticks (citten) + sweeper.current_ticks; if (current >= sweeper.limit.ticks) { - LOG ("'kitten_ticks' limit of %" PRIu64 " ticks hit after %" PRIu64 + LOG ("'cadical_kitten_ticks' limit of %" PRIu64 " ticks hit after %" PRIu64 " ticks during %s", sweeper.limit.ticks, current, when); return true; @@ -225,8 +229,8 @@ void Internal::init_sweeper (Sweeper &sweeper) { .size (); // initialize with the cost of building full occ list. sweeper.current_ticks += 2 + 2 * cache_lines (clauses.size (), sizeof (Clause *)); - assert (!citten); - citten = kitten_init (); + CADICAL_assert (!citten); + citten = cadical_kitten_init (); citten_clear_track_log_terminate (); sweep_dense_mode_and_watch_irredundant (); // full occurence list @@ -284,7 +288,7 @@ void Internal::release_sweeper (Sweeper &sweeper) { for (unsigned i = 0; i < 2; i++) erase_vector (sweeper.core[i]); - kitten_release (citten); + cadical_kitten_release (citten); citten = 0; stats.ticks.sweep += sweeper.current_ticks; sweep_sparse_mode (); @@ -293,23 +297,23 @@ void Internal::release_sweeper (Sweeper &sweeper) { void Internal::clear_sweeper (Sweeper &sweeper) { LOG ("clearing sweeping environment"); - sweeper.current_ticks += kitten_current_ticks (citten); + sweeper.current_ticks += cadical_kitten_current_ticks (citten); citten_clear_track_log_terminate (); for (auto &idx : sweeper.vars) { - assert (sweeper.depths[idx]); + CADICAL_assert (sweeper.depths[idx]); sweeper.depths[idx] = 0; } sweeper.vars.clear (); for (auto c : sweeper.clauses) { - assert (c->swept); + CADICAL_assert (c->swept); c->swept = false; } sweeper.clauses.clear (); sweeper.backbone.clear (); sweeper.partition.clear (); sweeper.encoded = 0; - sweep_set_kitten_ticks_limit (sweeper); + sweep_set_cadical_kitten_ticks_limit (sweeper); } int Internal::sweep_repr (Sweeper &sweeper, int lit) { @@ -331,7 +335,7 @@ int Internal::sweep_repr (Sweeper &sweeper, int lit) { sweeper.reprs[prev] = res; prev = next; } - assert (sweeper.reprs[-prev] == not_res); + CADICAL_assert (sweeper.reprs[-prev] == not_res); } return res; } @@ -344,16 +348,16 @@ void Internal::add_literal_to_environment (Sweeper &sweeper, unsigned depth, const int idx = abs (lit); if (sweeper.depths[idx]) return; - assert (depth < UINT_MAX); + CADICAL_assert (depth < UINT_MAX); sweeper.depths[idx] = depth + 1; - assert (idx); + CADICAL_assert (idx); sweeper.vars.push_back (idx); LOG ("sweeping[%u] adding literal %d", depth, lit); } void Internal::sweep_add_clause (Sweeper &sweeper, unsigned depth) { - // TODO: assertion fails, check if this an issue or can be avoided - // assert (sweeper.clause.size () > 1); + // TODO: CADICAL_assertion fails, check if this an issue or can be avoided + // CADICAL_assert (sweeper.clause.size () > 1); for (const auto &lit : sweeper.clause) add_literal_to_environment (sweeper, depth, lit); citten_clause_with_id (citten, sweeper.clauses.size (), @@ -366,9 +370,9 @@ void Internal::sweep_add_clause (Sweeper &sweeper, unsigned depth) { void Internal::sweep_clause (Sweeper &sweeper, unsigned depth, Clause *c) { if (c->swept) return; - assert (can_sweep_clause (c)); + CADICAL_assert (can_sweep_clause (c)); LOG (c, "sweeping[%u]", depth); - assert (sweeper.clause.empty ()); + CADICAL_assert (sweeper.clause.empty ()); for (const auto &lit : *c) { const signed char tmp = val (lit); if (tmp > 0) { @@ -403,7 +407,7 @@ static void save_core_clause (void *state, unsigned id, bool learned, pc.cad_id = INVALID64; // delay giving ids } else { pc.sweep_id = id; // necessary - assert (id < sweeper->clauses.size ()); + CADICAL_assert (id < sweeper->clauses.size ()); pc.cad_id = sweeper->clauses[id]->id; } pc.kit_id = 0; @@ -436,17 +440,17 @@ static void save_core_clause_with_lrat (void *state, unsigned cid, pc.sweep_id = INVALID; pc.cad_id = INVALID64; if (!learned) { - assert (size); - assert (!chain_size); - assert (id < clauses.size ()); + CADICAL_assert (size); + CADICAL_assert (!chain_size); + CADICAL_assert (id < clauses.size ()); pc.sweep_id = id; - assert (id < clauses.size ()); + CADICAL_assert (id < clauses.size ()); pc.cad_id = clauses[id]->id; for (const auto &lit : *clauses[id]) { pc.literals.push_back (lit); } } else { - assert (chain_size); + CADICAL_assert (chain_size); pc.sweep_id = INVALID; pc.cad_id = INVALID64; // delay giving ids const unsigned *end = lits + size; @@ -462,7 +466,7 @@ static void save_core_clause_with_lrat (void *state, unsigned cid, LOG (pc.literals, "traced %s", pc.learned == true ? "learned" : "original"); else { - assert (id < clauses.size ()); + CADICAL_assert (id < clauses.size ()); LOG (clauses[id], "traced"); } #endif @@ -476,14 +480,14 @@ static int citten_terminate (void *data) { } // end extern C void Internal::citten_clear_track_log_terminate () { - assert (citten); - kitten_clear (citten); - kitten_track_antecedents (citten); + CADICAL_assert (citten); + cadical_kitten_clear (citten); + cadical_kitten_track_antecedents (citten); if (external->terminator) - kitten_set_terminator (citten, internal, citten_terminate); + cadical_kitten_set_terminator (citten, internal, citten_terminate); #ifdef LOGGING if (opts.log) - kitten_set_logging (citten); + cadical_kitten_set_logging (citten); #endif } @@ -491,10 +495,10 @@ void Internal::add_core (Sweeper &sweeper, unsigned core_idx) { if (unsat) return; LOG ("check and add extracted core[%u] lemmas to proof", core_idx); - assert (core_idx == 0 || core_idx == 1); + CADICAL_assert (core_idx == 0 || core_idx == 1); vector<sweep_proof_clause> &core = sweeper.core[core_idx]; - assert (!lrat || proof); + CADICAL_assert (!lrat || proof); unsigned unsat_size = 0; for (auto &pc : core) { @@ -502,18 +506,18 @@ void Internal::add_core (Sweeper &sweeper, unsigned core_idx) { if (!pc.learned) { LOG (pc.literals, - "not adding already present core[%u] kitten[%u] clause", + "not adding already present core[%u] cadical_kitten[%u] clause", core_idx, pc.kit_id); continue; } - LOG (pc.literals, "adding extracted core[%u] kitten[%u] lemma", + LOG (pc.literals, "adding extracted core[%u] cadical_kitten[%u] lemma", core_idx, pc.kit_id); const unsigned new_size = pc.literals.size (); if (lrat) { - assert (pc.cad_id == INVALID64); + CADICAL_assert (pc.cad_id == INVALID64); for (auto &cid : pc.chain) { int64_t id = 0; for (const auto &cpc : core) { @@ -522,8 +526,8 @@ void Internal::add_core (Sweeper &sweeper, unsigned core_idx) { id = cpc.cad_id; else { id = cpc.cad_id; - assert (cpc.cad_id == sweeper.clauses[cpc.sweep_id]->id); - assert (!sweeper.clauses[cpc.sweep_id]->garbage); + CADICAL_assert (cpc.cad_id == sweeper.clauses[cpc.sweep_id]->id); + CADICAL_assert (!sweeper.clauses[cpc.sweep_id]->garbage); // avoid duplicate ids of units with seen flags for (const auto &lit : cpc.literals) { if (val (lit) >= 0) @@ -542,7 +546,7 @@ void Internal::add_core (Sweeper &sweeper, unsigned core_idx) { break; } } - assert (id); + CADICAL_assert (id); if (id != INVALID64) lrat_chain.push_back (id); } @@ -581,8 +585,8 @@ void Internal::add_core (Sweeper &sweeper, unsigned core_idx) { continue; } - assert (new_size > 1); - assert (pc.learned); + CADICAL_assert (new_size > 1); + CADICAL_assert (pc.learned); if (proof) { pc.cad_id = ++clause_id; @@ -594,19 +598,19 @@ void Internal::add_core (Sweeper &sweeper, unsigned core_idx) { void Internal::save_core (Sweeper &sweeper, unsigned core) { LOG ("saving extracted core[%u] lemmas", core); - assert (core == 0 || core == 1); - assert (sweeper.core[core].empty ()); + CADICAL_assert (core == 0 || core == 1); + CADICAL_assert (sweeper.core[core].empty ()); sweeper.save = core; - kitten_compute_clausal_core (citten, 0); + cadical_kitten_compute_clausal_core (citten, 0); if (lrat) - kitten_trace_core (citten, &sweeper, save_core_clause_with_lrat); + cadical_kitten_trace_core (citten, &sweeper, save_core_clause_with_lrat); else - kitten_traverse_core_clauses_with_id (citten, &sweeper, + cadical_kitten_traverse_core_clauses_with_id (citten, &sweeper, save_core_clause); } void Internal::clear_core (Sweeper &sweeper, unsigned core_idx) { - assert (core_idx == 0 || core_idx == 1); + CADICAL_assert (core_idx == 0 || core_idx == 1); LOG ("clearing core[%u] lemmas", core_idx); vector<sweep_proof_clause> &core = sweeper.core[core_idx]; if (proof) { @@ -632,10 +636,10 @@ void Internal::init_backbone_and_partition (Sweeper &sweeper) { for (const auto &idx : sweeper.vars) { if (!active (idx)) continue; - assert (idx > 0); + CADICAL_assert (idx > 0); const int lit = idx; const int not_lit = -lit; - const signed char tmp = kitten_signed_value (citten, lit); + const signed char tmp = cadical_kitten_signed_value (citten, lit); const int candidate = (tmp < 0) ? not_lit : lit; LOG ("sweeping candidate %d", candidate); sweeper.backbone.push_back (candidate); @@ -648,9 +652,9 @@ void Internal::init_backbone_and_partition (Sweeper &sweeper) { } void Internal::sweep_empty_clause (Sweeper &sweeper) { - assert (!unsat); + CADICAL_assert (!unsat); save_add_clear_core (sweeper); - assert (unsat); + CADICAL_assert (unsat); } void Internal::sweep_refine_partition (Sweeper &sweeper) { @@ -671,7 +675,7 @@ void Internal::sweep_refine_partition (Sweeper &sweeper) { continue; if (val (other)) continue; - signed char value = kitten_signed_value (citten, other); + signed char value = cadical_kitten_signed_value (citten, other); if (!value) LOG ("dropping sub-solver unassigned %d", other); else if (value > 0) { @@ -708,7 +712,7 @@ void Internal::sweep_refine_partition (Sweeper &sweeper) { continue; if (val (other)) continue; - signed char value = kitten_signed_value (citten, other); + signed char value = cadical_kitten_signed_value (citten, other); if (value < 0) { new_partition.push_back (other); assigned_false++; @@ -746,7 +750,7 @@ void Internal::sweep_refine_backbone (Sweeper &sweeper) { const int lit = *p; if (val (lit)) continue; - signed char value = kitten_signed_value (citten, lit); + signed char value = cadical_kitten_signed_value (citten, lit); if (!value) LOG ("dropping sub-solver unassigned %d", lit); else if (value > 0) @@ -756,7 +760,7 @@ void Internal::sweep_refine_backbone (Sweeper &sweeper) { } void Internal::sweep_refine (Sweeper &sweeper) { - assert (kitten_status (citten) == 10); + CADICAL_assert (cadical_kitten_status (citten) == 10); if (sweeper.backbone.empty ()) LOG ("no need to refine empty backbone candidates"); else @@ -771,8 +775,8 @@ void Internal::flip_backbone_literals (Sweeper &sweeper) { const unsigned max_rounds = opts.sweepfliprounds; if (!max_rounds) return; - assert (sweeper.backbone.size ()); - if (kitten_status (citten) != 10) + CADICAL_assert (sweeper.backbone.size ()); + if (cadical_kitten_status (citten) != 10) return; #ifdef LOGGING unsigned total_flipped = 0; @@ -811,7 +815,7 @@ void Internal::flip_backbone_literals (Sweeper &sweeper) { break; if (terminated_asynchronously ()) break; - if (kitten_ticks_limit_hit (sweeper, "backbone flipping")) + if (cadical_kitten_ticks_limit_hit (sweeper, "backbone flipping")) break; if (refine) sweep_refine (sweeper); @@ -823,13 +827,13 @@ void Internal::flip_backbone_literals (Sweeper &sweeper) { bool Internal::sweep_extract_fixed (Sweeper &sweeper, int lit) { const int not_lit = -lit; stats.sweep_solved_backbone++; - kitten_assume_signed (citten, not_lit); + cadical_kitten_assume_signed (citten, not_lit); int res = sweep_solve (); if (!res) { stats.sweep_unknown_backbone++; return false; } - assert (res == 20); + CADICAL_assert (res == 20); LOG ("sweep unit %d", lit); save_add_clear_core (sweeper); stats.sweep_unsat_backbone++; @@ -838,10 +842,10 @@ bool Internal::sweep_extract_fixed (Sweeper &sweeper, int lit) { bool Internal::sweep_backbone_candidate (Sweeper &sweeper, int lit) { LOG ("trying backbone candidate %d", lit); - signed char value = kitten_fixed_signed (citten, lit); + signed char value = cadical_kitten_fixed_signed (citten, lit); if (value) { stats.sweep_fixed_backbone++; - assert (value > 0); + CADICAL_assert (value > 0); if (val (lit) <= 0) { return sweep_extract_fixed (sweeper, lit); } else @@ -849,7 +853,7 @@ bool Internal::sweep_backbone_candidate (Sweeper &sweeper, int lit) { return false; } - int res = kitten_status (citten); + int res = cadical_kitten_status (citten); if (res != 10) { LOG ("not flipping due to status %d != 10", res); } @@ -864,7 +868,7 @@ bool Internal::sweep_backbone_candidate (Sweeper &sweeper, int lit) { LOG ("flipping %d failed", lit); const int not_lit = -lit; stats.sweep_solved_backbone++; - kitten_assume_signed (citten, not_lit); + cadical_kitten_assume_signed (citten, not_lit); res = sweep_solve (); if (res == 10) { LOG ("sweeping backbone candidate %d failed", lit); @@ -876,7 +880,7 @@ bool Internal::sweep_backbone_candidate (Sweeper &sweeper, int lit) { if (res == 20) { LOG ("sweep unit %d", lit); save_add_clear_core (sweeper); - assert (val (lit)); + CADICAL_assert (val (lit)); stats.sweep_unsat_backbone++; return true; } @@ -894,11 +898,11 @@ bool Internal::sweep_backbone_candidate (Sweeper &sweeper, int lit) { // int64_t Internal::add_sweep_binary (sweep_proof_clause pc, int lit, int other) { - assert (!unsat); + CADICAL_assert (!unsat); if (unsat) return 0; // sanity check, should be fuzzed - assert (!val (lit) && !val (other)); + CADICAL_assert (!val (lit) && !val (other)); if (val (lit) || val (other)) return 0; @@ -940,28 +944,28 @@ bool Internal::scheduled_variable (Sweeper &sweeper, int idx) { } void Internal::schedule_inner (Sweeper &sweeper, int idx) { - assert (idx); + CADICAL_assert (idx); if (!active (idx)) return; const int next = sweeper.next[idx]; if (next != 0) { LOG ("rescheduling inner %d as last", idx); const unsigned prev = sweeper.prev[idx]; - assert (sweeper.prev[next] == idx); + CADICAL_assert (sweeper.prev[next] == idx); sweeper.prev[next] = prev; if (prev == 0) { - assert (sweeper.first == idx); + CADICAL_assert (sweeper.first == idx); sweeper.first = next; } else { - assert (sweeper.next[prev] == idx); + CADICAL_assert (sweeper.next[prev] == idx); sweeper.next[prev] = next; } const unsigned last = sweeper.last; if (last == 0) { - assert (sweeper.first == 0); + CADICAL_assert (sweeper.first == 0); sweeper.first = idx; } else { - assert (sweeper.next[last] == 0); + CADICAL_assert (sweeper.next[last] == 0); sweeper.next[last] = idx; } sweeper.prev[idx] = last; @@ -971,13 +975,13 @@ void Internal::schedule_inner (Sweeper &sweeper, int idx) { LOG ("scheduling inner %d as last", idx); const unsigned last = sweeper.last; if (last == 0) { - assert (sweeper.first == 0); + CADICAL_assert (sweeper.first == 0); sweeper.first = idx; } else { - assert (sweeper.next[last] == 0); + CADICAL_assert (sweeper.next[last] == 0); sweeper.next[last] = idx; } - assert (sweeper.next[idx] == 0); + CADICAL_assert (sweeper.next[idx] == 0); sweeper.prev[idx] = last; sweeper.last = idx; } else @@ -985,17 +989,17 @@ void Internal::schedule_inner (Sweeper &sweeper, int idx) { } void Internal::schedule_outer (Sweeper &sweeper, int idx) { - assert (!scheduled_variable (sweeper, idx)); - assert (active (idx)); + CADICAL_assert (!scheduled_variable (sweeper, idx)); + CADICAL_assert (active (idx)); const int first = sweeper.first; if (first == 0) { - assert (sweeper.last == 0); + CADICAL_assert (sweeper.last == 0); sweeper.last = idx; } else { - assert (sweeper.prev[first] == 0); + CADICAL_assert (sweeper.prev[first] == 0); sweeper.prev[first] = idx; } - assert (sweeper.prev[idx] == 0); + CADICAL_assert (sweeper.prev[idx] == 0); sweeper.next[idx] = first; sweeper.first = idx; LOG ("scheduling outer %d as first", idx); @@ -1007,16 +1011,16 @@ int Internal::next_scheduled (Sweeper &sweeper) { LOG ("no more scheduled variables left"); return 0; } - assert (res > 0); + CADICAL_assert (res > 0); LOG ("dequeuing next scheduled %d", res); const unsigned prev = sweeper.prev[res]; - assert (sweeper.next[res] == 0); + CADICAL_assert (sweeper.next[res] == 0); sweeper.prev[res] = 0; if (prev == 0) { - assert (sweeper.first == res); + CADICAL_assert (sweeper.first == res); sweeper.first = 0; } else { - assert (sweeper.next[prev] == res); + CADICAL_assert (sweeper.next[prev] == res); sweeper.next[prev] = 0; } sweeper.last = prev; @@ -1027,7 +1031,7 @@ void Internal::sweep_substitute_lrat (Clause *c, int64_t id) { if (!lrat) return; for (const auto &lit : *c) { - assert (val (lit) <= 0); + CADICAL_assert (val (lit) <= 0); if (val (lit) < 0) { int64_t id = unit_id (-lit); lrat_chain.push_back (id); @@ -1054,11 +1058,11 @@ void Internal::substitute_connected_clauses (Sweeper &sweeper, int lit, return; LOG ("substituting %d with %d in all irredundant clauses", lit, repr); - assert (lit != repr); - assert (lit != -repr); + CADICAL_assert (lit != repr); + CADICAL_assert (lit != -repr); - assert (active (lit)); - assert (active (repr)); + CADICAL_assert (active (lit)); + CADICAL_assert (active (repr)); uint64_t &ticks = sweeper.current_ticks; @@ -1074,25 +1078,25 @@ void Internal::substitute_connected_clauses (Sweeper &sweeper, int lit, ticks++; if (c->garbage) continue; - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); bool satisfied = false; bool repr_already_watched = false; const int not_repr = -repr; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG bool found = false; #endif for (const auto &other : *c) { if (other == lit) { -#ifndef NDEBUG - assert (!found); +#ifndef CADICAL_NDEBUG + CADICAL_assert (!found); found = true; #endif clause.push_back (repr); continue; } - assert (other != -lit); + CADICAL_assert (other != -lit); if (other == repr) { - assert (!repr_already_watched); + CADICAL_assert (!repr_already_watched); repr_already_watched = true; continue; } @@ -1115,12 +1119,12 @@ void Internal::substitute_connected_clauses (Sweeper &sweeper, int lit, sweep_update_noccs (c); continue; } - assert (found); + CADICAL_assert (found); const unsigned new_size = clause.size (); sweep_substitute_lrat (c, id); if (new_size == 0) { LOG (c, "substituted empty clause"); - assert (!unsat); + CADICAL_assert (!unsat); learn_empty_clause (); break; } @@ -1136,7 +1140,7 @@ void Internal::substitute_connected_clauses (Sweeper &sweeper, int lit, stats.sweep_units++; break; } - assert (c->size >= 2); + CADICAL_assert (c->size >= 2); if (!c->redundant) mark_removed (c); uint64_t new_id = ++clause_id; @@ -1179,7 +1183,7 @@ void Internal::sweep_substitute_new_equivalences (Sweeper &sweeper) { return; unsigned count = 0; - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); for (const auto &sb : sweeper.binaries) { count++; @@ -1190,7 +1194,7 @@ void Internal::sweep_substitute_new_equivalences (Sweeper &sweeper) { } else { substitute_connected_clauses (sweeper, -lit, other, sb.id); } - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); if (val (lit) < 0) { if (lrat) { const int64_t lid = unit_id (-lit); @@ -1218,7 +1222,7 @@ void Internal::sweep_substitute_new_equivalences (Sweeper &sweeper) { } assign_unit (lit); } else - assert (val (lit) > 0); + CADICAL_assert (val (lit) > 0); } lrat_chain.clear (); delete_sweep_binary (sb); @@ -1230,19 +1234,19 @@ void Internal::sweep_substitute_new_equivalences (Sweeper &sweeper) { } count = 0; } - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); } sweeper.binaries.clear (); } void Internal::sweep_remove (Sweeper &sweeper, int lit) { - assert (sweeper.reprs[lit] != lit); + CADICAL_assert (sweeper.reprs[lit] != lit); vector<int> &partition = sweeper.partition; const auto begin_partition = partition.begin (); auto p = begin_partition; const auto end_partition = partition.end (); for (; *p != lit; p++) - assert (p + 1 != end_partition); + CADICAL_assert (p + 1 != end_partition); auto begin_class = p; while (begin_class != begin_partition && begin_class[-1] != 0) begin_class--; @@ -1252,7 +1256,7 @@ void Internal::sweep_remove (Sweeper &sweeper, int lit) { const unsigned size = end_class - begin_class; LOG ("removing non-representative %d from equivalence class of size %u", lit, size); - assert (size > 1); + CADICAL_assert (size > 1); auto q = begin_class; if (size == 2) { LOG ("completely squashing equivalence class of %d", lit); @@ -1270,8 +1274,8 @@ void Internal::flip_partition_literals (Sweeper &sweeper) { const unsigned max_rounds = opts.sweepfliprounds; if (!max_rounds) return; - assert (sweeper.partition.size ()); - if (kitten_status (citten) != 10) + CADICAL_assert (sweeper.partition.size ()); + if (cadical_kitten_status (citten) != 10) return; #ifdef LOGGING unsigned total_flipped = 0; @@ -1286,17 +1290,17 @@ void Internal::flip_partition_literals (Sweeper &sweeper) { const auto end = sweeper.partition.end (); while (src != end) { auto end_src = src; - while (assert (end_src != end), *end_src != 0) + while (CADICAL_assert (end_src != end), *end_src != 0) end_src++; unsigned size = end_src - src; - assert (size > 1); + CADICAL_assert (size > 1); auto q = dst; for (auto p = src; p != end_src; p++) { const int lit = *p; if (limit_hit) { *q++ = lit; continue; - } else if (kitten_ticks_limit_hit (sweeper, "partition flipping")) { + } else if (cadical_kitten_ticks_limit_hit (sweeper, "partition flipping")) { *q++ = lit; limit_hit = true; continue; @@ -1325,7 +1329,7 @@ void Internal::flip_partition_literals (Sweeper &sweeper) { LOG ("flipped %u equivalence candidates in round %u", flipped, round); if (terminated_asynchronously ()) break; - if (kitten_ticks_limit_hit (sweeper, "partition flipping")) + if (cadical_kitten_ticks_limit_hit (sweeper, "partition flipping")) break; if (refine) sweep_refine (sweeper); @@ -1339,11 +1343,11 @@ bool Internal::sweep_equivalence_candidates (Sweeper &sweeper, int lit, LOG ("trying equivalence candidates %d = %d", lit, other); const auto begin = sweeper.partition.begin (); auto const end = sweeper.partition.end (); - assert (begin + 3 <= end); - assert (end[-3] == lit); - assert (end[-2] == other); + CADICAL_assert (begin + 3 <= end); + CADICAL_assert (end[-3] == lit); + CADICAL_assert (end[-2] == other); const int third = (end - begin == 3) ? 0 : end[-4]; - int res = kitten_status (citten); + int res = cadical_kitten_status (citten); if (res == 10) { stats.sweep_flip_equivalences++; if (sweep_flip (lit)) { @@ -1406,8 +1410,8 @@ bool Internal::sweep_equivalence_candidates (Sweeper &sweeper, int lit, const int not_other = -other; const int not_lit = -lit; LOG ("flipping %d and %d both failed", lit, other); - kitten_assume_signed (citten, not_lit); - kitten_assume_signed (citten, other); + cadical_kitten_assume_signed (citten, not_lit); + cadical_kitten_assume_signed (citten, other); stats.sweep_solved_equivalences++; res = sweep_solve (); if (res == 10) { @@ -1427,8 +1431,8 @@ bool Internal::sweep_equivalence_candidates (Sweeper &sweeper, int lit, save_core (sweeper, 0); - kitten_assume_signed (citten, lit); - kitten_assume_signed (citten, not_other); + cadical_kitten_assume_signed (citten, lit); + cadical_kitten_assume_signed (citten, not_other); res = sweep_solve (); stats.sweep_solved_equivalences++; if (res == 10) { @@ -1446,7 +1450,7 @@ bool Internal::sweep_equivalence_candidates (Sweeper &sweeper, int lit, return false; } - assert (res == 20); + CADICAL_assert (res == 20); stats.sweep_unsat_equivalences++; LOG ("second sweeping implication %d <- %d succeeded too", other, lit); @@ -1455,15 +1459,15 @@ bool Internal::sweep_equivalence_candidates (Sweeper &sweeper, int lit, LOG ("sweep equivalence %d = %d", lit, other); - // If kitten behaves as expected, the two binaries of the equivalence + // If cadical_kitten behaves as expected, the two binaries of the equivalence // should be stored at sweeper.core[i].back () for i in {0, 1}. // We pick the smaller absolute valued literal as representative and // store the equivalence . add_core (sweeper, 0); add_core (sweeper, 1); if (!val (lit) && !val (other)) { - assert (sweeper.core[0].size ()); - assert (sweeper.core[1].size ()); + CADICAL_assert (sweeper.core[0].size ()); + CADICAL_assert (sweeper.core[1].size ()); stats.sweep_equivalences++; sweep_binary bin1; sweep_binary bin2; @@ -1508,22 +1512,22 @@ bool Internal::sweep_equivalence_candidates (Sweeper &sweeper, int lit, } const char *Internal::sweep_variable (Sweeper &sweeper, int idx) { - assert (!unsat); + CADICAL_assert (!unsat); if (!active (idx)) return "inactive variable"; const int start = idx; if (sweeper.reprs[start] != start) return "non-representative variable"; - assert (sweeper.vars.empty ()); - assert (sweeper.clauses.empty ()); - assert (sweeper.backbone.empty ()); - assert (sweeper.partition.empty ()); - assert (!sweeper.encoded); + CADICAL_assert (sweeper.vars.empty ()); + CADICAL_assert (sweeper.clauses.empty ()); + CADICAL_assert (sweeper.backbone.empty ()); + CADICAL_assert (sweeper.partition.empty ()); + CADICAL_assert (!sweeper.encoded); stats.sweep_variables++; LOG ("sweeping %d", idx); - assert (!val (start)); + CADICAL_assert (!val (start)); LOG ("starting sweeping[0]"); add_literal_to_environment (sweeper, 0, start); LOG ("finished sweeping[0]"); @@ -1559,7 +1563,7 @@ const char *Internal::sweep_variable (Sweeper &sweeper, int idx) { if (opts.sweeprand && choices > 1) { const unsigned swaps = sweeper.random.pick_int (0, choices - 1); if (swaps) { - assert (expand + swaps < sweeper.vars.size ()); + CADICAL_assert (expand + swaps < sweeper.vars.size ()); swap (sweeper.vars[expand], sweeper.vars[expand + swaps]); } } @@ -1602,14 +1606,14 @@ const char *Internal::sweep_variable (Sweeper &sweeper, int idx) { LOG ("sub-solver returns '%d'", res); if (res == 10) { init_backbone_and_partition (sweeper); -#ifndef QUIET +#ifndef CADICAL_QUIET uint64_t units = stats.sweep_units; uint64_t solved = stats.sweep_solved; #endif START (sweepbackbone); while (sweeper.backbone.size ()) { if (unsat || terminated_asynchronously () || - kitten_ticks_limit_hit (sweeper, "backbone refinement")) { + cadical_kitten_ticks_limit_hit (sweeper, "backbone refinement")) { limit_reached = true; STOP_SWEEP_BACKBONE: STOP (sweepbackbone); @@ -1617,7 +1621,7 @@ const char *Internal::sweep_variable (Sweeper &sweeper, int idx) { } flip_backbone_literals (sweeper); if (terminated_asynchronously () || - kitten_ticks_limit_hit (sweeper, "backbone refinement")) { + cadical_kitten_ticks_limit_hit (sweeper, "backbone refinement")) { limit_reached = true; goto STOP_SWEEP_BACKBONE; } @@ -1631,7 +1635,7 @@ const char *Internal::sweep_variable (Sweeper &sweeper, int idx) { success = true; } STOP (sweepbackbone); -#ifndef QUIET +#ifndef CADICAL_QUIET units = stats.sweep_units - units; solved = stats.sweep_solved - solved; #endif @@ -1639,15 +1643,15 @@ const char *Internal::sweep_variable (Sweeper &sweeper, int idx) { "complete swept variable %d backbone with %" PRIu64 " units in %" PRIu64 " solver calls", externalize (idx), units, solved); - assert (sweeper.backbone.empty ()); -#ifndef QUIET + CADICAL_assert (sweeper.backbone.empty ()); +#ifndef CADICAL_QUIET uint64_t equivalences = stats.sweep_equivalences; solved = stats.sweep_solved; #endif START (sweepequivalences); while (sweeper.partition.size ()) { if (unsat || terminated_asynchronously () || - kitten_ticks_limit_hit (sweeper, "partition refinement")) { + cadical_kitten_ticks_limit_hit (sweeper, "partition refinement")) { limit_reached = true; STOP_SWEEP_EQUIVALENCES: STOP (sweepequivalences); @@ -1655,7 +1659,7 @@ const char *Internal::sweep_variable (Sweeper &sweeper, int idx) { } flip_partition_literals (sweeper); if (terminated_asynchronously () || - kitten_ticks_limit_hit (sweeper, "backbone refinement")) { + cadical_kitten_ticks_limit_hit (sweeper, "backbone refinement")) { limit_reached = true; goto STOP_SWEEP_EQUIVALENCES; } @@ -1663,7 +1667,7 @@ const char *Internal::sweep_variable (Sweeper &sweeper, int idx) { break; if (sweeper.partition.size () > 2) { const auto end = sweeper.partition.end (); - assert (end[-1] == 0); + CADICAL_assert (end[-1] == 0); int lit = end[-3]; int other = end[-2]; if (sweep_equivalence_candidates (sweeper, lit, other)) @@ -1672,7 +1676,7 @@ const char *Internal::sweep_variable (Sweeper &sweeper, int idx) { sweeper.partition.clear (); } STOP (sweepequivalences); -#ifndef QUIET +#ifndef CADICAL_QUIET equivalences = stats.sweep_equivalences - equivalences; solved = stats.sweep_solved - solved; if (equivalences) @@ -1698,7 +1702,7 @@ DONE: return "unsuccessfully without reaching limit"; else if (success && !limit_reached) return "successfully without reaching limit"; - assert (!success && limit_reached); + CADICAL_assert (!success && limit_reached); return "unsuccessfully and reached limit"; } @@ -1709,8 +1713,8 @@ struct sweep_candidate { struct rank_sweep_candidate { bool operator() (sweep_candidate a, sweep_candidate b) const { - assert (a.rank && b.rank); - assert (a.idx > 0 && b.idx > 0); + CADICAL_assert (a.rank && b.rank); + CADICAL_assert (a.idx > 0 && b.idx > 0); if (a.rank < b.rank) return true; if (b.rank < a.rank) @@ -1759,7 +1763,7 @@ unsigned Internal::schedule_all_other_not_scheduled_yet (Sweeper &sweeper) { fresh.push_back (cand); } const size_t size = fresh.size (); - assert (size <= UINT_MAX); + CADICAL_assert (size <= UINT_MAX); sort (fresh.begin (), fresh.end (), rank_sweep_candidate ()); for (auto &cand : fresh) schedule_outer (sweeper, cand.idx); @@ -1807,7 +1811,7 @@ void Internal::mark_incomplete (Sweeper &sweeper) { } } sweep_incomplete = true; -#ifndef QUIET +#ifndef CADICAL_QUIET VERBOSE (2, "marked %u scheduled sweeping variables as incomplete", marked); #else @@ -1820,7 +1824,7 @@ unsigned Internal::schedule_sweeping (Sweeper &sweeper) { const unsigned fresh = schedule_all_other_not_scheduled_yet (sweeper); const unsigned scheduled = fresh + rescheduled; const unsigned incomplete = incomplete_variables (); -#ifndef QUIET +#ifndef CADICAL_QUIET PHASE ("sweep", stats.sweep, "scheduled %u variables %.0f%% " "(%u rescheduled %.0f%%, %u incomplete %.0f%%)", @@ -1829,7 +1833,7 @@ unsigned Internal::schedule_sweeping (Sweeper &sweeper) { percent (incomplete, scheduled)); #endif if (incomplete) - assert (sweep_incomplete); + CADICAL_assert (sweep_incomplete); else { if (sweep_incomplete) stats.sweep_completed++; @@ -1840,17 +1844,17 @@ unsigned Internal::schedule_sweeping (Sweeper &sweeper) { void Internal::unschedule_sweeping (Sweeper &sweeper, unsigned swept, unsigned scheduled) { -#ifdef QUIET +#ifdef CADICAL_QUIET (void) scheduled, (void) swept; #endif - assert (sweep_schedule.empty ()); - assert (sweep_incomplete); + CADICAL_assert (sweep_schedule.empty ()); + CADICAL_assert (sweep_incomplete); for (all_scheduled (idx)) if (active (idx)) { sweep_schedule.push_back (idx); LOG ("untried scheduled %d", idx); } -#ifndef QUIET +#ifndef CADICAL_QUIET const unsigned retained = sweep_schedule.size (); #endif VERBOSE (3, "retained %u variables %.0f%% to be swept next time", @@ -1883,7 +1887,7 @@ bool Internal::sweep () { SET_EFFORT_LIMIT (tickslimit, sweep, !opts.sweepcomplete); delaying_sweep.bumpreasons.unbypass_delay (); - assert (!level); + CADICAL_assert (!level); START_SIMPLIFIER (sweep, SWEEP); stats.sweep++; uint64_t equivalences = stats.sweep_equivalences; @@ -1893,7 +1897,7 @@ bool Internal::sweep () { sweeper.limit.ticks = INT64_MAX; else sweeper.limit.ticks = tickslimit - stats.ticks.sweep; - sweep_set_kitten_ticks_limit (sweeper); + sweep_set_cadical_kitten_ticks_limit (sweeper); const unsigned scheduled = schedule_sweeping (sweeper); uint64_t swept = 0, limit = 10; for (;;) { @@ -1901,13 +1905,13 @@ bool Internal::sweep () { break; if (terminated_asynchronously ()) break; - if (kitten_ticks_limit_hit (sweeper, "sweeping loop")) + if (cadical_kitten_ticks_limit_hit (sweeper, "sweeping loop")) break; int idx = next_scheduled (sweeper); if (idx == 0) break; flags (idx).sweep = false; -#ifndef QUIET +#ifndef CADICAL_QUIET const char *res = #endif sweep_variable (sweeper, idx); @@ -1951,3 +1955,5 @@ bool Internal::sweep () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/terminal.cpp b/src/sat/cadical/cadical_terminal.cpp similarity index 78% rename from src/sat/cadical/terminal.cpp rename to src/sat/cadical/cadical_terminal.cpp index 919d81c9a..c8aeea2ab 100644 --- a/src/sat/cadical/terminal.cpp +++ b/src/sat/cadical/cadical_terminal.cpp @@ -1,11 +1,20 @@ +#include "global.h" + #include "internal.hpp" +#ifdef WIN32 +#include <io.h> +#define isatty _isatty +#endif + +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { Terminal::Terminal (FILE *f) : file (f), reset_on_exit (false) { - assert (file); + CADICAL_assert (file); int fd = fileno (f); - assert (fd == 1 || fd == 2); + CADICAL_assert (fd == 1 || fd == 2); use_colors = connected = isatty (fd); } @@ -36,3 +45,5 @@ Terminal tout (stdout); Terminal terr (stderr); } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/ternary.cpp b/src/sat/cadical/cadical_ternary.cpp similarity index 92% rename from src/sat/cadical/ternary.cpp rename to src/sat/cadical/cadical_ternary.cpp index 259404fd8..9f99e2279 100644 --- a/src/sat/cadical/ternary.cpp +++ b/src/sat/cadical/cadical_ternary.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -23,9 +27,9 @@ namespace CaDiCaL { // literals already exists. bool Internal::ternary_find_binary_clause (int a, int b) { - assert (occurring ()); - assert (active (a)); - assert (active (b)); + CADICAL_assert (occurring ()); + CADICAL_assert (active (a)); + CADICAL_assert (active (b)); size_t s = occs (a).size (); size_t t = occs (b).size (); int lit = s < t ? a : b; @@ -49,10 +53,10 @@ bool Internal::ternary_find_binary_clause (int a, int b) { // literals already exists or is subsumed by an existing binary clause. bool Internal::ternary_find_ternary_clause (int a, int b, int c) { - assert (occurring ()); - assert (active (a)); - assert (active (b)); - assert (active (c)); + CADICAL_assert (occurring ()); + CADICAL_assert (active (a)); + CADICAL_assert (active (b)); + CADICAL_assert (active (c)); size_t r = occs (a).size (); size_t s = occs (b).size (); size_t t = occs (c).size (); @@ -79,7 +83,7 @@ bool Internal::ternary_find_ternary_clause (int a, int b, int c) { if (lits[0] == c && lits[1] == b) return true; } else { - assert (d->size == 3); + CADICAL_assert (d->size == 3); if (lits[0] == a && lits[1] == b && lits[2] == c) return true; if (lits[0] == a && lits[1] == c && lits[2] == b) @@ -111,9 +115,9 @@ bool Internal::hyper_ternary_resolve (Clause *c, int pivot, Clause *d) { LOG (c, "1st antecedent"); LOG (d, "2nd antecedent"); stats.ternres++; - assert (c->size == 3); - assert (d->size == 3); - assert (clause.empty ()); + CADICAL_assert (c->size == 3); + CADICAL_assert (d->size == 3); + CADICAL_assert (clause.empty ()); for (const auto &lit : *c) if (lit != pivot) clause.push_back (lit); @@ -160,7 +164,7 @@ void Internal::ternary_lit (int pivot, int64_t &steps, int64_t &htrs) { if (c->garbage) continue; if (c->size != 3) { - assert (c->size == 2); + CADICAL_assert (c->size == 2); continue; } if (--steps < 0) @@ -182,7 +186,7 @@ void Internal::ternary_lit (int pivot, int64_t &steps, int64_t &htrs) { if (d->garbage) continue; if (d->size != 3) { - assert (d->size == 2); + CADICAL_assert (d->size == 2); continue; } for (const auto &lit : *d) @@ -192,13 +196,13 @@ void Internal::ternary_lit (int pivot, int64_t &steps, int64_t &htrs) { } if (assigned) continue; - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); htrs--; if (hyper_ternary_resolve (c, pivot, d)) { size_t size = clause.size (); bool red = (size == 3 || (c->redundant && d->redundant)); if (lrat) { - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); lrat_chain.push_back (c->id); lrat_chain.push_back (d->id); } @@ -218,7 +222,7 @@ void Internal::ternary_lit (int pivot, int64_t &steps, int64_t &htrs) { stats.htrs2++; break; } else { - assert (r->size == 3); + CADICAL_assert (r->size == 3); stats.htrs3++; } } else { @@ -235,8 +239,8 @@ void Internal::ternary_lit (int pivot, int64_t &steps, int64_t &htrs) { // number of positive and negative occurrence. void Internal::ternary_idx (int idx, int64_t &steps, int64_t &htrs) { - assert (0 < idx); - assert (idx <= max_var); + CADICAL_assert (0 < idx); + CADICAL_assert (idx <= max_var); steps -= 3; if (!active (idx)) return; @@ -266,9 +270,9 @@ void Internal::ternary_idx (int idx, int64_t &steps, int64_t &htrs) { bool Internal::ternary_round (int64_t &steps_limit, int64_t &htrs_limit) { - assert (!unsat); + CADICAL_assert (!unsat); -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t bincon = 0; int64_t terncon = 0; #endif @@ -294,14 +298,14 @@ bool Internal::ternary_round (int64_t &steps_limit, int64_t &htrs_limit) { if (assigned) continue; if (c->size == 2) { -#ifndef QUIET +#ifndef CADICAL_QUIET bincon++; #endif } else { - assert (c->size == 3); + CADICAL_assert (c->size == 3); if (!marked) continue; -#ifndef QUIET +#ifndef CADICAL_QUIET terncon++; #endif } @@ -347,7 +351,7 @@ bool Internal::ternary_round (int64_t &steps_limit, int64_t &htrs_limit) { PHASE ("ternary", stats.ternary, "completed hyper ternary resolution"); reset_occs (); - assert (!unsat); + CADICAL_assert (!unsat); return remain; // Are there variables that should be tried again? } @@ -373,9 +377,9 @@ bool Internal::ternary () { START_SIMPLIFIER (ternary, TERNARY); stats.ternary++; - assert (!level); + CADICAL_assert (!level); - assert (!unsat); + CADICAL_assert (!unsat); if (watching ()) reset_watches (); @@ -430,8 +434,8 @@ bool Internal::ternary () { break; } - assert (!occurring ()); - assert (!unsat); + CADICAL_assert (!occurring ()); + CADICAL_assert (!unsat); init_watches (); connect_watches (); if (!propagate ()) { @@ -448,3 +452,5 @@ bool Internal::ternary () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/tier.cpp b/src/sat/cadical/cadical_tier.cpp similarity index 89% rename from src/sat/cadical/tier.cpp rename to src/sat/cadical/cadical_tier.cpp index 8112d81ac..92faf507f 100644 --- a/src/sat/cadical/tier.cpp +++ b/src/sat/cadical/cadical_tier.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { void Internal::recompute_tier () { @@ -12,11 +16,11 @@ void Internal::recompute_tier () { lim.recompute_tier = stats.conflicts + delta; LOG ("rescheduling in %zd at %zd (conflicts at %zd)", delta, lim.recompute_tier, stats.conflicts); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG uint64_t total_used = 0; for (auto u : stats.used[stable]) total_used += u; - assert (total_used == stats.bump_used[stable]); + CADICAL_assert (total_used == stats.bump_used[stable]); #endif if (!stats.bump_used[stable]) { @@ -50,4 +54,6 @@ void Internal::recompute_tier () { stable ? "stable" : "focused"); } -} // namespace CaDiCaL \ No newline at end of file +} // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/transred.cpp b/src/sat/cadical/cadical_transred.cpp similarity index 94% rename from src/sat/cadical/transred.cpp rename to src/sat/cadical/cadical_transred.cpp index f9dc0cafa..82a961dee 100644 --- a/src/sat/cadical/transred.cpp +++ b/src/sat/cadical/cadical_transred.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { // Implement transitive reduction in the binary implication graph. This is @@ -18,8 +22,8 @@ void Internal::transred () { if (!stats.current.redundant && !stats.current.irredundant) return; - assert (opts.transred); - assert (!level); + CADICAL_assert (opts.transred); + CADICAL_assert (!level); START_SIMPLIFIER (transred, TRANSRED); stats.transreds++; @@ -127,7 +131,7 @@ void Internal::transred () { // const bool irredundant = !c->redundant; - assert (work.empty ()); + CADICAL_assert (work.empty ()); mark (src); work.push_back (src); LOG ("transred assign %d", src); @@ -137,13 +141,13 @@ void Internal::transred () { size_t j = 0; // 'propagated' in BFS - assert (lrat_chain.empty ()); - assert (mini_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); + CADICAL_assert (mini_chain.empty ()); vector<int> parents; while (!transitive && !failed && j < work.size ()) { const int lit = work[j++]; - assert (marked (lit) > 0); + CADICAL_assert (marked (lit) > 0); LOG ("transred propagating %d", lit); propagations++; const Watches &ws = watches (-lit); @@ -198,7 +202,7 @@ void Internal::transred () { const int lit = work.back (); work.pop_back (); if (lrat && failed && !work.empty ()) { - assert (!parents.empty () && !mini_chain.empty ()); + CADICAL_assert (!parents.empty () && !mini_chain.empty ()); LOG ("transred LRAT current lit %d next pos %d next neg %d", lit, next_pos, next_neg); if (lit == failed_lit || lit == next_pos) { @@ -214,7 +218,7 @@ void Internal::transred () { unmark (lit); } mini_chain.clear (); - assert (mini_chain.empty ()); + CADICAL_assert (mini_chain.empty ()); if (lrat && failed) { reverse (lrat_chain.begin (), lrat_chain.end ()); } @@ -251,3 +255,5 @@ void Internal::transred () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/unstable.cpp b/src/sat/cadical/cadical_unstable.cpp similarity index 67% rename from src/sat/cadical/unstable.cpp rename to src/sat/cadical/cadical_unstable.cpp index 5b23164be..c3162d70a 100644 --- a/src/sat/cadical/unstable.cpp +++ b/src/sat/cadical/cadical_unstable.cpp @@ -1,10 +1,14 @@ +#include "global.h" + #ifdef PROFILE_MODE #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { bool Internal::propagate_unstable () { - assert (!stable); + CADICAL_assert (!stable); START (propunstable); bool res = propagate (); STOP (propunstable); @@ -12,18 +16,23 @@ bool Internal::propagate_unstable () { } void Internal::analyze_unstable () { - assert (!stable); + CADICAL_assert (!stable); START (analyzeunstable); analyze (); STOP (analyzeunstable); } int Internal::decide_unstable () { - assert (!stable); + CADICAL_assert (!stable); return decide (); } }; // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END + #else +ABC_NAMESPACE_IMPL_START int unstable_if_no_profile_mode; +ABC_NAMESPACE_IMPL_END #endif diff --git a/src/sat/cadical/util.cpp b/src/sat/cadical/cadical_util.cpp similarity index 94% rename from src/sat/cadical/util.cpp rename to src/sat/cadical/cadical_util.cpp index 5b7c52f30..fd9b5b29d 100644 --- a/src/sat/cadical/util.cpp +++ b/src/sat/cadical/cadical_util.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -46,7 +50,7 @@ bool parse_int_str (const char *val_str, int &val) { } else if (ch) return false; - assert (exponent <= 10); + CADICAL_assert (exponent <= 10); int64_t val64 = mantissa; for (int i = 0; i < exponent; i++) val64 *= 10; @@ -60,8 +64,8 @@ bool parse_int_str (const char *val_str, int &val) { val64 = INT_MAX; } - assert (INT_MIN <= val64); - assert (val64 <= INT_MAX); + CADICAL_assert (INT_MIN <= val64); + CADICAL_assert (val64 <= INT_MAX); val = val64; } @@ -127,3 +131,5 @@ uint64_t hash_string (const char *str) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/var.cpp b/src/sat/cadical/cadical_var.cpp similarity index 63% rename from src/sat/cadical/var.cpp rename to src/sat/cadical/cadical_var.cpp index 0baa0b291..e2348da7b 100644 --- a/src/sat/cadical/var.cpp +++ b/src/sat/cadical/cadical_var.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { void Internal::reset_subsume_bits () { @@ -9,7 +13,7 @@ void Internal::reset_subsume_bits () { } void Internal::check_var_stats () { -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG int64_t fixed = 0, eliminated = 0, substituted = 0, pure = 0, unused = 0; for (auto idx : vars) { Flags &f = flags (idx); @@ -26,14 +30,16 @@ void Internal::check_var_stats () { if (f.pure ()) pure++; } - assert (stats.now.fixed == fixed); - assert (stats.now.eliminated == eliminated); - assert (stats.now.substituted == substituted); - assert (stats.now.pure == pure); + CADICAL_assert (stats.now.fixed == fixed); + CADICAL_assert (stats.now.eliminated == eliminated); + CADICAL_assert (stats.now.substituted == substituted); + CADICAL_assert (stats.now.pure == pure); int64_t inactive = unused + fixed + eliminated + substituted + pure; - assert (stats.inactive == inactive); - assert (max_var == stats.active + stats.inactive); + CADICAL_assert (stats.inactive == inactive); + CADICAL_assert (max_var == stats.active + stats.inactive); #endif } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/veripbtracer.cpp b/src/sat/cadical/cadical_veripbtracer.cpp similarity index 92% rename from src/sat/cadical/veripbtracer.cpp rename to src/sat/cadical/cadical_veripbtracer.cpp index 3a8c817f7..b1f7c95a7 100644 --- a/src/sat/cadical/veripbtracer.cpp +++ b/src/sat/cadical/cadical_veripbtracer.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -8,7 +12,7 @@ VeripbTracer::VeripbTracer (Internal *i, File *f, bool b, bool a, bool c) : internal (i), file (f), with_antecedents (a), checked_deletions (c), num_clauses (0), size_clauses (0), clauses (0), last_hash (0), last_id (0), last_clause (0) -#ifndef QUIET +#ifndef CADICAL_QUIET , added (0), deleted (0) #endif @@ -22,10 +26,10 @@ VeripbTracer::VeripbTracer (Internal *i, File *f, bool b, bool a, bool c) uint64_t nonce = random.next (); if (!(nonce & 1)) nonce++; - assert (nonce), assert (nonce & 1); + CADICAL_assert (nonce), CADICAL_assert (nonce & 1); nonces[n] = nonce; } -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG binary = b; #else (void) b; @@ -50,7 +54,7 @@ VeripbTracer::~VeripbTracer () { /*------------------------------------------------------------------------*/ void VeripbTracer::enlarge_clauses () { - assert (num_clauses == size_clauses); + CADICAL_assert (num_clauses == size_clauses); const uint64_t new_size_clauses = size_clauses ? 2 * size_clauses : 1; LOG ("VeriPB Tracer enlarging clauses from %" PRIu64 " to %" PRIu64, (uint64_t) size_clauses, (uint64_t) new_size_clauses); @@ -81,13 +85,13 @@ HashId *VeripbTracer::new_clause () { } void VeripbTracer::delete_clause (HashId *c) { - assert (c); + CADICAL_assert (c); num_clauses--; delete c; } uint64_t VeripbTracer::reduce_hash (uint64_t hash, uint64_t size) { - assert (size > 0); + CADICAL_assert (size > 0); unsigned shift = 32; uint64_t res = hash; while ((((uint64_t) 1) << shift) > size) { @@ -95,12 +99,12 @@ uint64_t VeripbTracer::reduce_hash (uint64_t hash, uint64_t size) { shift >>= 1; } res &= size - 1; - assert (res < size); + CADICAL_assert (res < size); return res; } uint64_t VeripbTracer::compute_hash (const int64_t id) { - assert (id > 0); + CADICAL_assert (id > 0); unsigned j = id % num_nonces; // Dont know if this is a good uint64_t tmp = nonces[j] * (uint64_t) id; // hash funktion or even better return last_hash = tmp; // than just using id. @@ -129,7 +133,7 @@ bool VeripbTracer::find_and_delete (const int64_t id) { } if (!c) return false; - assert (c && res); + CADICAL_assert (c && res); *res = c->next; delete_clause (c); return true; @@ -147,15 +151,15 @@ void VeripbTracer::insert () { /*------------------------------------------------------------------------*/ inline void VeripbTracer::put_binary_zero () { - assert (binary); - assert (file); + CADICAL_assert (binary); + CADICAL_assert (file); file->put ((unsigned char) 0); } inline void VeripbTracer::put_binary_lit (int lit) { - assert (binary); - assert (file); - assert (lit != INT_MIN); + CADICAL_assert (binary); + CADICAL_assert (file); + CADICAL_assert (lit != INT_MIN); unsigned x = 2 * abs (lit) + (lit < 0); unsigned char ch; while (x & ~0x7f) { @@ -168,8 +172,8 @@ inline void VeripbTracer::put_binary_lit (int lit) { } inline void VeripbTracer::put_binary_id (int64_t id, bool can_be_negative) { - assert (binary); - assert (file); + CADICAL_assert (binary); + CADICAL_assert (file); uint64_t x = abs (id); if (can_be_negative) { x = 2 * x + (id < 0); @@ -300,7 +304,7 @@ void VeripbTracer::add_derived_clause (int64_t id, bool redundant, veripb_add_derived_clause (id, redundant, clause, chain); else veripb_add_derived_clause (id, redundant, clause); -#ifndef QUIET +#ifndef CADICAL_QUIET added++; #endif } @@ -311,7 +315,7 @@ void VeripbTracer::delete_clause (int64_t id, bool redundant, return; LOG ("VERIPB TRACER tracing deletion of clause[%" PRId64 "]", id); veripb_delete_clause (id, redundant); -#ifndef QUIET +#ifndef CADICAL_QUIET deleted++; #endif } @@ -349,7 +353,7 @@ void VeripbTracer::strengthen (int64_t id) { bool VeripbTracer::closed () { return file->closed (); } -#ifndef QUIET +#ifndef CADICAL_QUIET void VeripbTracer::print_statistics () { // TODO complete @@ -366,9 +370,9 @@ void VeripbTracer::print_statistics () { #endif void VeripbTracer::close (bool print) { - assert (!closed ()); + CADICAL_assert (!closed ()); file->close (); -#ifndef QUIET +#ifndef CADICAL_QUIET if (print) { MSG ("VeriPB proof file '%s' closed", file->name ()); print_statistics (); @@ -379,9 +383,9 @@ void VeripbTracer::close (bool print) { } void VeripbTracer::flush (bool print) { - assert (!closed ()); + CADICAL_assert (!closed ()); file->flush (); -#ifndef QUIET +#ifndef CADICAL_QUIET if (print) { MSG ("VeriPB proof file '%s' flushed", file->name ()); print_statistics (); @@ -392,3 +396,5 @@ void VeripbTracer::flush (bool print) { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/version.cpp b/src/sat/cadical/cadical_version.cpp similarity index 89% rename from src/sat/cadical/version.cpp rename to src/sat/cadical/cadical_version.cpp index c96540fb8..a0cceef63 100644 --- a/src/sat/cadical/version.cpp +++ b/src/sat/cadical/cadical_version.cpp @@ -1,10 +1,12 @@ +#include "global.h" + /*------------------------------------------------------------------------*/ // To simplify the build process without 'make', you can disable the // generation of 'build.hpp' through '../scripts/make-build-header.sh' by -// defining '-DNBUILD'. Then we try to guess part of the configuration. +// defining '-DCADICAL_NBUILD'. Then we try to guess part of the configuration. -#ifndef NBUILD +#ifndef CADICAL_NBUILD #if __GNUC__ > 4 #if __has_include(<build.hpp>) #include "build.hpp" @@ -12,7 +14,7 @@ #else #include "build.hpp" #endif // __GNUC > 4 -#endif // NBUILD +#endif // CADICAL_NBUILD /*------------------------------------------------------------------------*/ @@ -22,11 +24,11 @@ // the file '../VERSION' with '../scripts/update-version.sh'. The standard // build process relies on 'VERSION' to be defined in 'build.hpp'. -#ifdef NBUILD +#ifdef CADICAL_NBUILD #ifndef VERSION #define VERSION "2.2.0-rc1" #endif // VERSION -#endif // NBUILD +#endif // CADICAL_NBUILD /*------------------------------------------------------------------------*/ @@ -40,7 +42,7 @@ static const char *AFFILIATIONS = /*------------------------------------------------------------------------*/ -// Again if we do not have 'NBUILD' or if something during configuration is +// Again if we do not have 'CADICAL_NBUILD' or if something during configuration is // broken we still want to be able to compile the solver. In this case we // then try our best to figure out 'COMPILER' and 'DATE', but for // 'IDENTIFIER' and 'FLAGS' we can only use the '0' string, which marks @@ -92,6 +94,8 @@ static const char *AFFILIATIONS = #include "version.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { const char *version () { return VERSION; } @@ -105,3 +109,5 @@ const char *date () { return DATE; } const char *flags () { return FLAGS; } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/vivify.cpp b/src/sat/cadical/cadical_vivify.cpp similarity index 92% rename from src/sat/cadical/vivify.cpp rename to src/sat/cadical/cadical_vivify.cpp index cf6ec1fb8..8d99c2932 100644 --- a/src/sat/cadical/vivify.cpp +++ b/src/sat/cadical/cadical_vivify.cpp @@ -1,3 +1,5 @@ +#include "global.h" + #include "vivify.hpp" #include "internal.hpp" #include "util.hpp" @@ -5,6 +7,8 @@ #include <limits> #include <utility> +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -50,26 +54,26 @@ inline void Internal::vivify_subsume_clause (Clause *subsuming, Clause *subsumed) { stats.subsumed++; stats.vivifysubs++; -#ifndef NDEBUG - assert (subsuming); - assert (subsumed); - assert (subsuming != subsumed); - assert (!subsumed->garbage); +#ifndef CADICAL_NDEBUG + CADICAL_assert (subsuming); + CADICAL_assert (subsumed); + CADICAL_assert (subsuming != subsumed); + CADICAL_assert (!subsumed->garbage); // size after removeing units; int real_size_subsuming = 0, real_size_subsumed = 0; for (auto lit : *subsuming) { if (!val (lit) || var (lit).level) ++real_size_subsuming; else - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); } for (auto lit : *subsumed) { if (!val (lit) || var (lit).level) ++real_size_subsumed; else - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); } - assert (real_size_subsuming <= real_size_subsumed); + CADICAL_assert (real_size_subsuming <= real_size_subsumed); #endif LOG (subsumed, "subsumed"); if (subsumed->redundant) { @@ -80,7 +84,7 @@ inline void Internal::vivify_subsume_clause (Clause *subsuming, ++stats.vivifysubirr; } if (subsuming->garbage) { - assert (subsuming->size == 2); + CADICAL_assert (subsuming->size == 2); LOG (subsuming, "binary subsuming clause was already deleted, so undeleting"); subsuming->garbage = false; @@ -104,9 +108,9 @@ inline void Internal::vivify_subsume_clause (Clause *subsuming, stats.current.irredundant++; stats.added.irredundant++; stats.irrlits += subsuming->size; - assert (stats.current.redundant > 0); + CADICAL_assert (stats.current.redundant > 0); stats.current.redundant--; - assert (stats.added.redundant > 0); + CADICAL_assert (stats.added.redundant > 0); stats.added.redundant--; // ... and keep 'stats.added.total'. } @@ -117,12 +121,12 @@ inline void Internal::demote_clause (Clause *c) { stats.subsumed++; stats.vivifydemote++; LOG (c, "demoting"); - assert (!c->redundant); + CADICAL_assert (!c->redundant); mark_removed (c); c->redundant = true; - assert (stats.current.irredundant > 0); + CADICAL_assert (stats.current.irredundant > 0); stats.current.irredundant--; - assert (stats.added.irredundant > 0); + CADICAL_assert (stats.added.irredundant > 0); stats.added.irredundant--; stats.irrlits -= c->size; stats.current.redundant++; @@ -142,12 +146,12 @@ inline void Internal::demote_clause (Clause *c) { inline void Internal::vivify_assign (int lit, Clause *reason) { require_mode (VIVIFY); const int idx = vidx (lit); - assert (!vals[idx]); - assert (!flags (idx).eliminated () || !reason); + CADICAL_assert (!vals[idx]); + CADICAL_assert (!flags (idx).eliminated () || !reason); Var &v = var (idx); v.level = level; // required to reuse decisions v.trail = (int) trail.size (); // used in 'vivify_better_watch' - assert ((int) num_assigned < max_var); + CADICAL_assert ((int) num_assigned < max_var); num_assigned++; v.reason = level ? reason : 0; // for conflict analysis if (!level) @@ -155,8 +159,8 @@ inline void Internal::vivify_assign (int lit, Clause *reason) { const signed char tmp = sign (lit); vals[idx] = tmp; vals[-idx] = -tmp; - assert (val (lit) > 0); - assert (val (-lit) < 0); + CADICAL_assert (val (lit) > 0); + CADICAL_assert (val (-lit) < 0); trail.push_back (lit); LOG (reason, "vivify assign %d", lit); } @@ -168,8 +172,8 @@ void Internal::vivify_assume (int lit) { level++; control.push_back (Level (lit, trail.size ())); LOG ("vivify decide %d", lit); - assert (level > 0); - assert (propagated == trail.size ()); + CADICAL_assert (level > 0); + CADICAL_assert (propagated == trail.size ()); vivify_assign (lit, 0); } @@ -179,7 +183,7 @@ void Internal::vivify_assume (int lit) { bool Internal::vivify_propagate (int64_t &ticks) { require_mode (VIVIFY); - assert (!unsat); + CADICAL_assert (!unsat); START (propagate); int64_t before = propagated2 = propagated; for (;;) { @@ -239,12 +243,12 @@ bool Internal::vivify_propagate (int64_t &ticks) { k++; if (v < 0) { k = lits + 2; - assert (w.clause->pos <= size); + CADICAL_assert (w.clause->pos <= size); while (k != middle && (v = val (r = *k)) < 0) k++; } w.clause->pos = k - lits; - assert (lits + 2 <= k), assert (k <= w.clause->end ()); + CADICAL_assert (lits + 2 <= k), CADICAL_assert (k <= w.clause->end ()); if (v > 0) j[-1].blit = r; else if (!v) { @@ -261,7 +265,7 @@ bool Internal::vivify_propagate (int64_t &ticks) { continue; } ticks++; - assert (v < 0); + CADICAL_assert (v < 0); vivify_chain_for_units (other, w.clause); vivify_assign (other, w.clause); lrat_chain.clear (); @@ -270,8 +274,8 @@ bool Internal::vivify_propagate (int64_t &ticks) { LOG ("ignoring conflict due to clause to vivify"); continue; } - assert (u < 0); - assert (v < 0); + CADICAL_assert (u < 0); + CADICAL_assert (v < 0); conflict = w.clause; break; } @@ -395,7 +399,7 @@ struct vivify_clause_later { // Among redundant clauses (in redundant mode) prefer small glue. // if (a->redundant) { - assert (b->redundant); + CADICAL_assert (b->redundant); if (a->glue > b->glue) return true; if (a->glue < b->glue) @@ -474,9 +478,9 @@ void Internal::flush_vivification_schedule (std::vector<Clause *> &schedule, if (k == eop) { LOG (c, "found subsumed"); LOG (prev, "subsuming"); - assert (!c->garbage); - assert (!prev->garbage); - assert (c->redundant || !prev->redundant); + CADICAL_assert (!c->garbage); + CADICAL_assert (!prev->garbage); + CADICAL_assert (c->redundant || !prev->redundant); mark_garbage (c); subsumed++; j--; @@ -494,7 +498,7 @@ void Internal::flush_vivification_schedule (std::vector<Clause *> &schedule, schedule.resize (j - schedule.begin ()); shrink_vector (schedule); } else - assert (j == end); + CADICAL_assert (j == end); } /*------------------------------------------------------------------------*/ @@ -513,7 +517,7 @@ bool Internal::consider_to_vivify_clause (Clause *c) { return false; if (!c->redundant) return true; - assert (c->redundant); + CADICAL_assert (c->redundant); // likely_to_be_kept_clause is too aggressive at removing tier-3 clauses return true; @@ -550,14 +554,14 @@ struct vivify_better_watch { void Internal::vivify_strengthen (Clause *c) { - assert (!clause.empty ()); + CADICAL_assert (!clause.empty ()); if (clause.size () == 1) { backtrack_without_updating_phases (); const int unit = clause[0]; LOG (c, "vivification shrunken to unit %d", unit); - assert (!val (unit)); + CADICAL_assert (!val (unit)); assign_unit (unit); // lrat_chain.clear (); done in search_assign stats.vivifyunits++; @@ -590,12 +594,12 @@ void Internal::vivify_strengthen (Clause *c) { new_level = level1 - 1; } - assert (new_level >= 0); + CADICAL_assert (new_level >= 0); if (new_level < level) backtrack (new_level); - assert (val (lit0) >= 0); - assert (val (lit1) >= 0 || (val (lit0) > 0 && val (lit1) < 0 && + CADICAL_assert (val (lit0) >= 0); + CADICAL_assert (val (lit1) >= 0 || (val (lit0) > 0 && val (lit1) < 0 && var (lit0).level <= var (lit1).level)); Clause *d = new_clause_as (c); @@ -631,12 +635,12 @@ void Internal::vivify_sort_watched (Clause *c) { new_level = level1 - 1; } - assert (new_level >= 0); + CADICAL_assert (new_level >= 0); if (new_level < level) backtrack (new_level); - assert (val (lit0) >= 0); - assert (val (lit1) >= 0 || (val (lit0) > 0 && val (lit1) < 0 && + 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. @@ -652,8 +656,8 @@ void Internal::vivify_analyze (Clause *start, bool &subsumes, const auto &t = &trail; // normal trail, so next_trail is wrong int i = t->size (); // Start at end-of-trail. Clause *reason = start; - assert (reason); - assert (!trail.empty ()); + CADICAL_assert (reason); + CADICAL_assert (!trail.empty ()); int uip = trail.back (); bool mark_implied = (implied); @@ -692,7 +696,7 @@ void Internal::vivify_analyze (Clause *start, bool &subsumes, continue; } - assert (val (other)); + CADICAL_assert (val (other)); if (f.seen) continue; LOG ("pushing lit %d", other); @@ -704,7 +708,7 @@ void Internal::vivify_analyze (Clause *start, bool &subsumes, promote_clause (start, new_glue); } if (subsumes) { - assert (reason); + CADICAL_assert (reason); LOG (reason, "clause found subsuming"); LOG (candidate, "clause found subsumed"); *subsuming = reason; @@ -718,7 +722,7 @@ void Internal::vivify_analyze (Clause *start, bool &subsumes, uip = 0; while (!uip && i > 0) { - assert (i > 0); + CADICAL_assert (i > 0); const int lit = (*t)[--i]; if (!var (lit).level) continue; @@ -739,16 +743,16 @@ void Internal::vivify_analyze (Clause *start, bool &subsumes, void Internal::vivify_deduce (Clause *candidate, Clause *conflict, int implied, Clause **subsuming, bool &redundant) { - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); bool subsumes; Clause *reason; - assert (clause.empty ()); + CADICAL_assert (clause.empty ()); if (implied) { reason = candidate; mark2 (candidate); const int not_implied = -implied; - assert (var (not_implied).level); + CADICAL_assert (var (not_implied).level); Flags &f = flags (not_implied); f.seen = true; LOG ("pushing implied lit %d", not_implied); @@ -756,8 +760,8 @@ void Internal::vivify_deduce (Clause *candidate, Clause *conflict, clause.push_back (implied); } else { reason = (conflict ? conflict : candidate); - assert (reason); - assert (!reason->garbage); + CADICAL_assert (reason); + CADICAL_assert (!reason->garbage); mark2 (candidate); subsumes = (candidate != reason); redundant = reason->redundant; @@ -767,7 +771,7 @@ void Internal::vivify_deduce (Clause *candidate, Clause *conflict, for (auto lit : *reason) { const Var &v = var (lit); Flags &f = flags (lit); - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); if (!v.level) { if (!lrat) continue; @@ -783,7 +787,7 @@ void Internal::vivify_deduce (Clause *candidate, Clause *conflict, analyzed.push_back (lit); continue; } - assert (v.level); + CADICAL_assert (v.level); if (!marked2 (lit)) { LOG ("lit %d is not marked", lit); subsumes = false; @@ -798,8 +802,8 @@ void Internal::vivify_deduce (Clause *candidate, Clause *conflict, promote_clause (reason, new_glue); } if (subsumes) { - assert (candidate != reason); -#ifndef NDEBUG + CADICAL_assert (candidate != reason); +#ifndef CADICAL_NDEBUG int nonfalse_reason = 0; for (auto lit : *reason) if (!fixed (lit)) @@ -810,7 +814,7 @@ void Internal::vivify_deduce (Clause *candidate, Clause *conflict, if (!fixed (lit)) ++nonfalse_candidate; - assert (nonfalse_reason <= nonfalse_candidate); + CADICAL_assert (nonfalse_reason <= nonfalse_candidate); #endif LOG (candidate, "vivify subsumed 0"); LOG (reason, "vivify subsuming 0"); @@ -826,7 +830,7 @@ void Internal::vivify_deduce (Clause *candidate, Clause *conflict, redundant); unmark (candidate); if (subsumes) { - assert (*subsuming); + CADICAL_assert (*subsuming); LOG (candidate, "vivify subsumed"); LOG (*subsuming, "vivify subsuming"); if (lrat) @@ -854,7 +858,7 @@ bool Internal::vivify_shrinkable (const std::vector<int> &sorted, return true; } } else { - assert (value < 0); + CADICAL_assert (value < 0); const Var &v = var (lit); const Flags &f = flags (lit); if (!v.level) @@ -885,7 +889,7 @@ inline void Internal::vivify_increment_stats (const Vivifier &vivifier) { ++stats.vivifystred3; break; default: - assert (vivifier.tier == Vivify_Mode::IRREDUNDANT); + CADICAL_assert (vivifier.tier == Vivify_Mode::IRREDUNDANT); ++stats.vivifystrirr; break; } @@ -902,11 +906,11 @@ bool Internal::vivify_instantiate ( conflict = nullptr; const int lit = sorted.back (); LOG ("vivify instantiation"); - assert (!var (lit).reason); - assert (var (lit).level); - assert (val (lit)); + CADICAL_assert (!var (lit).reason); + CADICAL_assert (var (lit).level); + CADICAL_assert (val (lit)); backtrack (level - 1); - assert (val (lit) == 0); + CADICAL_assert (val (lit) == 0); stats.vivifydecs++; vivify_assume (lit); bool ok = vivify_propagate (ticks); @@ -916,7 +920,7 @@ bool Internal::vivify_instantiate ( // strengthen clause if (lrat) { clear_analyzed_literals (); - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); vivify_build_lrat (0, c, lrat_stack); vivify_build_lrat (0, conflict, lrat_stack); clear_analyzed_literals (); @@ -928,7 +932,7 @@ bool Internal::vivify_instantiate ( strengthen_clause (c, remove); vivify_sort_watched (c); watch_clause (c); - assert (!conflict); + CADICAL_assert (!conflict); return true; } else { LOG ("vivify instantiation failed"); @@ -942,13 +946,13 @@ bool Internal::vivify_instantiate ( bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { - assert (c->size > 2); // see (NO-BINARY) below - assert (analyzed.empty ()); + CADICAL_assert (c->size > 2); // see (NO-BINARY) below + CADICAL_assert (analyzed.empty ()); c->vivify = false; // mark as checked / tried c->vivified = true; // and globally remember - assert (!c->garbage); + CADICAL_assert (!c->garbage); auto &lrat_stack = vivifier.lrat_stack; auto &ticks = vivifier.ticks; @@ -973,7 +977,7 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { sorted.push_back (lit); } - assert (sorted.size () > 1); + CADICAL_assert (sorted.size () > 1); if (sorted.size () == 2) { LOG ("skipping actual binary"); return false; @@ -1027,7 +1031,7 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { } if (forced) { LOG ("clause is reason forcing %d", forced); - assert (var (forced).level); + CADICAL_assert (var (forced).level); backtrack_without_updating_phases (var (forced).level - 1); } @@ -1039,7 +1043,7 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { int l = 1; // This is the decision level we want to reuse. for (const auto &lit : sorted) { - assert (!fixed (lit)); + CADICAL_assert (!fixed (lit)); const int decision = control[l].decision; if (-lit == decision) { LOG ("reusing decision %d at decision level %d", decision, l); @@ -1103,25 +1107,25 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { if (tmp) { // literal already assigned const Var &v = var (lit); - assert (v.level); + CADICAL_assert (v.level); if (!v.reason) { LOG ("skipping decision %d", lit); continue; } if (tmp < 0) { - assert (v.level); + CADICAL_assert (v.level); LOG ("literal %d is already false and can be removed", lit); continue; } - assert (tmp > 0); + CADICAL_assert (tmp > 0); LOG ("subsumed since literal %d already true", lit); subsume = lit; // will be able to subsume candidate '@5' break; } - assert (!tmp); + CADICAL_assert (!tmp); stats.vivifydecs++; vivify_assume (-lit); @@ -1150,7 +1154,7 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { Clause *subsuming = nullptr; bool redundant = false; const int level_after_assumptions = level; - assert (level_after_assumptions); + CADICAL_assert (level_after_assumptions); vivify_deduce (c, conflict, subsume, &subsuming, redundant); bool res; @@ -1166,7 +1170,7 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { } if (subsuming) { - assert (c != subsuming); + CADICAL_assert (c != subsuming); LOG (c, "deleting subsumed clause"); if (c->redundant && subsuming->redundant && c->glue < subsuming->glue) { promote_clause (c, c->glue); @@ -1203,19 +1207,19 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { } else if (subsume) { LOG (c, "no vivification instantiation with implied literal %d", subsume); - assert (!c->redundant); - assert (redundant); + CADICAL_assert (!c->redundant); + CADICAL_assert (redundant); res = false; ++stats.vivifyimplied; } else { - assert (level > 2); - assert ((size_t) level == sorted.size ()); + CADICAL_assert (level > 2); + CADICAL_assert ((size_t) level == sorted.size ()); LOG (c, "vivification failed on"); lrat_chain.clear (); - assert (!subsume); + CADICAL_assert (!subsume); if (!subsume && opts.vivifyinst) { res = vivify_instantiate (sorted, c, lrat_stack, ticks); - assert (!conflict); + CADICAL_assert (!conflict); } else { LOG ("cannot apply instantiation"); res = false; @@ -1253,7 +1257,7 @@ bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) { void Internal::vivify_build_lrat ( int lit, Clause *reason, std::vector<std::tuple<int, Clause *, bool>> &stack) { - assert (stack.empty ()); + CADICAL_assert (stack.empty ()); stack.push_back ({lit, reason, false}); while (!stack.empty ()) { int lit; @@ -1271,8 +1275,8 @@ void Internal::vivify_build_lrat ( if (lit && reason) { Flags &f = flags (lit); f.seen = true; - analyzed.push_back (lit); // assert (val (other) < 0); - assert (flags (lit).seen); + analyzed.push_back (lit); // CADICAL_assert (val (other) < 0); + CADICAL_assert (flags (lit).seen); } continue; } else @@ -1309,11 +1313,11 @@ inline void Internal::vivify_chain_for_units (int lit, Clause *reason) { // equivalence if (opts.chrono && assignment_level (lit, reason)) return; if (level) return; // not decision level 0 - assert (lrat_chain.empty ()); + CADICAL_assert (lrat_chain.empty ()); for (auto &reason_lit : *reason) { if (lit == reason_lit) continue; - assert (val (reason_lit)); + CADICAL_assert (val (reason_lit)); const int signed_reason_lit = val (reason_lit) * reason_lit; int64_t id = unit_id (signed_reason_lit); lrat_chain.push_back (id); @@ -1344,7 +1348,7 @@ vivify_ref create_ref (Internal *internal, Clause *c) { } { const int64_t lit_count = internal->noccs (lit); - assert (lit_count); + CADICAL_assert (lit_count); LOG ("checking literal %d with %zd occurrences", lit, lit_count); if (lit_count <= best_count) continue; @@ -1353,9 +1357,9 @@ vivify_ref create_ref (Internal *internal, Clause *c) { } CONTINUE_WITH_NEXT_LITERAL:; } - assert (best); - assert (best_count); - assert (best_count < UINT32_MAX); + CADICAL_assert (best); + CADICAL_assert (best_count); + CADICAL_assert (best_count < UINT32_MAX); ref.count[i] = ((uint64_t) best_count << 32) + (uint64_t) internal->vlit (best); LOG ("final count at position %d is %d - %d: %lu", i, best, best_count, @@ -1402,7 +1406,7 @@ void Internal::vivify_initialize (Vivifier &vivifier, int64_t &ticks) { // Disconnect all watches since we sort literals within clauses. // - assert (watching ()); + CADICAL_assert (watching ()); #if 0 clear_watches (); #endif @@ -1497,7 +1501,11 @@ inline std::vector<vivify_ref> ¤t_refs_schedule (Vivifier &vivifier) { return vivifier.refs_schedule_irred; break; } +#ifdef WIN32 + __assume(false); +#else __builtin_unreachable (); +#endif } inline std::vector<Clause *> ¤t_schedule (Vivifier &vivifier) { @@ -1515,13 +1523,17 @@ inline std::vector<Clause *> ¤t_schedule (Vivifier &vivifier) { return vivifier.schedule_irred; break; } +#ifdef WIN32 + __assume(false); +#else __builtin_unreachable (); +#endif } struct vivify_refcount_rank { int offset; vivify_refcount_rank (int j) : offset (j) { - assert (offset < COUNTREF_COUNTS); + CADICAL_assert (offset < COUNTREF_COUNTS); } typedef uint64_t Type; Type operator() (const vivify_ref &a) const { return a.count[offset]; } @@ -1530,7 +1542,7 @@ struct vivify_refcount_rank { struct vivify_refcount_smaller { int offset; vivify_refcount_smaller (int j) : offset (j) { - assert (offset < COUNTREF_COUNTS); + CADICAL_assert (offset < COUNTREF_COUNTS); } bool operator() (const vivify_ref &a, const vivify_ref &b) const { const auto s = vivify_refcount_rank (offset) (a); @@ -1576,7 +1588,7 @@ void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) { "starting %c vivification round ticks limit %" PRId64 "", vivifier.tag, ticks_limit); - assert (watching ()); + CADICAL_assert (watching ()); auto &refs_schedule = current_refs_schedule (vivifier); auto &schedule = current_schedule (vivifier); @@ -1645,7 +1657,7 @@ void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) { // Limit the number of propagations during vivification as in 'probe'. // const int64_t limit = ticks_limit - stats.ticks.vivify; - assert (limit >= 0); + CADICAL_assert (limit >= 0); // the clauses might still contain set literals, so propagation since the // beginning @@ -1770,7 +1782,7 @@ void set_vivifier_mode (Vivifier &vivifier, Vivify_Mode tier) { vivifier.tag = 'w'; break; default: - assert (tier == Vivify_Mode::IRREDUNDANT); + CADICAL_assert (tier == Vivify_Mode::IRREDUNDANT); vivifier.tag = 'x'; break; } @@ -1801,8 +1813,8 @@ bool Internal::vivify () { return false; if (level) backtrack (); - assert (opts.vivify); - assert (!level); + CADICAL_assert (opts.vivify); + CADICAL_assert (!level); SET_EFFORT_LIMIT (totallimit, vivify, true); @@ -1854,12 +1866,12 @@ bool Internal::vivify () { if (limit < stats.ticks.vivify) limit = stats.ticks.vivify; const double effort = (total * tier1effort) / sumeffort; - assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= + CADICAL_assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= limit); limit += effort; if (limit - shared_effort > stats.ticks.vivify) { limit -= shared_effort; - assert (limit >= 0); + CADICAL_assert (limit >= 0); vivify_round (vivifier, limit); } else { LOG ("building the schedule already used our entire ticks budget for " @@ -1874,12 +1886,12 @@ bool Internal::vivify () { if (limit < stats.ticks.vivify) limit = stats.ticks.vivify; const double effort = (total * tier2effort) / sumeffort; - assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= + CADICAL_assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= limit); limit += effort; if (limit - shared_effort > stats.ticks.vivify) { limit -= shared_effort; - assert (limit >= 0); + CADICAL_assert (limit >= 0); set_vivifier_mode (vivifier, Vivify_Mode::TIER2); vivify_round (vivifier, limit); } else { @@ -1893,12 +1905,12 @@ bool Internal::vivify () { if (limit < stats.ticks.vivify) limit = stats.ticks.vivify; const double effort = (total * tier3effort) / sumeffort; - assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= + CADICAL_assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= limit); limit += effort; if (limit - shared_effort > stats.ticks.vivify) { limit -= shared_effort; - assert (limit >= 0); + CADICAL_assert (limit >= 0); set_vivifier_mode (vivifier, Vivify_Mode::TIER3); vivify_round (vivifier, limit); } else { @@ -1912,12 +1924,12 @@ bool Internal::vivify () { if (limit < stats.ticks.vivify) limit = stats.ticks.vivify; const double effort = (total * irreffort) / sumeffort; - assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= + CADICAL_assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >= limit); limit += effort; if (limit - shared_effort > stats.ticks.vivify) { limit -= shared_effort; - assert (limit >= 0); + CADICAL_assert (limit >= 0); set_vivifier_mode (vivifier, Vivify_Mode::IRREDUNDANT); const int old = stats.vivifystrirr; const int old_tried = stats.vivifychecks; @@ -1946,3 +1958,5 @@ bool Internal::vivify () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/walk.cpp b/src/sat/cadical/cadical_walk.cpp similarity index 93% rename from src/sat/cadical/walk.cpp rename to src/sat/cadical/cadical_walk.cpp index ac671a7ad..13b1bf9f4 100644 --- a/src/sat/cadical/walk.cpp +++ b/src/sat/cadical/cadical_walk.cpp @@ -1,5 +1,9 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -52,9 +56,9 @@ inline static double fitcbval (double size) { 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; - assert (dx); + CADICAL_assert (dx); const double res = dy * (size - x1) / dx + y1; - assert (res > 0); + CADICAL_assert (res > 0); return res; } @@ -71,7 +75,7 @@ Walker::Walker (Internal *i, double size, int64_t l) // const bool use_size_based_cb = (internal->stats.walk.count & 1); const double cb = use_size_based_cb ? fitcbval (size) : 2.0; - assert (cb); + CADICAL_assert (cb); const double base = 1 / cb; // scores are 'base^0,base^1,base^2,... double next = 1; @@ -95,7 +99,7 @@ inline double Walker::score (unsigned i) { Clause *Internal::walk_pick_clause (Walker &walker) { require_mode (WALK); - assert (!walker.broken.empty ()); + CADICAL_assert (!walker.broken.empty ()); int64_t size = walker.broken.size (); if (size > INT_MAX) size = INT_MAX; @@ -113,12 +117,12 @@ Clause *Internal::walk_pick_clause (Walker &walker) { unsigned Internal::walk_break_value (int lit) { require_mode (WALK); - assert (val (lit) > 0); + CADICAL_assert (val (lit) > 0); unsigned res = 0; // The computed break-count of 'lit'. for (auto &w : watches (lit)) { - assert (w.blit != lit); + CADICAL_assert (w.blit != lit); if (val (w.blit) > 0) continue; if (w.binary ()) { @@ -127,7 +131,7 @@ unsigned Internal::walk_break_value (int lit) { } Clause *c = w.clause; - assert (lit == c->literals[0]); + CADICAL_assert (lit == c->literals[0]); // Now try to find a second satisfied literal starting at 'literals[1]' // shifting all the traversed literals to right by one position in order @@ -186,16 +190,16 @@ unsigned Internal::walk_break_value (int lit) { int Internal::walk_pick_lit (Walker &walker, Clause *c) { LOG ("picking literal by break-count"); - assert (walker.scores.empty ()); + CADICAL_assert (walker.scores.empty ()); double sum = 0; int64_t propagations = 0; for (const auto lit : *c) { - assert (active (lit)); + CADICAL_assert (active (lit)); if (var (lit).level == 1) { LOG ("skipping assumption %d for scoring", -lit); continue; } - assert (active (lit)); + CADICAL_assert (active (lit)); propagations++; unsigned tmp = walk_break_value (-lit); double score = walker.score (tmp); @@ -204,10 +208,10 @@ int Internal::walk_pick_lit (Walker &walker, Clause *c) { sum += score; } LOG ("scored %zd literals", walker.scores.size ()); - assert (!walker.scores.empty ()); + CADICAL_assert (!walker.scores.empty ()); walker.propagations += propagations; stats.propagations.walk += propagations; - assert (walker.scores.size () <= (size_t) c->size); + CADICAL_assert (walker.scores.size () <= (size_t) c->size); const double lim = sum * walker.random.generate_double (); LOG ("score sum %g limit %g", sum, lim); const auto end = c->end (); @@ -215,7 +219,7 @@ int Internal::walk_pick_lit (Walker &walker, Clause *c) { auto j = walker.scores.begin (); int res; for (;;) { - assert (i != end); + CADICAL_assert (i != end); res = *i++; if (var (res).level > 1) break; @@ -241,14 +245,14 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { require_mode (WALK); LOG ("flipping assign %d", lit); - assert (val (lit) < 0); + CADICAL_assert (val (lit) < 0); // First flip the literal value. // const int tmp = sign (lit); const int idx = abs (lit); set_val (idx, tmp); - assert (val (lit) > 0); + CADICAL_assert (val (lit) > 0); // Then remove 'c' and all other now satisfied (made) clauses. { @@ -288,12 +292,12 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { const int size = d->size; for (int i = 0; i < size; i++) { const int other = literals[i]; - assert (active (other)); + CADICAL_assert (active (other)); literals[i] = prev; prev = other; if (other == lit) break; - assert (val (other) < 0); + CADICAL_assert (val (other) < 0); } // If 'lit' is in 'd' then move it to the front to watch it. @@ -348,11 +352,11 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { Clause *d = w.clause; LOG (d, "unwatch %d in", -lit); int *literals = d->literals, replacement = 0, prev = -lit; - assert (literals[0] == -lit); + CADICAL_assert (literals[0] == -lit); const int size = d->size; for (int i = 1; i < size; i++) { const int other = literals[i]; - assert (active (other)); + CADICAL_assert (active (other)); literals[i] = prev; // shift all to right prev = other; const signed char tmp = val (other); @@ -364,7 +368,7 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { if (replacement) { literals[1] = -lit; literals[0] = replacement; - assert (-lit != replacement); + CADICAL_assert (-lit != replacement); watch_literal (replacement, -lit, d); } else { for (int i = size - 1; i > 0; i--) { // undo shift @@ -372,7 +376,7 @@ void Internal::walk_flip_lit (Walker &walker, int lit) { literals[i] = prev; prev = other; } - assert (literals[0] == -lit); + CADICAL_assert (literals[0] == -lit); LOG (d, "broken"); walker.broken.push_back (d); #ifdef LOGGING @@ -422,11 +426,11 @@ int Internal::walk_round (int64_t limit, bool prev) { if (last.collect.fixed < stats.all.fixed) garbage_collection (); -#ifndef QUIET +#ifndef CADICAL_QUIET // We want to see more messages during initial local search. // if (localsearching) { - assert (!force_phase_messages); + CADICAL_assert (!force_phase_messages); force_phase_messages = true; } #endif @@ -483,7 +487,7 @@ int Internal::walk_round (int64_t limit, bool prev) { const int idx = abs (lit); LOG ("initial assign %d to assumption phase", tmp < 0 ? -idx : idx); set_val (idx, tmp); - assert (level == 1); + CADICAL_assert (level == 1); var (idx).level = 1; } if (!failed) @@ -500,7 +504,7 @@ int Internal::walk_round (int64_t limit, bool prev) { continue; } if (vals[idx]) { - assert (var (idx).level == 1); + CADICAL_assert (var (idx).level == 1); LOG ("skipping assumed variable %d", idx); continue; } @@ -509,9 +513,9 @@ int Internal::walk_round (int64_t limit, bool prev) { tmp = phases.prev[idx]; if (!tmp) tmp = sign (decide_phase (idx, true)); - assert (tmp == 1 || tmp == -1); + CADICAL_assert (tmp == 1 || tmp == -1); set_val (idx, tmp); - assert (level == 2); + CADICAL_assert (level == 2); var (idx).level = 2; LOG ("initial assign %d to decision phase", tmp < 0 ? -idx : idx); } @@ -542,7 +546,7 @@ int Internal::walk_round (int64_t limit, bool prev) { // for (int i = 0; satisfied < 2 && i < size; i++) { const int lit = lits[i]; - assert (active (lit)); // Due to garbage collection. + CADICAL_assert (active (lit)); // Due to garbage collection. if (val (lit) > 0) { swap (lits[satisfied], lits[i]); if (!satisfied++) @@ -566,7 +570,7 @@ int Internal::walk_round (int64_t limit, bool prev) { watched++; #endif } else { - assert (satisfiable); // at least one non-assumed variable ... + CADICAL_assert (satisfiable); // at least one non-assumed variable ... LOG (c, "broken"); walker.broken.push_back (c); } @@ -599,12 +603,12 @@ int Internal::walk_round (int64_t limit, bool prev) { walk_save_minimum (walker); int64_t minimum = broken; -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t flips = 0; #endif while (!terminated_asynchronously () && !walker.broken.empty () && walker.propagations < walker.limit) { -#ifndef QUIET +#ifndef CADICAL_QUIET flips++; #endif stats.walk.flips++; @@ -673,15 +677,15 @@ int Internal::walk_round (int64_t limit, bool prev) { if (active (idx)) set_val (idx, 0); - assert (level == 2); + CADICAL_assert (level == 2); level = 0; clear_watches (); connect_watches (); -#ifndef QUIET +#ifndef CADICAL_QUIET if (localsearching) { - assert (force_phase_messages); + CADICAL_assert (force_phase_messages); force_phase_messages = false; } #endif @@ -702,3 +706,5 @@ void Internal::walk () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/watch.cpp b/src/sat/cadical/cadical_watch.cpp similarity index 90% rename from src/sat/cadical/watch.cpp rename to src/sat/cadical/cadical_watch.cpp index 2456866d3..84c51ef2c 100644 --- a/src/sat/cadical/watch.cpp +++ b/src/sat/cadical/cadical_watch.cpp @@ -1,9 +1,13 @@ +#include "global.h" + #include "internal.hpp" +ABC_NAMESPACE_IMPL_START + namespace CaDiCaL { void Internal::init_watches () { - assert (wtab.empty ()); + CADICAL_assert (wtab.empty ()); if (wtab.size () < 2 * vsize) wtab.resize (2 * vsize, Watches ()); LOG ("initialized watcher tables"); @@ -15,7 +19,7 @@ void Internal::clear_watches () { } void Internal::reset_watches () { - assert (!wtab.empty ()); + CADICAL_assert (!wtab.empty ()); erase_vector (wtab); LOG ("reset watcher tables"); } @@ -25,7 +29,7 @@ void Internal::reset_watches () { void Internal::connect_watches (bool irredundant_only) { START (connect); - assert (watching ()); + CADICAL_assert (watching ()); LOG ("watching all %sclauses", irredundant_only ? "irredundant " : ""); @@ -81,7 +85,7 @@ void Internal::connect_watches (bool irredundant_only) { void Internal::connect_binary_watches () { START (connect); - assert (watching ()); + CADICAL_assert (watching ()); LOG ("watching binary clauses"); @@ -97,7 +101,7 @@ void Internal::connect_binary_watches () { } void Internal::sort_watches () { - assert (watching ()); + CADICAL_assert (watching ()); LOG ("sorting watches"); Watches saved; for (auto lit : lits) { @@ -107,7 +111,7 @@ void Internal::sort_watches () { watch_iterator j = ws.begin (); const_watch_iterator i; - assert (saved.empty ()); + CADICAL_assert (saved.empty ()); for (i = j; i != end; i++) { const Watch w = *i; @@ -124,3 +128,5 @@ void Internal::sort_watches () { } } // namespace CaDiCaL + +ABC_NAMESPACE_IMPL_END diff --git a/src/sat/cadical/ccadical.h b/src/sat/cadical/ccadical.h index fb99e3b42..ec5292a79 100644 --- a/src/sat/cadical/ccadical.h +++ b/src/sat/cadical/ccadical.h @@ -1,10 +1,10 @@ #ifndef _ccadical_h_INCLUDED #define _ccadical_h_INCLUDED +#include "global.h" + /*------------------------------------------------------------------------*/ -#ifdef __cplusplus -extern "C" { -#endif +ABC_NAMESPACE_HEADER_START /*------------------------------------------------------------------------*/ #include <stdint.h> @@ -54,6 +54,11 @@ int ccadical_simplify (CCaDiCaL *); int ccadical_vars (CCaDiCaL *); int ccadical_reserve_difference (CCaDiCaL *, int number_of_vars); +// Extra + +void ccadical_reserve(CCaDiCaL *, int min_max_var); +int ccadical_is_inconsistent(CCaDiCaL *); + /*------------------------------------------------------------------------*/ // Support legacy names used before moving to more IPASIR conforming names. @@ -63,9 +68,7 @@ int ccadical_reserve_difference (CCaDiCaL *, int number_of_vars); #define ccadical_deref ccadical_val /*------------------------------------------------------------------------*/ -#ifdef __cplusplus -} -#endif +ABC_NAMESPACE_HEADER_END /*------------------------------------------------------------------------*/ #endif diff --git a/src/sat/cadical/checker.hpp b/src/sat/cadical/checker.hpp index fba087095..8c351e326 100644 --- a/src/sat/cadical/checker.hpp +++ b/src/sat/cadical/checker.hpp @@ -1,10 +1,14 @@ #ifndef _checker_hpp_INCLUDED #define _checker_hpp_INCLUDED +#include "global.h" + #include "tracer.hpp" // Alphabetically after 'checker'. #include <cstdint> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -169,4 +173,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/clause.hpp b/src/sat/cadical/clause.hpp index eed019cde..e5d0290f6 100644 --- a/src/sat/cadical/clause.hpp +++ b/src/sat/cadical/clause.hpp @@ -1,11 +1,15 @@ #ifndef _clause_hpp_INCLUDED #define _clause_hpp_INCLUDED +#include "global.h" + #include "util.hpp" #include <climits> #include <cstdint> #include <cstdlib> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -138,7 +142,7 @@ struct Clause { // of a clause is 8 bytes anyhow, we just allocate 8 byte aligned memory // all the time (even if allocated outside of the arena). // - assert (size > 1); + CADICAL_assert (size > 1); const size_t header_bytes = sizeof (Clause); const size_t actual_literal_bytes = size * sizeof (int); size_t combined_bytes = header_bytes + actual_literal_bytes; @@ -185,4 +189,6 @@ struct clause_lit_less_than { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/config.hpp b/src/sat/cadical/config.hpp index 5510ce0eb..d49b05c84 100644 --- a/src/sat/cadical/config.hpp +++ b/src/sat/cadical/config.hpp @@ -1,6 +1,10 @@ #ifndef _config_hpp_INCLUDED #define _config_hpp_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { class Options; @@ -17,4 +21,6 @@ struct Config { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/congruence.hpp b/src/sat/cadical/congruence.hpp index 5686238d8..e0acf9b00 100644 --- a/src/sat/cadical/congruence.hpp +++ b/src/sat/cadical/congruence.hpp @@ -1,6 +1,8 @@ #ifndef _congruenc_hpp_INCLUDED #define _congruenc_hpp_INCLUDED +#include "global.h" + #include <algorithm> #include <array> #include <cassert> @@ -17,6 +19,8 @@ #include "util.hpp" #include "watch.hpp" +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { typedef int64_t LRAT_ID; @@ -91,15 +95,15 @@ struct lit_equivalence { Clause *first_clause; Clause *second_clause; void check_invariant () { - assert (second_clause); - assert (first_clause); - assert (std::find (begin (*first_clause), end (*first_clause), first) != + CADICAL_assert (second_clause); + CADICAL_assert (first_clause); + CADICAL_assert (std::find (begin (*first_clause), end (*first_clause), first) != end (*first_clause)); - assert (std::find (begin (*second_clause), end (*second_clause), + CADICAL_assert (std::find (begin (*second_clause), end (*second_clause), second) != end (*second_clause)); - assert (std::find (begin (*first_clause), end (*first_clause), + CADICAL_assert (std::find (begin (*first_clause), end (*first_clause), -second) != end (*first_clause)); - assert (std::find (begin (*second_clause), end (*second_clause), + CADICAL_assert (std::find (begin (*second_clause), end (*second_clause), -first) != end (*second_clause)); } lit_equivalence (int f, Clause *f_id, int s, Clause *s_id) @@ -711,4 +715,6 @@ struct Closure { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/contract.hpp b/src/sat/cadical/contract.hpp index 3e98867c0..4a3ba066a 100644 --- a/src/sat/cadical/contract.hpp +++ b/src/sat/cadical/contract.hpp @@ -1,8 +1,12 @@ #ifndef _contract_hpp_INCLUDED #define _contract_hpp_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + /*------------------------------------------------------------------------*/ -#ifndef NCONTRACTS +#ifndef CADICAL_NCONTRACTS /*------------------------------------------------------------------------*/ // If the user violates API contracts while calling functions declared in @@ -104,7 +108,7 @@ void require_solver_pointer_to_be_non_zero (const void *ptr, } while (0) /*------------------------------------------------------------------------*/ -#else // NCONTRACTS +#else // CADICAL_NCONTRACTS /*------------------------------------------------------------------------*/ #define REQUIRE(...) \ @@ -133,4 +137,6 @@ void require_solver_pointer_to_be_non_zero (const void *ptr, #endif /*------------------------------------------------------------------------*/ +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/cover.hpp b/src/sat/cadical/cover.hpp index 67e1bea77..47e6f1b82 100644 --- a/src/sat/cadical/cover.hpp +++ b/src/sat/cadical/cover.hpp @@ -1,6 +1,8 @@ #ifndef _cover_hpp_INCLUDED #define _cover_hpp_INCLUDED +#include "global.h" + /*------------------------------------------------------------------------*/ // This header only provides the 'COVER' macro for testing. It is unrelated @@ -10,8 +12,8 @@ /*------------------------------------------------------------------------*/ -// Coverage goal, used similar to 'assert' (but with flipped condition) and -// also included even if 'NDEBUG' is defined (in optimizing compilation). +// Coverage goal, used similar to 'CADICAL_assert' (but with flipped condition) and +// also included even if 'CADICAL_NDEBUG' is defined (in optimizing compilation). // // This should in essence not be used in production code. // diff --git a/src/sat/cadical/decompose.hpp b/src/sat/cadical/decompose.hpp index d62b6ba4a..9c93e8448 100644 --- a/src/sat/cadical/decompose.hpp +++ b/src/sat/cadical/decompose.hpp @@ -1,6 +1,10 @@ #ifndef _decompose_hpp_INCLUDED #define _decompose_hpp_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { // This implements Tarjan's algorithm for decomposing the binary implication @@ -20,4 +24,6 @@ struct DFS { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/delay.hpp b/src/sat/cadical/delay.hpp index e1e10ec82..9ee9a9234 100644 --- a/src/sat/cadical/delay.hpp +++ b/src/sat/cadical/delay.hpp @@ -1,9 +1,13 @@ #ifndef _delay_hpp_INCLUDED #define _delay_hpp_INCLUDED +#include "global.h" + #include <cstdint> #include <limits> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct Delay { unsigned count; @@ -35,4 +39,6 @@ struct Delay { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/drattracer.hpp b/src/sat/cadical/drattracer.hpp index 42e238838..8c2c023d5 100644 --- a/src/sat/cadical/drattracer.hpp +++ b/src/sat/cadical/drattracer.hpp @@ -1,8 +1,12 @@ #ifndef _drattracer_h_INCLUDED #define _drattracer_h_INCLUDED +#include "global.h" + #include "tracer.hpp" +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { class DratTracer : public FileTracer { @@ -10,7 +14,7 @@ class DratTracer : public FileTracer { Internal *internal; File *file; bool binary; -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t added, deleted; #endif void put_binary_zero (); @@ -40,7 +44,7 @@ public: void report_status (int, int64_t) override {} // skip -#ifndef QUIET +#ifndef CADICAL_QUIET void print_statistics (); #endif bool closed () override; @@ -50,4 +54,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/elim.hpp b/src/sat/cadical/elim.hpp index 3f5fb63ac..73e1bf547 100644 --- a/src/sat/cadical/elim.hpp +++ b/src/sat/cadical/elim.hpp @@ -1,8 +1,12 @@ #ifndef _elim_hpp_INCLUDED #define _elim_hpp_INCLUDED +#include "global.h" + #include "heap.hpp" // Alphabetically after 'elim.hpp'. +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct Internal; @@ -19,7 +23,7 @@ struct proof_clause { int64_t id; vector<int> literals; // for lrat - unsigned cid; // kitten id + unsigned cid; // cadical_kitten id bool learned; vector<int64_t> chain; }; @@ -50,4 +54,6 @@ struct Eliminator { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/ema.hpp b/src/sat/cadical/ema.hpp index 39eba390a..51eb5f8c1 100644 --- a/src/sat/cadical/ema.hpp +++ b/src/sat/cadical/ema.hpp @@ -1,8 +1,12 @@ #ifndef _ema_hpp_INCLUDED #define _ema_hpp_INCLUDED +#include "global.h" + #include <cstdint> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct Internal; @@ -36,7 +40,7 @@ struct EMA { updated (0), #endif value (0), biased (0), alpha (a), beta (1 - a), exp (!!beta) { - assert (beta >= 0); + CADICAL_assert (beta >= 0); } operator double () const { return value; } @@ -56,7 +60,7 @@ struct EMA { #define INIT_EMA(E, WINDOW) \ do { \ - assert ((WINDOW) >= 1); \ + CADICAL_assert ((WINDOW) >= 1); \ double ALPHA = 1.0 / (double) (WINDOW); \ E = EMA (ALPHA); \ LOG ("init " #E " EMA target alpha %g window %d", ALPHA, \ @@ -65,4 +69,6 @@ struct EMA { /*------------------------------------------------------------------------*/ +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/external.hpp b/src/sat/cadical/external.hpp index 25e21affb..7618fb936 100644 --- a/src/sat/cadical/external.hpp +++ b/src/sat/cadical/external.hpp @@ -1,6 +1,8 @@ #ifndef _external_hpp_INCLUDED #define _external_hpp_INCLUDED +#include "global.h" + /*------------------------------------------------------------------------*/ #include "range.hpp" @@ -9,6 +11,8 @@ /*------------------------------------------------------------------------*/ +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { using namespace std; @@ -146,26 +150,26 @@ struct External { /*==== end of state ====================================================*/ - // These two just factor out common sanity (assertion) checking code. + // These two just factor out common sanity (CADICAL_assertion) checking code. inline int vidx (int elit) const { - assert (elit); - assert (elit != INT_MIN); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); int res = abs (elit); - assert (res <= max_var); + CADICAL_assert (res <= max_var); return res; } inline int vlit (int elit) const { - assert (elit); - assert (elit != INT_MIN); - assert (abs (elit) <= max_var); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); + CADICAL_assert (abs (elit) <= max_var); return elit; } inline bool is_valid_input (int elit) { - assert (elit); - assert (elit != INT_MIN); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); int eidx = abs (elit); return eidx > max_var || !ervars[eidx]; } @@ -202,10 +206,10 @@ struct External { // Marking external literals. unsigned elit2ulit (int elit) const { - assert (elit); - assert (elit != INT_MIN); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); const int idx = abs (elit) - 1; - assert (idx <= max_var); + CADICAL_assert (idx <= max_var); return 2u * idx + (elit < 0); } @@ -251,8 +255,8 @@ struct External { void melt (int elit); bool frozen (int elit) { - assert (elit); - assert (elit != INT_MIN); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); int eidx = abs (elit); if (eidx > max_var) return false; @@ -319,7 +323,7 @@ struct External { // a negative value into a positive one (happened in 'extend'). // inline int ival (int elit) const { - assert (elit != INT_MIN); + CADICAL_assert (elit != INT_MIN); int eidx = abs (elit); bool val = false; if (eidx <= max_var && (size_t) eidx < vals.size ()) @@ -440,8 +444,8 @@ struct External { // already). // inline int sol (int elit) const { - assert (solution); - assert (elit != INT_MIN); + CADICAL_assert (solution); + CADICAL_assert (elit != INT_MIN); int eidx = abs (elit); if (eidx > max_var) return 0; @@ -458,4 +462,6 @@ struct External { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/factor.hpp b/src/sat/cadical/factor.hpp index ac01d35c5..3aa5ea6c6 100644 --- a/src/sat/cadical/factor.hpp +++ b/src/sat/cadical/factor.hpp @@ -1,9 +1,13 @@ #ifndef _factor_hpp_INCLUDED #define _factor_hpp_INCLUDED +#include "global.h" + #include "clause.hpp" #include "heap.hpp" +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct Internal; @@ -51,4 +55,6 @@ struct Factoring { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/file.hpp b/src/sat/cadical/file.hpp index a42b31bbb..860e9613a 100644 --- a/src/sat/cadical/file.hpp +++ b/src/sat/cadical/file.hpp @@ -1,17 +1,23 @@ #ifndef _file_hpp_INCLUDED #define _file_hpp_INCLUDED +#include "global.h" + #include <cassert> #include <cstdint> #include <cstdio> #include <cstdlib> #include <vector> -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG #include <climits> #endif /*------------------------------------------------------------------------*/ +#ifdef WIN32 +#define cadical_putc_unlocked putc +#define cadical_getc_unlocked getc +#else #ifndef NUNLOCKED #define cadical_putc_unlocked putc_unlocked #define cadical_getc_unlocked getc_unlocked @@ -19,8 +25,11 @@ #define cadical_putc_unlocked putc #define cadical_getc_unlocked getc #endif +#endif /*------------------------------------------------------------------------*/ +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { // Wraps a 'C' file 'FILE' with name and supports zipped reading and writing @@ -33,7 +42,7 @@ struct Internal; class File { Internal *internal; -#if !defined(QUIET) || !defined(NDEBUG) +#if !defined(CADICAL_QUIET) || !defined(CADICAL_NDEBUG) bool writing; #endif @@ -57,7 +66,7 @@ class File { const char *mode); static FILE *read_pipe (Internal *, const char *fmt, const int *sig, const char *path); -#ifndef __WIN32 +#ifndef WIN32 static FILE *write_pipe (Internal *, const char *fmt, const char *path, int &child_pid); #endif @@ -95,7 +104,7 @@ public: // threads, which on the other hand currently is impossible. int get () { - assert (!writing); + CADICAL_assert (!writing); int res = cadical_getc_unlocked (file); if (res == '\n') _lineno++; @@ -105,7 +114,7 @@ public: } bool put (char ch) { - assert (writing); + CADICAL_assert (writing); if (cadical_putc_unlocked (ch, file) == EOF) return false; _bytes++; @@ -115,7 +124,7 @@ public: bool endl () { return put ('\n'); } bool put (unsigned char ch) { - assert (writing); + CADICAL_assert (writing); if (cadical_putc_unlocked (ch, file) == EOF) return false; _bytes++; @@ -130,20 +139,20 @@ public: } bool put (int lit) { - assert (writing); + CADICAL_assert (writing); if (!lit) return put ('0'); else if (lit == -2147483648) { - assert (lit == INT_MIN); + CADICAL_assert (lit == INT_MIN); return put ("-2147483648"); } else { char buffer[11]; int i = sizeof buffer; buffer[--i] = 0; - assert (lit != INT_MIN); + CADICAL_assert (lit != INT_MIN); unsigned idx = abs (lit); while (idx) { - assert (i > 0); + CADICAL_assert (i > 0); buffer[--i] = '0' + idx % 10; idx /= 10; } @@ -154,20 +163,20 @@ public: } bool put (int64_t l) { - assert (writing); + CADICAL_assert (writing); if (!l) return put ('0'); else if (l == INT64_MIN) { - assert (sizeof l == 8); + CADICAL_assert (sizeof l == 8); return put ("-9223372036854775808"); } else { char buffer[21]; int i = sizeof buffer; buffer[--i] = 0; - assert (l != INT64_MIN); + CADICAL_assert (l != INT64_MIN); uint64_t k = l < 0 ? -l : l; while (k) { - assert (i > 0); + CADICAL_assert (i > 0); buffer[--i] = '0' + k % 10; k /= 10; } @@ -178,7 +187,7 @@ public: } bool put (uint64_t l) { - assert (writing); + CADICAL_assert (writing); if (!l) return put ('0'); else { @@ -186,7 +195,7 @@ public: int i = sizeof buffer; buffer[--i] = 0; while (l) { - assert (i > 0); + CADICAL_assert (i > 0); buffer[--i] = '0' + l % 10; l /= 10; } @@ -207,4 +216,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/flags.hpp b/src/sat/cadical/flags.hpp index d5ba3e2a4..995438c7a 100644 --- a/src/sat/cadical/flags.hpp +++ b/src/sat/cadical/flags.hpp @@ -1,6 +1,10 @@ #ifndef _flags_hpp_INCLUDED #define _flags_hpp_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct Flags { // Variable flags. @@ -82,4 +86,6 @@ struct Flags { // Variable flags. } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/format.hpp b/src/sat/cadical/format.hpp index 8ad892cf4..cf229e760 100644 --- a/src/sat/cadical/format.hpp +++ b/src/sat/cadical/format.hpp @@ -1,9 +1,13 @@ #ifndef _format_hpp_INCLUDED #define _format_hpp_INCLUDED +#include "global.h" + #include <cstdarg> #include <cstdint> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { // This class provides a 'printf' style formatting utility. @@ -33,4 +37,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/frattracer.hpp b/src/sat/cadical/frattracer.hpp index f9a1cccf3..cadfa3721 100644 --- a/src/sat/cadical/frattracer.hpp +++ b/src/sat/cadical/frattracer.hpp @@ -1,6 +1,10 @@ #ifndef _frattracer_h_INCLUDED #define _frattracer_h_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { class FratTracer : public FileTracer { @@ -10,7 +14,7 @@ class FratTracer : public FileTracer { bool binary; bool with_antecedents; -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t added, deleted; int64_t finalized, original; #endif @@ -49,7 +53,7 @@ public: void report_status (int, int64_t) override {} // skip -#ifndef QUIET +#ifndef CADICAL_QUIET void print_statistics (); #endif bool closed () override; @@ -59,4 +63,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/global.h b/src/sat/cadical/global.h new file mode 100644 index 000000000..916246cab --- /dev/null +++ b/src/sat/cadical/global.h @@ -0,0 +1,21 @@ +#ifndef ABC_SAT_CADICAL_GLOBAL_HPP_ +#define ABC_SAT_CADICAL_GLOBAL_HPP_ + +// comment out next line to enable cadical debug mode +#define CADICAL_NDEBUG + +#define CADICAL_NBUILD +#define CADICAL_QUIET +#define CADICAL_NCONTRACTS +#define CADICAL_NTRACING +#define CADICAL_NCLOSEFROM + +#ifdef CADICAL_NDEBUG +#define CADICAL_assert(ignore) ((void)0) +#else +#define CADICAL_assert(cond) assert(cond) +#endif + +#include "misc/util/abc_global.h" + +#endif diff --git a/src/sat/cadical/heap.hpp b/src/sat/cadical/heap.hpp index f3f0ab0e4..c42941413 100644 --- a/src/sat/cadical/heap.hpp +++ b/src/sat/cadical/heap.hpp @@ -1,10 +1,14 @@ #ifndef _heap_hpp_INCLUDED #define _heap_hpp_INCLUDED +#include "global.h" + #include "util.hpp" // Alphabetically after 'heap.hpp'. #include <cassert> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { using namespace std; @@ -33,7 +37,7 @@ template <class C> class heap { if (e >= pos.size ()) pos.resize (1 + (size_t) e, invalid_heap_position); unsigned &res = pos[e]; - assert (res == invalid_heap_position || (size_t) res < array.size ()); + CADICAL_assert (res == invalid_heap_position || (size_t) res < array.size ()); return res; } @@ -46,17 +50,17 @@ template <class C> class heap { } unsigned parent (unsigned e) { - assert (has_parent (e)); + CADICAL_assert (has_parent (e)); return array[(index (e) - 1) / 2]; } unsigned left (unsigned e) { - assert (has_left (e)); + CADICAL_assert (has_left (e)); return array[2 * index (e) + 1]; } unsigned right (unsigned e) { - assert (has_right (e)); + CADICAL_assert (has_right (e)); return array[2 * index (e) + 2]; } @@ -100,21 +104,21 @@ template <class C> class heap { void check () { #if 0 // EXPENSIVE HEAP CHECKING IF ENABLED #warning "expensive checking in heap enabled" - assert (array.size () <= invalid_heap_position); + CADICAL_assert (array.size () <= invalid_heap_position); for (size_t i = 0; i < array.size (); i++) { size_t l = 2*i + 1, r = 2*i + 2; - if (l < array.size ()) assert (!less (array[i], array[l])); - if (r < array.size ()) assert (!less (array[i], array[r])); - assert (array[i] >= 0); + if (l < array.size ()) CADICAL_assert (!less (array[i], array[l])); + if (r < array.size ()) CADICAL_assert (!less (array[i], array[r])); + CADICAL_assert (array[i] >= 0); { - assert ((size_t) array[i] < pos.size ()); - assert (i == (size_t) pos[array[i]]); + CADICAL_assert ((size_t) array[i] < pos.size ()); + CADICAL_assert (i == (size_t) pos[array[i]]); } } for (size_t i = 0; i < pos.size (); i++) { if (pos[i] == invalid_heap_position) continue; - assert (pos[i] < array.size ()); - assert (array[pos[i]] == (unsigned) i); + CADICAL_assert (pos[i] < array.size ()); + CADICAL_assert (array[pos[i]] == (unsigned) i); } #endif } @@ -141,9 +145,9 @@ public: // Add a new (not contained) element 'e' to the heap. // void push_back (unsigned e) { - assert (!contains (e)); + CADICAL_assert (!contains (e)); size_t i = array.size (); - assert (i < (size_t) invalid_heap_position); + CADICAL_assert (i < (size_t) invalid_heap_position); array.push_back (e); index (e) = (unsigned) i; up (e); @@ -154,14 +158,14 @@ public: // Returns the maximum element in the heap. // unsigned front () const { - assert (!empty ()); + CADICAL_assert (!empty ()); return array[0]; } // Removes the maximum element in the heap. // unsigned pop_front () { - assert (!empty ()); + CADICAL_assert (!empty ()); unsigned res = array[0], last = array.back (); if (size () > 1) exchange (res, last); @@ -176,7 +180,7 @@ public: // Notify the heap, that evaluation of 'less' has changed for 'e'. // void update (unsigned e) { - assert (contains (e)); + CADICAL_assert (contains (e)); up (e); down (e); check (); @@ -209,4 +213,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/idruptracer.hpp b/src/sat/cadical/idruptracer.hpp index 0b43e7a9c..4ea478705 100644 --- a/src/sat/cadical/idruptracer.hpp +++ b/src/sat/cadical/idruptracer.hpp @@ -1,6 +1,10 @@ #ifndef _idruptracer_h_INCLUDED #define _idruptracer_h_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + class FileTracer; namespace CaDiCaL { @@ -46,7 +50,7 @@ class IdrupTracer : public FileTracer { bool find_and_delete (const int64_t); // find clause position in hash table -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t added, deleted, weakened, restore, original, solved; #endif @@ -97,7 +101,7 @@ public: // logging and file io void connect_internal (Internal *i) override; -#ifndef QUIET +#ifndef CADICAL_QUIET void print_statistics (); #endif bool closed () override; @@ -107,4 +111,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/instantiate.hpp b/src/sat/cadical/instantiate.hpp index fef7532e7..f6c8767dd 100644 --- a/src/sat/cadical/instantiate.hpp +++ b/src/sat/cadical/instantiate.hpp @@ -1,6 +1,10 @@ #ifndef _instantiate_hpp_INCLUDED #define _instantiate_hpp_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { // We are trying to remove literals in clauses, which occur in few clauses @@ -42,4 +46,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/internal.hpp b/src/sat/cadical/internal.hpp index 8dfa2cd8d..d86479a52 100644 --- a/src/sat/cadical/internal.hpp +++ b/src/sat/cadical/internal.hpp @@ -1,6 +1,8 @@ #ifndef _internal_hpp_INCLUDED #define _internal_hpp_INCLUDED +#include "global.h" + /*------------------------------------------------------------------------*/ // Wrapped build specific headers which should go first. @@ -22,9 +24,11 @@ // Less common 'C' header. +#ifndef WIN32 extern "C" { #include <unistd.h> } +#endif /*------------------------------------------------------------------------*/ @@ -104,11 +108,13 @@ extern "C" { #include "watch.hpp" // c headers -extern "C" { +//extern "C" { #include "kitten.h" -} +//} /*------------------------------------------------------------------------*/ +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { using namespace std; @@ -157,14 +163,14 @@ struct Internal { bool in_mode (Mode m) const { return (mode & m) != 0; } void set_mode (Mode m) { - assert (!(mode & m)); + CADICAL_assert (!(mode & m)); mode |= m; } void reset_mode (Mode m) { - assert (mode & m); + CADICAL_assert (mode & m); mode &= ~m; } - void require_mode (Mode m) const { assert (mode & m), (void) m; } + void require_mode (Mode m) const { CADICAL_assert (mode & m), (void) m; } /*----------------------------------------------------------------------*/ @@ -268,7 +274,7 @@ struct Internal { vector<int> sweep_schedule; // remember sweep varibles to reschedule bool sweep_incomplete; // sweep - kitten *citten; + cadical_kitten *citten; size_t num_assigned; // check for satisfied @@ -294,7 +300,7 @@ struct Internal { Options opts; // run-time options Stats stats; // statistics -#ifndef QUIET +#ifndef CADICAL_QUIET Profiles profiles; // time profiles for various functions bool force_phase_messages; // force 'phase (...)' messages #endif @@ -355,15 +361,15 @@ struct Internal { int active () const { int res = stats.active; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG int tmp = max_var; tmp -= stats.unused; tmp -= stats.now.fixed; tmp -= stats.now.eliminated; tmp -= stats.now.substituted; tmp -= stats.now.pure; - assert (tmp >= 0); - assert (tmp == res); + CADICAL_assert (tmp >= 0); + CADICAL_assert (tmp == res); #endif return res; } @@ -388,10 +394,10 @@ struct Internal { // int vidx (int lit) const { int idx; - assert (lit); - assert (lit != INT_MIN); + CADICAL_assert (lit); + CADICAL_assert (lit != INT_MIN); idx = abs (lit); - assert (idx <= max_var); + CADICAL_assert (idx <= max_var); return idx; } @@ -404,9 +410,9 @@ struct Internal { } int u2i (unsigned u) { - assert (u > 1); + CADICAL_assert (u > 1); int res = u / 2; - assert (res <= max_var); + CADICAL_assert (res <= max_var); if (u & 1) res = -res; return res; @@ -414,7 +420,7 @@ struct Internal { int citten2lit (unsigned ulit) { int res = (ulit / 2) + 1; - assert (res <= max_var); + CADICAL_assert (res <= max_var); if (ulit & 1) res = -res; return res; @@ -426,18 +432,18 @@ struct Internal { } int64_t unit_id (int lit) const { - assert (lrat || frat); - assert (val (lit) > 0); + CADICAL_assert (lrat || frat); + CADICAL_assert (val (lit) > 0); const unsigned uidx = vlit (lit); int64_t id = unit_clauses_idx[uidx]; - assert (id); + CADICAL_assert (id); return id; } inline int64_t &unit_clauses (int uidx) { - assert (lrat || frat); - assert (uidx > 0); - assert ((size_t) uidx < unit_clauses_idx.size ()); + CADICAL_assert (lrat || frat); + CADICAL_assert (uidx > 0); + CADICAL_assert ((size_t) uidx < unit_clauses_idx.size ()); return unit_clauses_idx[uidx]; } @@ -476,14 +482,14 @@ struct Internal { return res; } void mark (int lit) { - assert (!marked (lit)); + CADICAL_assert (!marked (lit)); marks[vidx (lit)] = sign (lit); - assert (marked (lit) > 0); - assert (marked (-lit) < 0); + CADICAL_assert (marked (lit) > 0); + CADICAL_assert (marked (-lit) < 0); } void unmark (int lit) { marks[vidx (lit)] = 0; - assert (!marked (lit)); + CADICAL_assert (!marked (lit)); } // Use only bits 6 and 7 to store the sign or zero. The remaining @@ -498,24 +504,24 @@ struct Internal { void mark67 (int lit) { signed char &m = marks[vidx (lit)]; const signed char mask = 0x3f; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG const signed char bits = m & mask; #endif m = (m & mask) | (sign (lit) << 6); - assert (marked (lit) > 0); - assert (marked (-lit) < 0); - assert ((m & mask) == bits); - assert (marked67 (lit) > 0); - assert (marked67 (-lit) < 0); + CADICAL_assert (marked (lit) > 0); + CADICAL_assert (marked (-lit) < 0); + CADICAL_assert ((m & mask) == bits); + CADICAL_assert (marked67 (lit) > 0); + CADICAL_assert (marked67 (-lit) < 0); } void unmark67 (int lit) { signed char &m = marks[vidx (lit)]; const signed char mask = 0x3f; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG const signed bits = m & mask; #endif m &= mask; - assert ((m & mask) == bits); + CADICAL_assert ((m & mask) == bits); } void unmark (vector<int> &lits) { @@ -528,53 +534,53 @@ struct Internal { // bit in 'condition' to mark variables in the conditional part. // bool getbit (int lit, int bit) const { - assert (0 <= bit), assert (bit < 6); + CADICAL_assert (0 <= bit), CADICAL_assert (bit < 6); return marks[vidx (lit)] & (1 << bit); } void setbit (int lit, int bit) { - assert (0 <= bit), assert (bit < 6); - assert (!getbit (lit, bit)); + CADICAL_assert (0 <= bit), CADICAL_assert (bit < 6); + CADICAL_assert (!getbit (lit, bit)); marks[vidx (lit)] |= (1 << bit); - assert (getbit (lit, bit)); + CADICAL_assert (getbit (lit, bit)); } void unsetbit (int lit, int bit) { - assert (0 <= bit), assert (bit < 6); - assert (getbit (lit, bit)); + CADICAL_assert (0 <= bit), CADICAL_assert (bit < 6); + CADICAL_assert (getbit (lit, bit)); marks[vidx (lit)] &= ~(1 << bit); - assert (!getbit (lit, bit)); + CADICAL_assert (!getbit (lit, bit)); } // Marking individual literals. // bool marked2 (int lit) const { unsigned res = marks[vidx (lit)]; - assert (res <= 3); + CADICAL_assert (res <= 3); unsigned bit = bign (lit); return (res & bit) != 0; } void mark2 (int lit) { marks[vidx (lit)] |= bign (lit); - assert (marked2 (lit)); + CADICAL_assert (marked2 (lit)); } // marks bits 1,2,3 and 4,5,6 depending on fact and sign of lit // bool getfact (int lit, int fact) const { - assert (fact == 1 || fact == 2 || fact == 4); + CADICAL_assert (fact == 1 || fact == 2 || fact == 4); int res = marks[vidx (lit)]; if (lit < 0) { res >>= 3; } else { res &= 7; } - // assert (!res || res == 1 || res == 2 || res == 4); + // CADICAL_assert (!res || res == 1 || res == 2 || res == 4); return res & fact; } void markfact (int lit, int fact) { - assert (fact == 1 || fact == 2 || fact == 4); - assert (!getfact (lit, fact)); -#ifndef NDEBUG + CADICAL_assert (fact == 1 || fact == 2 || fact == 4); + CADICAL_assert (!getfact (lit, fact)); +#ifndef CADICAL_NDEBUG int before = getfact (-lit, fact); #endif int res = marks[vidx (lit)]; @@ -584,15 +590,15 @@ struct Internal { res |= fact; } marks[vidx (lit)] = res; - assert (getfact (lit, fact)); -#ifndef NDEBUG - assert (getfact (-lit, fact) == before); + CADICAL_assert (getfact (lit, fact)); +#ifndef CADICAL_NDEBUG + CADICAL_assert (getfact (-lit, fact) == before); #endif } void unmarkfact (int lit, int fact) { - assert (fact == 1 || fact == 2 || fact == 4); - assert (getfact (lit, fact)); + CADICAL_assert (fact == 1 || fact == 2 || fact == 4); + CADICAL_assert (getfact (lit, fact)); int res = marks[vidx (lit)]; if (lit < 0) { res &= ~(fact << 3); @@ -600,7 +606,7 @@ struct Internal { res &= ~fact; } marks[vidx (lit)] = res; - assert (!getfact (lit, fact)); + CADICAL_assert (!getfact (lit, fact)); } // Marking and unmarking of all literals in a clause. @@ -615,7 +621,7 @@ struct Internal { // Inlined here, since it occurs in the tight inner loop of 'propagate'. // inline void watch_literal (int lit, int blit, Clause *c) { - assert (lit != blit); + CADICAL_assert (lit != blit); Watches &ws = watches (lit); ws.push_back (Watch (blit, c)); LOG (c, "watch %d blit %d in", lit, blit); @@ -644,8 +650,8 @@ struct Internal { // inlined here since it occurs in several inner loops. // inline void update_queue_unassigned (int idx) { - assert (0 < idx); - assert (idx <= max_var); + CADICAL_assert (0 < idx); + CADICAL_assert (idx <= max_var); queue.unassigned = idx; queue.bumped = btab[idx]; LOG ("queue unassigned now %d bumped %" PRId64 "", idx, btab[idx]); @@ -815,7 +821,7 @@ struct Internal { void connect_propagator (); void mark_garbage_external_forgettable (int64_t id); bool is_external_forgettable (int64_t id); -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG bool get_merged_literals (std::vector<int> &); void get_all_fixed_literals (std::vector<int> &); #endif @@ -1113,7 +1119,7 @@ struct Internal { void mark_decomposed (int lit) { Flags &f = flags (lit); const unsigned bit = bign (lit); - assert ((f.marked_signed & bit) == 0); + CADICAL_assert ((f.marked_signed & bit) == 0); sign_marked.push_back (lit); f.marked_signed |= bit; } @@ -1166,7 +1172,7 @@ struct Internal { void find_gate_clauses (Eliminator &, int pivot); void unmark_gate_clauses (Eliminator &); - // mine definitions for kitten in 'definition.cpp' + // mine definitions for cadical_kitten in 'definition.cpp' // void find_definition (Eliminator &, int); void init_citten (); @@ -1204,8 +1210,8 @@ struct Internal { // sweeping in 'sweep.cpp' int sweep_solve (); - void sweep_set_kitten_ticks_limit (Sweeper &sweeper); - bool kitten_ticks_limit_hit (Sweeper &sweeper, const char *when); + void sweep_set_cadical_kitten_ticks_limit (Sweeper &sweeper); + bool cadical_kitten_ticks_limit_hit (Sweeper &sweeper, const char *when); void init_sweeper (Sweeper &sweeper); void release_sweeper (Sweeper &sweeper); void clear_sweeper (Sweeper &sweeper); @@ -1493,7 +1499,7 @@ struct Internal { bool terminating_asked (); -#ifndef QUIET +#ifndef CADICAL_QUIET // Built in profiling in 'profile.cpp' (see also 'profile.hpp'). // void start_profiling (Profile &p, double); @@ -1510,9 +1516,9 @@ struct Internal { // negative. We also avoid taking the absolute value. // signed char val (int lit) const { - assert (-max_var <= lit); - assert (lit); - assert (lit <= max_var); + CADICAL_assert (-max_var <= lit); + CADICAL_assert (lit); + CADICAL_assert (lit <= max_var); return vals[lit]; } @@ -1520,11 +1526,11 @@ struct Internal { // setter function for setting and resetting the value of a literal. // void set_val (int lit, signed char val) { - assert (-1 <= val); - assert (val <= 1); - assert (-max_var <= lit); - assert (lit); - assert (lit <= max_var); + CADICAL_assert (-1 <= val); + CADICAL_assert (val <= 1); + CADICAL_assert (-max_var <= lit); + CADICAL_assert (lit); + CADICAL_assert (lit <= max_var); vals[lit] = val; vals[-lit] = -val; } @@ -1534,9 +1540,9 @@ struct Internal { // of the variable anyhow. // int fixed (int lit) { - assert (-max_var <= lit); - assert (lit); - assert (lit <= max_var); + CADICAL_assert (-max_var <= lit); + CADICAL_assert (lit); + CADICAL_assert (lit <= max_var); const int idx = vidx (lit); int res = vals[idx]; if (res && vtab[idx].level) @@ -1549,10 +1555,10 @@ struct Internal { // Map back an internal literal to an external. // int externalize (int lit) { - assert (lit != INT_MIN); + CADICAL_assert (lit != INT_MIN); const int idx = abs (lit); - assert (idx); - assert (idx <= max_var); + CADICAL_assert (idx); + CADICAL_assert (idx <= max_var); int res = i2e[idx]; if (lit < 0) res = -res; @@ -1663,12 +1669,12 @@ struct Internal { /*----------------------------------------------------------------------*/ -#ifndef QUIET +#ifndef CADICAL_QUIET void print_prefix (); // Non-verbose messages and warnings, i.e., always printed unless 'quiet' - // is set, which disables messages at run-time, or even 'QUIET' is defined + // is set, which disables messages at run-time, or even 'CADICAL_QUIET' is defined // through the configuration option './configure --quiet', which disables // such messages completely at compile-time. // @@ -1740,10 +1746,10 @@ inline bool score_smaller::operator() (unsigned a, unsigned b) { // Avoid computing twice 'abs' in 'score ()'. // - assert (1 <= a); - assert (a <= (unsigned) internal->max_var); - assert (1 <= b); - assert (b <= (unsigned) internal->max_var); + CADICAL_assert (1 <= a); + CADICAL_assert (a <= (unsigned) internal->max_var); + CADICAL_assert (1 <= b); + CADICAL_assert (b <= (unsigned) internal->max_var); double s = internal->stab[a]; double t = internal->stab[b]; @@ -1760,8 +1766,8 @@ inline bool score_smaller::operator() (unsigned a, unsigned b) { // Implemented here for keeping it all inline (requires Internal::fixed). inline int External::fixed (int elit) const { - assert (elit); - assert (elit != INT_MIN); + CADICAL_assert (elit); + CADICAL_assert (elit != INT_MIN); int eidx = abs (elit); if (eidx > max_var) return 0; @@ -1798,7 +1804,7 @@ inline bool Internal::terminated_asynchronously (int factor) { // to this function do not check this again. // if (lim.terminate.forced) { - assert (lim.terminate.forced > 0); + CADICAL_assert (lim.terminate.forced > 0); if (lim.terminate.forced-- == 1) { LOG ("internally forcing termination"); termination_forced = true; @@ -1819,8 +1825,8 @@ inline bool Internal::terminated_asynchronously (int factor) { // flag leads to the first test above to succeed in subsequent calls. // if (external->terminator && !lim.terminate.check--) { - assert (factor > 0); - assert (INT_MAX / factor > opts.terminateint); + CADICAL_assert (factor > 0); + CADICAL_assert (INT_MAX / factor > opts.terminateint); lim.terminate.check = factor * opts.terminateint; if (external->terminator->terminate ()) { termination_forced = true; // Cache it. @@ -1835,8 +1841,8 @@ inline bool Internal::terminated_asynchronously (int factor) { /*------------------------------------------------------------------------*/ inline bool Internal::search_limits_hit () { - assert (!preprocessing); - assert (!localsearching); + CADICAL_assert (!preprocessing); + CADICAL_assert (!localsearching); if (lim.conflicts >= 0 && stats.conflicts >= lim.conflicts) { LOG ("conflict limit %" PRId64 " reached", lim.conflicts); @@ -1855,4 +1861,6 @@ inline bool Internal::search_limits_hit () { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/inttypes.hpp b/src/sat/cadical/inttypes.hpp index b1b95872e..2f7cb74a4 100644 --- a/src/sat/cadical/inttypes.hpp +++ b/src/sat/cadical/inttypes.hpp @@ -1,6 +1,8 @@ #ifndef _inttypes_h_INCLUDED #define _inttypes_h_INCLUDED +#include "global.h" + // This is an essence a wrapper around '<cinttypes>' respectively // 'inttypes.h' in order to please the 'MinGW' cross-compiler (we are using // 'i686-w64-mingw32-gcc') to produce correct 'printf' style formatting for diff --git a/src/sat/cadical/ipasir.h b/src/sat/cadical/ipasir.h index 0da79fe14..ccb610130 100644 --- a/src/sat/cadical/ipasir.h +++ b/src/sat/cadical/ipasir.h @@ -1,10 +1,10 @@ #ifndef _ipasir_h_INCLUDED #define _ipasir_h_INCLUDED +#include "global.h" + /*------------------------------------------------------------------------*/ -#ifdef __cplusplus -extern "C" { -#endif +ABC_NAMESPACE_HEADER_START /*------------------------------------------------------------------------*/ // Here are the declarations for the actual IPASIR functions, which is the @@ -29,9 +29,7 @@ void ipasir_set_learn (void *solver, void *state, int max_length, void (*learn) (void *state, int *clause)); /*------------------------------------------------------------------------*/ -#ifdef __cplusplus -} -#endif +ABC_NAMESPACE_HEADER_END /*------------------------------------------------------------------------*/ #endif diff --git a/src/sat/cadical/kitten.c b/src/sat/cadical/kitten.c deleted file mode 100644 index 7cce86c5b..000000000 --- a/src/sat/cadical/kitten.c +++ /dev/null @@ -1,2593 +0,0 @@ -#include "kitten.h" -#include "random.h" -#include "stack.h" - -#include <assert.h> -#include <inttypes.h> -#include <limits.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -typedef signed char value; - -static void die (const char *fmt, ...) { - fputs ("kitten: error: ", stderr); - va_list ap; - va_start (ap, fmt); - vfprintf (stderr, fmt, ap); - va_end (ap); - fputc ('\n', stderr); - exit (1); -} - -static inline void *kitten_calloc (size_t n, size_t size) { - void *res = calloc (n, size); - if (n && size && !res) - die ("out of memory allocating '%zu * %zu' bytes", n, size); - return res; -} - -#define CALLOC(P, N) \ - do { \ - (P) = kitten_calloc (N, sizeof *(P)); \ - } while (0) -#define DEALLOC(P, N) free (P) - -#undef ENLARGE_STACK - -#define ENLARGE_STACK(S) \ - do { \ - assert (FULL_STACK (S)); \ - const size_t SIZE = SIZE_STACK (S); \ - const size_t OLD_CAPACITY = CAPACITY_STACK (S); \ - const size_t NEW_CAPACITY = OLD_CAPACITY ? 2 * OLD_CAPACITY : 1; \ - const size_t BYTES = NEW_CAPACITY * sizeof *(S).begin; \ - (S).begin = realloc ((S).begin, BYTES); \ - if (!(S).begin) \ - die ("out of memory reallocating '%zu' bytes", BYTES); \ - (S).allocated = (S).begin + NEW_CAPACITY; \ - (S).end = (S).begin + SIZE; \ - } while (0) - -// Beside allocators above also use stand alone statistics counters. - -#define INC(NAME) \ - do { \ - statistics *statistics = &kitten->statistics; \ - assert (statistics->NAME < UINT64_MAX); \ - statistics->NAME++; \ - } while (0) - -#define ADD(NAME, DELTA) \ - do { \ - statistics *statistics = &kitten->statistics; \ - assert (statistics->NAME <= UINT64_MAX - (DELTA)); \ - statistics->NAME += (DELTA); \ - } while (0) - -#define KITTEN_TICKS (kitten->statistics.kitten_ticks) - -#define INVALID UINT_MAX -#define MAX_VARS ((1u << 31) - 1) - -#define CORE_FLAG (1u) -#define LEARNED_FLAG (2u) - -// clang-format off - -typedef struct kar kar; -typedef struct kink kink; -typedef struct klause klause; -typedef STACK (unsigned) klauses; -typedef unsigneds katches; - -// clang-format on - -struct kar { - unsigned level; - unsigned reason; -}; - -struct kink { - unsigned next; - unsigned prev; - uint64_t stamp; -}; - -struct klause { - unsigned aux; - unsigned size; - unsigned flags; - unsigned lits[1]; -}; - -typedef struct statistics statistics; - -struct statistics { - uint64_t learned; - uint64_t original; - uint64_t kitten_flip; - uint64_t kitten_flipped; - uint64_t kitten_sat; - uint64_t kitten_solved; - uint64_t kitten_conflicts; - uint64_t kitten_decisions; - uint64_t kitten_propagations; - uint64_t kitten_ticks; - uint64_t kitten_unknown; - uint64_t kitten_unsat; -}; - -typedef struct kimits kimits; - -struct kimits { - uint64_t ticks; -}; - -struct kitten { - // First zero initialized field in 'clear_kitten' is 'status'. - // - int status; - -#if defined(LOGGING) - bool logging; -#endif - bool antecedents; - bool learned; - - unsigned level; - unsigned propagated; - unsigned unassigned; - unsigned inconsistent; - unsigned failing; - - uint64_t generator; - - size_t lits; - size_t evars; - - size_t end_original_ref; - - struct { - unsigned first, last; - uint64_t stamp; - unsigned search; - } queue; - - // The 'size' field below is the first not zero reinitialized field - // by 'memset' in 'clear_kitten' (after 'kissat'). - - size_t size; - size_t esize; - - kar *vars; - kink *links; - value *marks; - value *values; - bool *failed; - unsigned char *phases; - unsigned *import; - katches *watches; - - unsigneds analyzed; - unsigneds assumptions; - unsigneds core; - unsigneds rcore; - unsigneds eclause; - unsigneds export; - unsigneds klause; - unsigneds klauses; - unsigneds resolved; - unsigneds trail; - unsigneds units; - unsigneds prime[2]; - - kimits limits; - int (*terminator) (void *); - void *terminator_data; - unsigneds clause; - uint64_t initialized; - statistics statistics; -}; - -/*------------------------------------------------------------------------*/ - -static inline bool is_core_klause (klause *c) { - return c->flags & CORE_FLAG; -} - -static inline bool is_learned_klause (klause *c) { - return c->flags & LEARNED_FLAG; -} - -static inline void set_core_klause (klause *c) { c->flags |= CORE_FLAG; } - -static inline void unset_core_klause (klause *c) { c->flags &= ~CORE_FLAG; } - -static inline klause *dereference_klause (kitten *kitten, unsigned ref) { - unsigned *res = BEGIN_STACK (kitten->klauses) + ref; - assert (res < END_STACK (kitten->klauses)); - return (klause *) res; -} - -static inline unsigned reference_klause (kitten *kitten, const klause *c) { - const unsigned *const begin = BEGIN_STACK (kitten->klauses); - const unsigned *p = (const unsigned *) c; - assert (begin <= p); - assert (p < END_STACK (kitten->klauses)); - const unsigned res = p - begin; - return res; -} - -/*------------------------------------------------------------------------*/ - -#define KATCHES(KIT) (kitten->watches[assert ((KIT) < kitten->lits), (KIT)]) - -#define all_klauses(C) \ - klause *C = begin_klauses (kitten), *end_##C = end_klauses (kitten); \ - (C) != end_##C; \ - (C) = next_klause (kitten, C) - -#define all_original_klauses(C) \ - klause *C = begin_klauses (kitten), \ - *end_##C = end_original_klauses (kitten); \ - (C) != end_##C; \ - (C) = next_klause (kitten, C) - -#define all_learned_klauses(C) \ - klause *C = begin_learned_klauses (kitten), \ - *end_##C = end_klauses (kitten); \ - (C) != end_##C; \ - (C) = next_klause (kitten, C) - -#define all_kits(KIT) \ - size_t KIT = 0, KIT_##END = kitten->lits; \ - KIT != KIT_##END; \ - KIT++ - -#define BEGIN_KLAUSE(C) (C)->lits - -#define END_KLAUSE(C) (BEGIN_KLAUSE (C) + (C)->size) - -#define all_literals_in_klause(KIT, C) \ - unsigned KIT, *KIT##_PTR = BEGIN_KLAUSE (C), \ - *KIT##_END = END_KLAUSE (C); \ - KIT##_PTR != KIT##_END && ((KIT = *KIT##_PTR), true); \ - ++KIT##_PTR - -#define all_antecedents(REF, C) \ - unsigned REF, *REF##_PTR = antecedents (C), \ - *REF##_END = REF##_PTR + (C)->aux; \ - REF##_PTR != REF##_END && ((REF = *REF##_PTR), true); \ - ++REF##_PTR - -#ifdef LOGGING - -#define logging (kitten->logging) - -static void log_basic (kitten *, const char *, ...) - __attribute__ ((format (printf, 2, 3))); - -static void log_basic (kitten *kitten, const char *fmt, ...) { - assert (logging); - printf ("c KITTEN %u ", kitten->level); - va_list ap; - va_start (ap, fmt); - vprintf (fmt, ap); - va_end (ap); - fputc ('\n', stdout); - fflush (stdout); -} - -static void log_reference (kitten *, unsigned, const char *, ...) - __attribute__ ((format (printf, 3, 4))); - -static void log_reference (kitten *kitten, unsigned ref, const char *fmt, - ...) { - klause *c = dereference_klause (kitten, ref); - assert (logging); - printf ("c KITTEN %u ", kitten->level); - va_list ap; - va_start (ap, fmt); - vprintf (fmt, ap); - va_end (ap); - if (is_learned_klause (c)) { - fputs (" learned", stdout); - if (c->aux) - printf ("[%u]", c->aux); - } else { - fputs (" original", stdout); - if (c->aux != INVALID) - printf ("[%u]", c->aux); - } - printf (" size %u clause[%u]", c->size, ref); - value *values = kitten->values; - kar *vars = kitten->vars; - for (all_literals_in_klause (lit, c)) { - printf (" %u", lit); - const value value = values[lit]; - if (value) - printf ("@%u=%d", vars[lit / 2].level, (int) value); - } - fputc ('\n', stdout); - fflush (stdout); -} - -static void log_literals (kitten *, unsigned *, unsigned, const char *, ...) - __attribute__ ((format (printf, 4, 5))); - -static void log_literals (kitten *kitten, unsigned *lits, unsigned size, - const char *fmt, ...) { - assert (logging); - printf ("c KITTEN %u ", kitten->level); - va_list ap; - va_start (ap, fmt); - vprintf (fmt, ap); - va_end (ap); - value *values = kitten->values; - kar *vars = kitten->vars; - for (unsigned i = 0; i < size; i++) { - const unsigned lit = lits[i]; - printf (" %u", lit); - const value value = values[lit]; - if (value) - printf ("@%u=%d", vars[lit / 2].level, (int) value); - } - fputc ('\n', stdout); - fflush (stdout); -} - -#define LOG(...) \ - do { \ - if (logging) \ - log_basic (kitten, __VA_ARGS__); \ - } while (0) - -#define ROG(...) \ - do { \ - if (logging) \ - log_reference (kitten, __VA_ARGS__); \ - } while (0) - -#define LOGLITS(...) \ - do { \ - if (logging) \ - log_literals (kitten, __VA_ARGS__); \ - } while (0) - -#else - -#define LOG(...) \ - do { \ - } while (0) -#define ROG(...) \ - do { \ - } while (0) -#define LOGLITS(...) \ - do { \ - } while (0) - -#endif - -static void check_queue (kitten *kitten) { -#ifdef CHECK_KITTEN - const unsigned vars = kitten->lits / 2; - unsigned found = 0, prev = INVALID; - kink *links = kitten->links; - uint64_t stamp = 0; - for (unsigned idx = kitten->queue.first, next; idx != INVALID; - idx = next) { - kink *link = links + idx; - assert (link->prev == prev); - assert (!found || stamp < link->stamp); - assert (link->stamp < kitten->queue.stamp); - stamp = link->stamp; - next = link->next; - prev = idx; - found++; - } - assert (found == vars); - unsigned next = INVALID; - found = 0; - for (unsigned idx = kitten->queue.last, prev; idx != INVALID; - idx = prev) { - kink *link = links + idx; - assert (link->next == next); - prev = link->prev; - next = idx; - found++; - } - assert (found == vars); - value *values = kitten->values; - bool first = true; - for (unsigned idx = kitten->queue.search, next; idx != INVALID; - idx = next) { - kink *link = links + idx; - next = link->next; - const unsigned lit = 2 * idx; - assert (first || values[lit]); - first = false; - } -#else - (void) kitten; -#endif -} - -static void update_search (kitten *kitten, unsigned idx) { - if (kitten->queue.search == idx) - return; - kitten->queue.search = idx; - LOG ("search updated to %u stamped %" PRIu64, idx, - kitten->links[idx].stamp); -} - -static void enqueue (kitten *kitten, unsigned idx) { - LOG ("enqueue %u", idx); - kink *links = kitten->links; - kink *l = links + idx; - const unsigned last = kitten->queue.last; - if (last == INVALID) - kitten->queue.first = idx; - else - links[last].next = idx; - l->prev = last; - l->next = INVALID; - kitten->queue.last = idx; - l->stamp = kitten->queue.stamp++; - LOG ("stamp %" PRIu64, l->stamp); -} - -static void dequeue (kitten *kitten, unsigned idx) { - LOG ("dequeue %u", idx); - kink *links = kitten->links; - kink *l = links + idx; - const unsigned prev = l->prev; - const unsigned next = l->next; - if (prev == INVALID) - kitten->queue.first = next; - else - links[prev].next = next; - if (next == INVALID) - kitten->queue.last = prev; - else - links[next].prev = prev; -} - -static void init_queue (kitten *kitten, size_t old_vars, size_t new_vars) { - for (size_t idx = old_vars; idx < new_vars; idx++) { - assert (!kitten->values[2 * idx]); - assert (kitten->unassigned < UINT_MAX); - kitten->unassigned++; - enqueue (kitten, idx); - } - LOG ("initialized decision queue from %zu to %zu", old_vars, new_vars); - update_search (kitten, kitten->queue.last); - check_queue (kitten); -} - -static void initialize_kitten (kitten *kitten) { - kitten->queue.first = INVALID; - kitten->queue.last = INVALID; - kitten->inconsistent = INVALID; - kitten->failing = INVALID; - kitten->queue.search = INVALID; - kitten->terminator = 0; - kitten->terminator_data = 0; - kitten->limits.ticks = UINT64_MAX; - kitten->generator = kitten->initialized++; -} - -static void clear_kitten (kitten *kitten) { - size_t bytes = (char *) &kitten->size - (char *) &kitten->status; - memset (&kitten->status, 0, bytes); - memset (&kitten->statistics, 0, sizeof (statistics)); - initialize_kitten (kitten); -} - -#define RESIZE1(P) \ - do { \ - void *OLD_PTR = (P); \ - CALLOC ((P), new_size / 2); \ - const size_t BYTES = old_vars * sizeof *(P); \ - memcpy ((P), OLD_PTR, BYTES); \ - void *NEW_PTR = (P); \ - (P) = OLD_PTR; \ - DEALLOC ((P), old_size / 2); \ - (P) = NEW_PTR; \ - } while (0) - -#define RESIZE2(P) \ - do { \ - void *OLD_PTR = (P); \ - CALLOC ((P), new_size); \ - const size_t BYTES = old_lits * sizeof *(P); \ - memcpy ((P), OLD_PTR, BYTES); \ - void *NEW_PTR = (P); \ - (P) = OLD_PTR; \ - DEALLOC ((P), old_size); \ - (P) = NEW_PTR; \ - } while (0) - -static void enlarge_internal (kitten *kitten, size_t lit) { - const size_t new_lits = (lit | 1) + 1; - const size_t old_lits = kitten->lits; - assert (old_lits <= lit); - assert (old_lits < new_lits); - assert ((lit ^ 1) < new_lits); - assert (lit < new_lits); - const size_t old_size = kitten->size; - const unsigned new_vars = new_lits / 2; - const unsigned old_vars = old_lits / 2; - if (old_size < new_lits) { - size_t new_size = old_size ? 2 * old_size : 2; - while (new_size <= lit) - new_size *= 2; - LOG ("internal literals resized to %zu from %zu (requested %zu)", - new_size, old_size, new_lits); - - RESIZE1 (kitten->marks); - RESIZE1 (kitten->phases); - RESIZE2 (kitten->values); - RESIZE2 (kitten->failed); - RESIZE1 (kitten->vars); - RESIZE1 (kitten->links); - RESIZE2 (kitten->watches); - - kitten->size = new_size; - } - kitten->lits = new_lits; - init_queue (kitten, old_vars, new_vars); - LOG ("internal literals activated until %zu literals", new_lits); - return; -} - -static const char *status_to_string (int status) { - switch (status) { - case 10: - return "formula satisfied"; - case 11: - return "formula satisfied and prime implicant computed"; - case 20: - return "formula inconsistent"; - case 21: - return "formula inconsistent and core computed"; - default: - assert (!status); - return "formula unsolved"; - } -} - -static void invalid_api_usage (const char *fun, const char *fmt, ...) { - fprintf (stderr, "kitten: fatal error: invalid API usage in '%s': ", fun); - va_list ap; - va_start (ap, fmt); - vfprintf (stderr, fmt, ap); - va_end (ap); - fputc ('\n', stderr); - fflush (stderr); - abort (); -} - -#define INVALID_API_USAGE(...) invalid_api_usage (__func__, __VA_ARGS__) - -#define REQUIRE_INITIALIZED() \ - do { \ - if (!kitten) \ - INVALID_API_USAGE ("solver argument zero"); \ - } while (0) - -#define REQUIRE_STATUS(EXPECTED) \ - do { \ - REQUIRE_INITIALIZED (); \ - if (kitten->status != (EXPECTED)) \ - INVALID_API_USAGE ("invalid status '%s' (expected '%s')", \ - status_to_string (kitten->status), \ - status_to_string (EXPECTED)); \ - } while (0) - -#define UPDATE_STATUS(STATUS) \ - do { \ - if (kitten->status != (STATUS)) \ - LOG ("updating status from '%s' to '%s'", \ - status_to_string (kitten->status), status_to_string (STATUS)); \ - else \ - LOG ("keeping status at '%s'", status_to_string (STATUS)); \ - kitten->status = (STATUS); \ - } while (0) - -kitten *kitten_init (void) { - kitten *kitten; - CALLOC (kitten, 1); - initialize_kitten (kitten); - return kitten; -} - -#ifdef LOGGING -void kitten_set_logging (kitten *kitten) { logging = true; } -#endif - -void kitten_track_antecedents (kitten *kitten) { - REQUIRE_STATUS (0); - - if (kitten->learned) - INVALID_API_USAGE ("can not start tracking antecedents after learning"); - - LOG ("enabling antecedents tracking"); - kitten->antecedents = true; -} - -void kitten_randomize_phases (kitten *kitten) { - REQUIRE_INITIALIZED (); - - LOG ("randomizing phases"); - - unsigned char *phases = kitten->phases; - const unsigned vars = kitten->size / 2; - - uint64_t random = kissat_next_random64 (&kitten->generator); - - unsigned i = 0; - const unsigned rest = vars & ~63u; - - while (i != rest) { - uint64_t *p = (uint64_t *) (phases + i); - p[0] = (random >> 0) & 0x0101010101010101; - p[1] = (random >> 1) & 0x0101010101010101; - p[2] = (random >> 2) & 0x0101010101010101; - p[3] = (random >> 3) & 0x0101010101010101; - p[4] = (random >> 4) & 0x0101010101010101; - p[5] = (random >> 5) & 0x0101010101010101; - p[6] = (random >> 6) & 0x0101010101010101; - p[7] = (random >> 7) & 0x0101010101010101; - random = kissat_next_random64 (&kitten->generator); - i += 64; - } - - unsigned shift = 0; - while (i != vars) - phases[i++] = (random >> shift++) & 1; -} - -void kitten_flip_phases (kitten *kitten) { - REQUIRE_INITIALIZED (); - - LOG ("flipping phases"); - - unsigned char *phases = kitten->phases; - const unsigned vars = kitten->size / 2; - - unsigned i = 0; - const unsigned rest = vars & ~7u; - - while (i != rest) { - uint64_t *p = (uint64_t *) (phases + i); - *p ^= 0x0101010101010101; - i += 8; - } - - while (i != vars) - phases[i++] ^= 1; -} - -void kitten_no_ticks_limit (kitten *kitten) { - REQUIRE_INITIALIZED (); - LOG ("forcing no ticks limit"); - kitten->limits.ticks = UINT64_MAX; -} - -uint64_t kitten_current_ticks (kitten *kitten) { - REQUIRE_INITIALIZED (); - const uint64_t current = KITTEN_TICKS; - return current; -} - -void kitten_set_ticks_limit (kitten *kitten, uint64_t delta) { - REQUIRE_INITIALIZED (); - const uint64_t current = KITTEN_TICKS; - uint64_t limit; - if (UINT64_MAX - delta <= current) { - LOG ("forcing unlimited ticks limit"); - limit = UINT64_MAX; - } else { - limit = current + delta; - LOG ("new limit of %" PRIu64 " ticks after %" PRIu64, limit, delta); - } - - kitten->limits.ticks = limit; -} - -void kitten_no_terminator (kitten *kitten) { - REQUIRE_INITIALIZED (); - LOG ("removing terminator"); - kitten->terminator = 0; - kitten->terminator_data = 0; -} - -void kitten_set_terminator (kitten *kitten, void *data, - int (*terminator) (void *)) { - REQUIRE_INITIALIZED (); - LOG ("setting terminator"); - kitten->terminator = terminator; - kitten->terminator_data = data; -} - -static void shuffle_unsigned_array (kitten *kitten, size_t size, - unsigned *a) { - for (size_t i = 0; i < size; i++) { - const size_t j = kissat_pick_random (&kitten->generator, 0, i); - if (j == i) - continue; - const unsigned first = a[i]; - const unsigned second = a[j]; - a[i] = second; - a[j] = first; - } -} - -static void shuffle_unsigned_stack (kitten *kitten, unsigneds *stack) { - const size_t size = SIZE_STACK (*stack); - unsigned *a = BEGIN_STACK (*stack); - shuffle_unsigned_array (kitten, size, a); -} - -static void shuffle_katches (kitten *kitten) { - LOG ("shuffling watch lists"); - for (size_t lit = 0; lit < kitten->lits; lit++) - shuffle_unsigned_stack (kitten, &KATCHES (lit)); -} - -static void shuffle_queue (kitten *kitten) { - LOG ("shuffling variable decision order"); - - const unsigned vars = kitten->lits / 2; - for (unsigned i = 0; i < vars; i++) { - const unsigned idx = kissat_pick_random (&kitten->generator, 0, vars); - dequeue (kitten, idx); - enqueue (kitten, idx); - } - update_search (kitten, kitten->queue.last); -} - -static void shuffle_units (kitten *kitten) { - LOG ("shuffling units"); - shuffle_unsigned_stack (kitten, &kitten->units); -} - -void kitten_shuffle_clauses (kitten *kitten) { - REQUIRE_STATUS (0); - shuffle_queue (kitten); - shuffle_katches (kitten); - shuffle_units (kitten); -} - -static inline unsigned *antecedents (klause *c) { - assert (is_learned_klause (c)); - return c->lits + c->size; -} - -static inline void watch_klause (kitten *kitten, unsigned lit, - unsigned ref) { - ROG (ref, "watching %u in", lit); - katches *watches = &KATCHES (lit); - PUSH_STACK (*watches, ref); -} - -static inline void connect_new_klause (kitten *kitten, unsigned ref) { - ROG (ref, "new"); - - klause *c = dereference_klause (kitten, ref); - - if (!c->size) { - if (kitten->inconsistent == INVALID) { - ROG (ref, "registering inconsistent empty"); - kitten->inconsistent = ref; - } else - ROG (ref, "ignoring inconsistent empty"); - } else if (c->size == 1) { - ROG (ref, "watching unit"); - PUSH_STACK (kitten->units, ref); - } else { - watch_klause (kitten, c->lits[0], ref); - watch_klause (kitten, c->lits[1], ref); - } -} - -static unsigned new_reference (kitten *kitten) { - size_t ref = SIZE_STACK (kitten->klauses); - if (ref >= INVALID) { - die ("maximum number of literals exhausted"); - } - const unsigned res = (unsigned) ref; - assert (res != INVALID); - INC (kitten_ticks); - return res; -} - -static void new_original_klause (kitten *kitten, unsigned id) { - unsigned res = new_reference (kitten); - unsigned size = SIZE_STACK (kitten->klause); - unsigneds *klauses = &kitten->klauses; - PUSH_STACK (*klauses, id); - PUSH_STACK (*klauses, size); - PUSH_STACK (*klauses, 0); - for (all_stack (unsigned, lit, kitten->klause)) - PUSH_STACK (*klauses, lit); - connect_new_klause (kitten, res); - kitten->end_original_ref = SIZE_STACK (*klauses); - kitten->statistics.original++; -} - -static void enlarge_external (kitten *kitten, size_t eidx) { - const size_t old_size = kitten->esize; - const unsigned old_evars = kitten->evars; - assert (old_evars <= eidx); - const unsigned new_evars = eidx + 1; - if (old_size <= eidx) { - size_t new_size = old_size ? 2 * old_size : 1; - while (new_size <= eidx) - new_size *= 2; - LOG ("external resizing to %zu variables from %zu (requested %u)", - new_size, old_size, new_evars); - unsigned *old_import = kitten->import; - CALLOC (kitten->import, new_size); - const size_t bytes = old_evars * sizeof *kitten->import; - memcpy (kitten->import, old_import, bytes); - DEALLOC (old_import, old_size); - kitten->esize = new_size; - } - kitten->evars = new_evars; - LOG ("external variables enlarged to %u", new_evars); -} - -static unsigned import_literal (kitten *kitten, unsigned elit) { - const unsigned eidx = elit / 2; - if (eidx >= kitten->evars) - enlarge_external (kitten, eidx); - - unsigned iidx = kitten->import[eidx]; - if (!iidx) { - iidx = SIZE_STACK (kitten->export); - PUSH_STACK (kitten->export, eidx); - kitten->import[eidx] = iidx + 1; - } else - iidx--; - unsigned ilit = 2 * iidx + (elit & 1); - LOG ("imported external literal %u as internal literal %u", elit, ilit); - if (ilit >= kitten->lits) - enlarge_internal (kitten, ilit); - return ilit; -} - -static unsigned export_literal (kitten *kitten, unsigned ilit) { - const unsigned iidx = ilit / 2; - assert (iidx < SIZE_STACK (kitten->export)); - const unsigned eidx = PEEK_STACK (kitten->export, iidx); - const unsigned elit = 2 * eidx + (ilit & 1); - return elit; -} - -unsigned new_learned_klause (kitten *kitten) { - unsigned res = new_reference (kitten); - unsigneds *klauses = &kitten->klauses; - const size_t size = SIZE_STACK (kitten->klause); - assert (size <= UINT_MAX); - const size_t aux = - kitten->antecedents ? SIZE_STACK (kitten->resolved) : 0; - assert (aux <= UINT_MAX); - PUSH_STACK (*klauses, (unsigned) aux); - PUSH_STACK (*klauses, (unsigned) size); - PUSH_STACK (*klauses, LEARNED_FLAG); - for (all_stack (unsigned, lit, kitten->klause)) - PUSH_STACK (*klauses, lit); - if (aux) - for (all_stack (unsigned, ref, kitten->resolved)) - PUSH_STACK (*klauses, ref); - connect_new_klause (kitten, res); - kitten->learned = true; - kitten->statistics.learned++; - return res; -} - -void kitten_clear (kitten *kitten) { - LOG ("clear kitten of size %zu", kitten->size); - - assert (EMPTY_STACK (kitten->analyzed)); - assert (EMPTY_STACK (kitten->eclause)); - assert (EMPTY_STACK (kitten->resolved)); - - CLEAR_STACK (kitten->assumptions); - CLEAR_STACK (kitten->core); - CLEAR_STACK (kitten->klause); - CLEAR_STACK (kitten->klauses); - CLEAR_STACK (kitten->trail); - CLEAR_STACK (kitten->units); - CLEAR_STACK (kitten->clause); - CLEAR_STACK (kitten->prime[0]); - CLEAR_STACK (kitten->prime[1]); - - for (all_kits (kit)) - CLEAR_STACK (KATCHES (kit)); - - while (!EMPTY_STACK (kitten->export)) - kitten->import[POP_STACK (kitten->export)] = 0; - - const size_t lits = kitten->size; - const unsigned vars = lits / 2; - -#ifndef NDEBUG - for (unsigned i = 0; i < vars; i++) - assert (!kitten->marks[i]); -#endif - - memset (kitten->phases, 0, vars); - memset (kitten->values, 0, lits); - memset (kitten->failed, 0, lits); - memset (kitten->vars, 0, vars); - - clear_kitten (kitten); -} - -void kitten_release (kitten *kitten) { - RELEASE_STACK (kitten->analyzed); - RELEASE_STACK (kitten->assumptions); - RELEASE_STACK (kitten->core); - RELEASE_STACK (kitten->eclause); - RELEASE_STACK (kitten->export); - RELEASE_STACK (kitten->klause); - RELEASE_STACK (kitten->klauses); - RELEASE_STACK (kitten->resolved); - RELEASE_STACK (kitten->trail); - RELEASE_STACK (kitten->units); - RELEASE_STACK (kitten->clause); - RELEASE_STACK (kitten->prime[0]); - RELEASE_STACK (kitten->prime[1]); - - for (size_t lit = 0; lit < kitten->size; lit++) - RELEASE_STACK (kitten->watches[lit]); - - const size_t lits = kitten->size; - const unsigned vars = lits / 2; - DEALLOC (kitten->marks, vars); - DEALLOC (kitten->phases, vars); - DEALLOC (kitten->values, lits); - DEALLOC (kitten->failed, lits); - DEALLOC (kitten->vars, vars); - DEALLOC (kitten->links, vars); - DEALLOC (kitten->watches, lits); - DEALLOC (kitten->import, kitten->esize); - (void) vars; - - free (kitten); -} - -static inline void move_to_front (kitten *kitten, unsigned idx) { - if (idx == kitten->queue.last) - return; - LOG ("move to front variable %u", idx); - dequeue (kitten, idx); - enqueue (kitten, idx); - assert (kitten->values[2 * idx]); -} - -static inline void assign (kitten *kitten, unsigned lit, unsigned reason) { -#ifdef LOGGING - if (reason == INVALID) - LOG ("assign %u as decision", lit); - else - ROG (reason, "assign %u reason", lit); -#endif - value *values = kitten->values; - const unsigned not_lit = lit ^ 1; - assert (!values[lit]); - assert (!values[not_lit]); - values[lit] = 1; - values[not_lit] = -1; - const unsigned idx = lit / 2; - const unsigned sign = lit & 1; - kitten->phases[idx] = sign; - PUSH_STACK (kitten->trail, lit); - kar *v = kitten->vars + idx; - v->level = kitten->level; - if (!v->level) { - assert (reason != INVALID); - klause *c = dereference_klause (kitten, reason); - if (c->size > 1) { - if (kitten->antecedents) { - PUSH_STACK (kitten->resolved, reason); - for (all_literals_in_klause (other, c)) - if (other != lit) { - const unsigned other_idx = other / 2; - const unsigned other_ref = kitten->vars[other_idx].reason; - assert (other_ref != INVALID); - PUSH_STACK (kitten->resolved, other_ref); - } - } - PUSH_STACK (kitten->klause, lit); - reason = new_learned_klause (kitten); - CLEAR_STACK (kitten->resolved); - CLEAR_STACK (kitten->klause); - } - } - v->reason = reason; - assert (kitten->unassigned); - kitten->unassigned--; -} - -static inline unsigned propagate_literal (kitten *kitten, unsigned lit) { - LOG ("propagating %u", lit); - value *values = kitten->values; - assert (values[lit] > 0); - const unsigned not_lit = lit ^ 1; - katches *watches = kitten->watches + not_lit; - unsigned conflict = INVALID; - unsigned *q = BEGIN_STACK (*watches); - const unsigned *const end_watches = END_STACK (*watches); - unsigned const *p = q; - uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; - while (p != end_watches) { - const unsigned ref = *q++ = *p++; - klause *c = dereference_klause (kitten, ref); - assert (c->size > 1); - unsigned *lits = c->lits; - const unsigned other = lits[0] ^ lits[1] ^ not_lit; - const value other_value = values[other]; - ticks++; - if (other_value > 0) - continue; - value replacement_value = -1; - unsigned replacement = INVALID; - const unsigned *const end_lits = lits + c->size; - unsigned *r; - for (r = lits + 2; r != end_lits; r++) { - replacement = *r; - replacement_value = values[replacement]; - if (replacement_value >= 0) - break; - } - if (replacement_value >= 0) { - assert (replacement != INVALID); - ROG (ref, "unwatching %u in", not_lit); - lits[0] = other; - lits[1] = replacement; - *r = not_lit; - watch_klause (kitten, replacement, ref); - q--; - } else if (other_value < 0) { - ROG (ref, "conflict"); - INC (kitten_conflicts); - conflict = ref; - break; - } else { - assert (!other_value); - assign (kitten, other, ref); - } - } - while (p != end_watches) - *q++ = *p++; - SET_END_OF_STACK (*watches, q); - ADD (kitten_ticks, ticks); - return conflict; -} - -static inline unsigned propagate (kitten *kitten) { - assert (kitten->inconsistent == INVALID); - unsigned propagated = 0; - unsigned conflict = INVALID; - while (conflict == INVALID && - kitten->propagated < SIZE_STACK (kitten->trail)) { - if (kitten->terminator && - kitten->terminator (kitten->terminator_data)) { - break; - } - const unsigned lit = PEEK_STACK (kitten->trail, kitten->propagated); - conflict = propagate_literal (kitten, lit); - kitten->propagated++; - propagated++; - } - ADD (kitten_propagations, propagated); - return conflict; -} - -static void bump (kitten *kitten) { - value *marks = kitten->marks; - for (all_stack (unsigned, idx, kitten->analyzed)) { - marks[idx] = 0; - move_to_front (kitten, idx); - } - check_queue (kitten); -} - -static inline void unassign (kitten *kitten, value *values, unsigned lit) { - const unsigned not_lit = lit ^ 1; - assert (values[lit]); - assert (values[not_lit]); - const unsigned idx = lit / 2; -#ifdef LOGGING - kar *var = kitten->vars + idx; - kitten->level = var->level; - LOG ("unassign %u", lit); -#endif - values[lit] = values[not_lit] = 0; - assert (kitten->unassigned < kitten->lits / 2); - kitten->unassigned++; - kink *links = kitten->links; - kink *link = links + idx; - if (link->stamp > links[kitten->queue.search].stamp) - update_search (kitten, idx); -} - -static void backtrack (kitten *kitten, unsigned jump) { - check_queue (kitten); - assert (jump < kitten->level); - LOG ("back%s to level %u", - (kitten->level == jump + 1 ? "tracking" : "jumping"), jump); - kar *vars = kitten->vars; - value *values = kitten->values; - unsigneds *trail = &kitten->trail; - while (!EMPTY_STACK (*trail)) { - const unsigned lit = TOP_STACK (*trail); - const unsigned idx = lit / 2; - const unsigned level = vars[idx].level; - if (level == jump) - break; - (void) POP_STACK (*trail); - unassign (kitten, values, lit); - } - kitten->propagated = SIZE_STACK (*trail); - kitten->level = jump; - check_queue (kitten); -} - -void completely_backtrack_to_root_level (kitten *kitten) { - check_queue (kitten); - LOG ("completely backtracking to level 0"); - value *values = kitten->values; - unsigneds *trail = &kitten->trail; - unsigneds *units = &kitten->units; -#ifndef NDEBUG - kar *vars = kitten->vars; -#endif - for (all_stack (unsigned, lit, *trail)) { - assert (vars[lit / 2].level); - unassign (kitten, values, lit); - } - CLEAR_STACK (*trail); - for (all_stack (unsigned, ref, *units)) { - klause *c = dereference_klause (kitten, ref); - assert (c->size == 1); - const unsigned unit = c->lits[0]; - const value value = values[unit]; - if (value <= 0) - continue; - unassign (kitten, values, unit); - } - kitten->propagated = 0; - kitten->level = 0; - check_queue (kitten); -} - -static void analyze (kitten *kitten, unsigned conflict) { - assert (kitten->level); - assert (kitten->inconsistent == INVALID); - assert (EMPTY_STACK (kitten->analyzed)); - assert (EMPTY_STACK (kitten->resolved)); - assert (EMPTY_STACK (kitten->klause)); - PUSH_STACK (kitten->klause, INVALID); - unsigned reason = conflict; - value *marks = kitten->marks; - const kar *const vars = kitten->vars; - const unsigned level = kitten->level; - unsigned const *p = END_STACK (kitten->trail); - unsigned open = 0, jump = 0, size = 1, uip; - for (;;) { - assert (reason != INVALID); - klause *c = dereference_klause (kitten, reason); - assert (c); - ROG (reason, "analyzing"); - PUSH_STACK (kitten->resolved, reason); - for (all_literals_in_klause (lit, c)) { - const unsigned idx = lit / 2; - if (marks[idx]) - continue; - assert (kitten->values[lit] < 0); - LOG ("analyzed %u", lit); - marks[idx] = true; - PUSH_STACK (kitten->analyzed, idx); - const kar *const v = vars + idx; - const unsigned tmp = v->level; - if (tmp < level) { - if (tmp > jump) { - jump = tmp; - if (size > 1) { - const unsigned other = PEEK_STACK (kitten->klause, 1); - POKE_STACK (kitten->klause, 1, lit); - lit = other; - } - } - PUSH_STACK (kitten->klause, lit); - size++; - } else - open++; - } - unsigned idx; - do { - assert (BEGIN_STACK (kitten->trail) < p); - uip = *--p; - } while (!marks[idx = uip / 2]); - assert (open); - if (!--open) - break; - reason = vars[idx].reason; - } - const unsigned not_uip = uip ^ 1; - LOG ("first UIP %u jump level %u size %u", not_uip, jump, size); - POKE_STACK (kitten->klause, 0, not_uip); - bump (kitten); - CLEAR_STACK (kitten->analyzed); - const unsigned learned_ref = new_learned_klause (kitten); - CLEAR_STACK (kitten->resolved); - CLEAR_STACK (kitten->klause); - backtrack (kitten, jump); - assign (kitten, not_uip, learned_ref); -} - -static void failing (kitten *kitten) { - assert (kitten->inconsistent == INVALID); - assert (!EMPTY_STACK (kitten->assumptions)); - assert (EMPTY_STACK (kitten->analyzed)); - assert (EMPTY_STACK (kitten->resolved)); - assert (EMPTY_STACK (kitten->klause)); - LOG ("analyzing failing assumptions"); - const value *const values = kitten->values; - const kar *const vars = kitten->vars; - unsigned failed_clashing = INVALID; - unsigned first_failed = INVALID; - unsigned failed_unit = INVALID; - for (all_stack (unsigned, lit, kitten->assumptions)) { - if (values[lit] >= 0) - continue; - if (first_failed == INVALID) - first_failed = lit; - const unsigned failed_idx = lit / 2; - const kar *const failed_var = vars + failed_idx; - if (!failed_var->level) { - failed_unit = lit; - break; - } - if (failed_clashing == INVALID && failed_var->reason == INVALID) - failed_clashing = lit; - } - unsigned failed; - if (failed_unit != INVALID) - failed = failed_unit; - else if (failed_clashing != INVALID) - failed = failed_clashing; - else - failed = first_failed; - assert (failed != INVALID); - const unsigned failed_idx = failed / 2; - const kar *const failed_var = vars + failed_idx; - const unsigned failed_reason = failed_var->reason; - LOG ("first failed assumption %u", failed); - kitten->failed[failed] = true; - - if (failed_unit != INVALID) { - assert (dereference_klause (kitten, failed_reason)->size == 1); - LOG ("root-level falsified assumption %u", failed); - kitten->failing = failed_reason; - ROG (kitten->failing, "failing reason"); - return; - } - - const unsigned not_failed = failed ^ 1; - if (failed_clashing != INVALID) { - LOG ("clashing with negated assumption %u", not_failed); - kitten->failed[not_failed] = true; - assert (kitten->failing == INVALID); - return; - } - - value *marks = kitten->marks; - assert (!marks[failed_idx]); - marks[failed_idx] = true; - PUSH_STACK (kitten->analyzed, failed_idx); - PUSH_STACK (kitten->klause, not_failed); - - unsigneds work; - INIT_STACK (work); - - LOGLITS (BEGIN_STACK (kitten->trail), SIZE_STACK (kitten->trail), - "trail"); - - assert (SIZE_STACK (kitten->trail)); - unsigned const *p = END_STACK (kitten->trail); - unsigned open = 1; - for (;;) { - if (!open) - break; - open--; - unsigned idx, uip; - do { - assert (BEGIN_STACK (kitten->trail) < p); - uip = *--p; - } while (!marks[idx = uip / 2]); - - const kar *var = vars + idx; - const unsigned reason = var->reason; - if (reason == INVALID) { - unsigned lit = 2 * idx; - if (values[lit] < 0) - lit ^= 1; - LOG ("failed assumption %u", lit); - assert (!kitten->failed[lit]); - kitten->failed[lit] = true; - const unsigned not_lit = lit ^ 1; - PUSH_STACK (kitten->klause, not_lit); - } else { - ROG (reason, "analyzing"); - PUSH_STACK (kitten->resolved, reason); - klause *c = dereference_klause (kitten, reason); - for (all_literals_in_klause (other, c)) { - const unsigned other_idx = other / 2; - if (marks[other_idx]) - continue; - assert (other_idx != idx); - marks[other_idx] = true; - assert (values[other]); - if (vars[other_idx].level) - open++; - else - PUSH_STACK (work, other_idx); - PUSH_STACK (kitten->analyzed, other_idx); - - LOG ("analyzing final literal %u", other ^ 1); - } - } - } - for (size_t next = 0; next < SIZE_STACK (work); next++) { - const unsigned idx = PEEK_STACK (work, next); - const kar *var = vars + idx; - const unsigned reason = var->reason; - if (reason == INVALID) { - unsigned lit = 2 * idx; - if (values[lit] < 0) - lit ^= 1; - LOG ("failed assumption %u", lit); - assert (!kitten->failed[lit]); - kitten->failed[lit] = true; - const unsigned not_lit = lit ^ 1; - PUSH_STACK (kitten->klause, not_lit); - } else { - ROG (reason, "analyzing unit"); - PUSH_STACK (kitten->resolved, reason); - } - } - - // this is bfs not dfs so it does not work for lrat :/ - /* - for (size_t next = 0; next < SIZE_STACK (kitten->analyzed); next++) { - const unsigned idx = PEEK_STACK (kitten->analyzed, next); - assert (marks[idx]); - const kar *var = vars + idx; - const unsigned reason = var->reason; - if (reason == INVALID) { - unsigned lit = 2 * idx; - if (values[lit] < 0) - lit ^= 1; - LOG ("failed assumption %u", lit); - assert (!kitten->failed[lit]); - kitten->failed[lit] = true; - const unsigned not_lit = lit ^ 1; - PUSH_STACK (kitten->klause, not_lit); - } else { - ROG (reason, "analyzing"); - PUSH_STACK (kitten->resolved, reason); - klause *c = dereference_klause (kitten, reason); - for (all_literals_in_klause (other, c)) { - const unsigned other_idx = other / 2; - if (other_idx == idx) - continue; - if (marks[other_idx]) - continue; - marks[other_idx] = true; - PUSH_STACK (kitten->analyzed, other_idx); - LOG ("analyzing final literal %u", other ^ 1); - } - } - } - */ - - for (all_stack (unsigned, idx, kitten->analyzed)) - assert (marks[idx]), marks[idx] = 0; - CLEAR_STACK (kitten->analyzed); - - RELEASE_STACK (work); - - const size_t resolved = SIZE_STACK (kitten->resolved); - assert (resolved); - - if (resolved == 1) { - kitten->failing = PEEK_STACK (kitten->resolved, 0); - ROG (kitten->failing, "reusing as core"); - } else { - kitten->failing = new_learned_klause (kitten); - ROG (kitten->failing, "new core"); - } - - CLEAR_STACK (kitten->resolved); - CLEAR_STACK (kitten->klause); -} - -static void flush_trail (kitten *kitten) { - unsigneds *trail = &kitten->trail; - LOG ("flushing %zu root-level literals from trail", SIZE_STACK (*trail)); - assert (!kitten->level); - kitten->propagated = 0; - CLEAR_STACK (*trail); -} - -static int decide (kitten *kitten) { - if (!kitten->level && !EMPTY_STACK (kitten->trail)) - flush_trail (kitten); - - const value *const values = kitten->values; - unsigned decision = INVALID; - const size_t assumptions = SIZE_STACK (kitten->assumptions); - while (kitten->level < assumptions) { - unsigned assumption = PEEK_STACK (kitten->assumptions, kitten->level); - value value = values[assumption]; - if (value < 0) { - LOG ("found failing assumption %u", assumption); - failing (kitten); - return 20; - } else if (value > 0) { - - kitten->level++; - LOG ("pseudo decision level %u for already satisfied assumption %u", - kitten->level, assumption); - } else { - decision = assumption; - LOG ("using assumption %u as decision", decision); - break; - } - } - - if (!kitten->unassigned) - return 10; - - if (KITTEN_TICKS >= kitten->limits.ticks) { - LOG ("ticks limit %" PRIu64 " hit after %" PRIu64 " ticks", - kitten->limits.ticks, KITTEN_TICKS); - return -1; - } - - if (kitten->terminator && kitten->terminator (kitten->terminator_data)) { - LOG ("terminator requested termination"); - return -1; - } - - if (decision == INVALID) { - unsigned idx = kitten->queue.search; - const kink *const links = kitten->links; - for (;;) { - assert (idx != INVALID); - if (!values[2 * idx]) - break; - idx = links[idx].prev; - } - update_search (kitten, idx); - const unsigned phase = kitten->phases[idx]; - decision = 2 * idx + phase; - LOG ("decision %u variable %u phase %u", decision, idx, phase); - } - INC (kitten_decisions); - kitten->level++; - assign (kitten, decision, INVALID); - return 0; -} - -static void inconsistent (kitten *kitten, unsigned ref) { - assert (ref != INVALID); - assert (kitten->inconsistent == INVALID); - - if (!kitten->antecedents) { - kitten->inconsistent = ref; - ROG (ref, "registering inconsistent virtually empty"); - return; - } - - unsigneds *analyzed = &kitten->analyzed; - unsigneds *resolved = &kitten->resolved; - - assert (EMPTY_STACK (*analyzed)); - assert (EMPTY_STACK (*resolved)); - - value *marks = kitten->marks; - const kar *const vars = kitten->vars; - unsigned next = 0; - - for (;;) { - assert (ref != INVALID); - klause *c = dereference_klause (kitten, ref); - assert (c); - ROG (ref, "analyzing inconsistent"); - PUSH_STACK (*resolved, ref); - for (all_literals_in_klause (lit, c)) { - const unsigned idx = lit / 2; - assert (!vars[idx].level); - if (marks[idx]) - continue; - assert (kitten->values[lit] < 0); - LOG ("analyzed %u", lit); - marks[idx] = true; - PUSH_STACK (kitten->analyzed, idx); - } - if (next == SIZE_STACK (kitten->analyzed)) - break; - const unsigned idx = PEEK_STACK (kitten->analyzed, next); - next++; - const kar *const v = vars + idx; - assert (!v->level); - ref = v->reason; - } - assert (EMPTY_STACK (kitten->klause)); - ref = new_learned_klause (kitten); - ROG (ref, "registering final inconsistent empty"); - kitten->inconsistent = ref; - - for (all_stack (unsigned, idx, *analyzed)) - marks[idx] = 0; - - CLEAR_STACK (*analyzed); - CLEAR_STACK (*resolved); -} - -static int propagate_units (kitten *kitten) { - if (kitten->inconsistent != INVALID) - return 20; - - if (EMPTY_STACK (kitten->units)) { - LOG ("no root level unit clauses"); - return 0; - } - - LOG ("propagating %zu root level unit clauses", - SIZE_STACK (kitten->units)); - - const value *const values = kitten->values; - - for (size_t next = 0; next < SIZE_STACK (kitten->units); next++) { - const unsigned ref = PEEK_STACK (kitten->units, next); - assert (ref != INVALID); - klause *c = dereference_klause (kitten, ref); - assert (c->size == 1); - ROG (ref, "propagating unit"); - const unsigned unit = c->lits[0]; - const value value = values[unit]; - if (value > 0) - continue; - if (value < 0) { - inconsistent (kitten, ref); - return 20; - } - assign (kitten, unit, ref); - } - const unsigned conflict = propagate (kitten); - if (conflict == INVALID) - return 0; - inconsistent (kitten, conflict); - return 20; -} - -/*------------------------------------------------------------------------*/ - -static klause *begin_klauses (kitten *kitten) { - return (klause *) BEGIN_STACK (kitten->klauses); -} - -static klause *end_original_klauses (kitten *kitten) { - return (klause *) (BEGIN_STACK (kitten->klauses) + - kitten->end_original_ref); -} - -static klause *end_klauses (kitten *kitten) { - return (klause *) END_STACK (kitten->klauses); -} - -static klause *next_klause (kitten *kitten, klause *c) { - assert (begin_klauses (kitten) <= c); - assert (c < end_klauses (kitten)); - unsigned *res = c->lits + c->size; - if (kitten->antecedents && is_learned_klause (c)) - res += c->aux; - return (klause *) res; -} - -/*------------------------------------------------------------------------*/ - -static void reset_core (kitten *kitten) { - LOG ("resetting core clauses"); - size_t reset = 0; - for (all_klauses (c)) - if (is_core_klause (c)) - unset_core_klause (c), reset++; - LOG ("reset %zu core clauses", reset); - CLEAR_STACK (kitten->core); -} - -static void reset_assumptions (kitten *kitten) { - LOG ("reset %zu assumptions", SIZE_STACK (kitten->assumptions)); - while (!EMPTY_STACK (kitten->assumptions)) { - const unsigned assumption = POP_STACK (kitten->assumptions); - kitten->failed[assumption] = false; - } -#ifndef NDEBUG - for (size_t i = 0; i < kitten->size; i++) - assert (!kitten->failed[i]); -#endif - CLEAR_STACK (kitten->assumptions); - if (kitten->failing != INVALID) { - ROG (kitten->failing, "reset failed assumption reason"); - kitten->failing = INVALID; - } -} - -static void reset_incremental (kitten *kitten) { - // if (kitten->level) - completely_backtrack_to_root_level (kitten); - if (!EMPTY_STACK (kitten->assumptions)) - reset_assumptions (kitten); - else - assert (kitten->failing == INVALID); - if (kitten->status == 21) - reset_core (kitten); - UPDATE_STATUS (0); -} - -/*------------------------------------------------------------------------*/ - -static bool flip_literal (kitten *kitten, unsigned lit) { - INC (kitten_flip); - signed char *values = kitten->values; - if (values[lit] < 0) - lit ^= 1; - LOG ("trying to flip value of satisfied literal %u", lit); - assert (values[lit] > 0); - katches *watches = kitten->watches + lit; - unsigned *q = BEGIN_STACK (*watches); - const unsigned *const end_watches = END_STACK (*watches); - unsigned const *p = q; - uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; - bool res = true; - while (p != end_watches) { - const unsigned ref = *q++ = *p++; - klause *c = dereference_klause (kitten, ref); - unsigned *lits = c->lits; - const unsigned other = lits[0] ^ lits[1] ^ lit; - const value other_value = values[other]; - ticks++; - if (other_value > 0) - continue; - value replacement_value = -1; - unsigned replacement = INVALID; - const unsigned *const end_lits = lits + c->size; - unsigned *r; - for (r = lits + 2; r != end_lits; r++) { - replacement = *r; - assert (replacement != lit); - replacement_value = values[replacement]; - assert (replacement_value); - if (replacement_value > 0) - break; - } - if (replacement_value > 0) { - assert (replacement != INVALID); - ROG (ref, "unwatching %u in", lit); - lits[0] = other; - lits[1] = replacement; - *r = lit; - watch_klause (kitten, replacement, ref); - q--; - } else { - assert (replacement_value < 0); - ROG (ref, "single satisfied"); - res = false; - break; - } - } - while (p != end_watches) - *q++ = *p++; - SET_END_OF_STACK (*watches, q); - ADD (kitten_ticks, ticks); - if (res) { - LOG ("flipping value of %u", lit); - values[lit] = -1; - const unsigned not_lit = lit ^ 1; - values[not_lit] = 1; - INC (kitten_flipped); - } else - LOG ("failed to flip value of %u", lit); - return res; -} - -/*------------------------------------------------------------------------*/ - -// this cadical specific clause addition avoids copying clauses multiple -// times just to convert literals to unsigned representation. -// -static unsigned int2u (int lit) { - assert (lit != 0); - int idx = abs (lit) - 1; - return (lit < 0) + 2u * (unsigned) idx; -} - -void kitten_assume (kitten *kitten, unsigned elit) { - REQUIRE_INITIALIZED (); - if (kitten->status) - reset_incremental (kitten); - const unsigned ilit = import_literal (kitten, elit); - LOG ("registering assumption %u", ilit); - PUSH_STACK (kitten->assumptions, ilit); -} - -void kitten_assume_signed (kitten *kitten, int elit) { - unsigned kelit = int2u (elit); - kitten_assume (kitten, kelit); -} - -void kitten_clause_with_id_and_exception (kitten *kitten, unsigned id, - size_t size, - const unsigned *elits, - unsigned except) { - REQUIRE_INITIALIZED (); - if (kitten->status) - reset_incremental (kitten); - assert (EMPTY_STACK (kitten->klause)); - const unsigned *const end = elits + size; - for (const unsigned *p = elits; p != end; p++) { - const unsigned elit = *p; - if (elit == except) - continue; - const unsigned ilit = import_literal (kitten, elit); - assert (ilit < kitten->lits); - const unsigned iidx = ilit / 2; - if (kitten->marks[iidx]) - INVALID_API_USAGE ("variable '%u' of literal '%u' occurs twice", - elit / 2, elit); - kitten->marks[iidx] = true; - PUSH_STACK (kitten->klause, ilit); - } - for (unsigned *p = kitten->klause.begin; p != kitten->klause.end; p++) - kitten->marks[*p / 2] = false; - new_original_klause (kitten, id); - CLEAR_STACK (kitten->klause); -} - -void citten_clause_with_id_and_exception (kitten *kitten, unsigned id, - size_t size, const int *elits, - unsigned except) { - REQUIRE_INITIALIZED (); - if (kitten->status) - reset_incremental (kitten); - assert (EMPTY_STACK (kitten->klause)); - const int *const end = elits + size; - for (const int *p = elits; p != end; p++) { - const unsigned elit = int2u (*p); // this is the conversion - if (elit == except) - continue; - const unsigned ilit = import_literal (kitten, elit); - assert (ilit < kitten->lits); - const unsigned iidx = ilit / 2; - if (kitten->marks[iidx]) - INVALID_API_USAGE ("variable '%u' of literal '%u' occurs twice", - elit / 2, elit); - kitten->marks[iidx] = true; - PUSH_STACK (kitten->klause, ilit); - } - for (unsigned *p = kitten->klause.begin; p != kitten->klause.end; p++) - kitten->marks[*p / 2] = false; - new_original_klause (kitten, id); - CLEAR_STACK (kitten->klause); -} - -void citten_clause_with_id_and_equivalence (kitten *kitten, unsigned id, - size_t size, const int *elits, - unsigned lit, unsigned other) { - REQUIRE_INITIALIZED (); - if (kitten->status) - reset_incremental (kitten); - assert (EMPTY_STACK (kitten->klause)); - bool sat = false; - const int *const end = elits + size; - for (const int *p = elits; p != end; p++) { - const unsigned elit = int2u (*p); // this is the conversion - if (elit == (lit ^ 1u) || elit == (other ^ 1u)) - continue; - if (elit == lit || elit == other) { - sat = true; - break; - } - const unsigned ilit = import_literal (kitten, elit); - assert (ilit < kitten->lits); - const unsigned iidx = ilit / 2; - if (kitten->marks[iidx]) - INVALID_API_USAGE ("variable '%u' of literal '%u' occurs twice", - elit / 2, elit); - kitten->marks[iidx] = true; - PUSH_STACK (kitten->klause, ilit); - } - for (unsigned *p = kitten->klause.begin; p != kitten->klause.end; p++) - kitten->marks[*p / 2] = false; - if (!sat) - new_original_klause (kitten, id); - CLEAR_STACK (kitten->klause); -} - -void kitten_clause (kitten *kitten, size_t size, unsigned *elits) { - kitten_clause_with_id_and_exception (kitten, INVALID, size, elits, - INVALID); -} - -void citten_clause_with_id (kitten *kitten, unsigned id, size_t size, - int *elits) { - citten_clause_with_id_and_exception (kitten, id, size, elits, INVALID); -} - -void kitten_unit (kitten *kitten, unsigned lit) { - kitten_clause (kitten, 1, &lit); -} - -void kitten_binary (kitten *kitten, unsigned a, unsigned b) { - unsigned clause[2] = {a, b}; - kitten_clause (kitten, 2, clause); -} - -int kitten_solve (kitten *kitten) { - REQUIRE_INITIALIZED (); - if (kitten->status) - reset_incremental (kitten); - else // if (kitten->level) - completely_backtrack_to_root_level (kitten); - - LOG ("starting solving under %zu assumptions", - SIZE_STACK (kitten->assumptions)); - - INC (kitten_solved); - - int res = propagate_units (kitten); - while (!res) { - const unsigned conflict = propagate (kitten); - if (kitten->terminator && - kitten->terminator (kitten->terminator_data)) { - LOG ("terminator requested termination"); - res = -1; - break; - } - if (conflict != INVALID) { - if (kitten->level) - analyze (kitten, conflict); - else { - inconsistent (kitten, conflict); - res = 20; - } - } else - res = decide (kitten); - } - - if (res < 0) - res = 0; - - if (!res && !EMPTY_STACK (kitten->assumptions)) - reset_assumptions (kitten); - - UPDATE_STATUS (res); - - if (res == 10) - INC (kitten_sat); - else if (res == 20) - INC (kitten_unsat); - else - INC (kitten_unknown); - - LOG ("finished solving with result %d", res); - - return res; -} - -int kitten_status (kitten *kitten) { return kitten->status; } - -unsigned kitten_compute_clausal_core (kitten *kitten, - uint64_t *learned_ptr) { - REQUIRE_STATUS (20); - - if (!kitten->antecedents) - INVALID_API_USAGE ("antecedents not tracked"); - - LOG ("computing clausal core"); - - unsigneds *resolved = &kitten->resolved; - assert (EMPTY_STACK (*resolved)); - - unsigned original = 0; - uint64_t learned = 0; - - unsigned reason_ref = kitten->inconsistent; - - if (reason_ref == INVALID) { - assert (!EMPTY_STACK (kitten->assumptions)); - reason_ref = kitten->failing; - if (reason_ref == INVALID) { - LOG ("assumptions mutually inconsistent"); - goto DONE; - } - } - - PUSH_STACK (*resolved, reason_ref); - unsigneds *core = &kitten->core; - assert (EMPTY_STACK (*core)); - - while (!EMPTY_STACK (*resolved)) { - const unsigned c_ref = POP_STACK (*resolved); - if (c_ref == INVALID) { - const unsigned d_ref = POP_STACK (*resolved); - ROG (d_ref, "core[%zu]", SIZE_STACK (*core)); - PUSH_STACK (*core, d_ref); - klause *d = dereference_klause (kitten, d_ref); - assert (!is_core_klause (d)); - set_core_klause (d); - if (is_learned_klause (d)) - learned++; - else - original++; - } else { - klause *c = dereference_klause (kitten, c_ref); - if (is_core_klause (c)) - continue; - PUSH_STACK (*resolved, c_ref); - PUSH_STACK (*resolved, INVALID); - ROG (c_ref, "analyzing antecedent core"); - if (!is_learned_klause (c)) - continue; - for (all_antecedents (d_ref, c)) { - klause *d = dereference_klause (kitten, d_ref); - if (!is_core_klause (d)) - PUSH_STACK (*resolved, d_ref); - } - } - } - -DONE: - - if (learned_ptr) - *learned_ptr = learned; - - LOG ("clausal core of %u original clauses", original); - LOG ("clausal core of %" PRIu64 " learned clauses", learned); - kitten->statistics.original = original; - kitten->statistics.learned = 0; - UPDATE_STATUS (21); - - return original; -} - -void kitten_traverse_core_ids (kitten *kitten, void *state, - void (*traverse) (void *, unsigned)) { - REQUIRE_STATUS (21); - - LOG ("traversing core of original clauses"); - - unsigned traversed = 0; - - for (all_original_klauses (c)) { - // only happens for 'true' incremental calls, i.e. if add happens after - // solve - if (is_learned_klause (c)) - continue; - if (!is_core_klause (c)) - continue; - ROG (reference_klause (kitten, c), "traversing"); - traverse (state, c->aux); - traversed++; - } - - LOG ("traversed %u original core clauses", traversed); - (void) traversed; - - assert (kitten->status == 21); -} - -void kitten_traverse_core_clauses (kitten *kitten, void *state, - void (*traverse) (void *, bool, size_t, - const unsigned *)) { - REQUIRE_STATUS (21); - - LOG ("traversing clausal core"); - - unsigned traversed = 0; - - for (all_stack (unsigned, c_ref, kitten->core)) { - klause *c = dereference_klause (kitten, c_ref); - assert (is_core_klause (c)); - const bool learned = is_learned_klause (c); - unsigneds *eclause = &kitten->eclause; - assert (EMPTY_STACK (*eclause)); - for (all_literals_in_klause (ilit, c)) { - const unsigned elit = export_literal (kitten, ilit); - PUSH_STACK (*eclause, elit); - } - const size_t size = SIZE_STACK (*eclause); - const unsigned *elits = eclause->begin; - ROG (reference_klause (kitten, c), "traversing"); - traverse (state, learned, size, elits); - CLEAR_STACK (*eclause); - traversed++; - } - - LOG ("traversed %u core clauses", traversed); - (void) traversed; - - assert (kitten->status == 21); -} - -void kitten_traverse_core_clauses_with_id ( - kitten *kitten, void *state, - void (*traverse) (void *state, unsigned, bool learned, size_t, - const unsigned *)) { - REQUIRE_STATUS (21); - - LOG ("traversing clausal core"); - - unsigned traversed = 0; - - for (all_stack (unsigned, c_ref, kitten->core)) { - klause *c = dereference_klause (kitten, c_ref); - assert (is_core_klause (c)); - const bool learned = is_learned_klause (c); - unsigneds *eclause = &kitten->eclause; - assert (EMPTY_STACK (*eclause)); - for (all_literals_in_klause (ilit, c)) { - const unsigned elit = export_literal (kitten, ilit); - PUSH_STACK (*eclause, elit); - } - const size_t size = SIZE_STACK (*eclause); - const unsigned *elits = eclause->begin; - ROG (reference_klause (kitten, c), "traversing"); - unsigned ctag = learned ? 0 : c->aux; - traverse (state, ctag, learned, size, elits); - CLEAR_STACK (*eclause); - traversed++; - } - - LOG ("traversed %u core clauses", traversed); - (void) traversed; - - assert (kitten->status == 21); -} - -void kitten_trace_core (kitten *kitten, void *state, - void (*trace) (void *, unsigned, unsigned, bool, - size_t, const unsigned *, size_t, - const unsigned *)) { - REQUIRE_STATUS (21); - - LOG ("tracing clausal core"); - - unsigned traced = 0; - - for (all_stack (unsigned, c_ref, kitten->core)) { - klause *c = dereference_klause (kitten, c_ref); - assert (is_core_klause (c)); - const bool learned = is_learned_klause (c); - unsigneds *eclause = &kitten->eclause; - assert (EMPTY_STACK (*eclause)); - for (all_literals_in_klause (ilit, c)) { - const unsigned elit = export_literal (kitten, ilit); - PUSH_STACK (*eclause, elit); - } - const size_t size = SIZE_STACK (*eclause); - const unsigned *elits = eclause->begin; - - unsigneds *resolved = &kitten->resolved; - assert (EMPTY_STACK (*resolved)); - if (learned) { - for (all_antecedents (ref, c)) { - PUSH_STACK (*resolved, ref); - } - } - const size_t rsize = SIZE_STACK (*resolved); - const unsigned *rids = resolved->begin; - - unsigned cid = reference_klause (kitten, c); - unsigned ctag = learned ? 0 : c->aux; - ROG (cid, "tracing"); - trace (state, cid, ctag, learned, size, elits, rsize, rids); - CLEAR_STACK (*eclause); - CLEAR_STACK (*resolved); - traced++; - } - - LOG ("traced %u core clauses", traced); - (void) traced; - - assert (kitten->status == 21); -} - -void kitten_shrink_to_clausal_core (kitten *kitten) { - REQUIRE_STATUS (21); - - LOG ("shrinking formula to core of original clauses"); - - CLEAR_STACK (kitten->trail); - - kitten->unassigned = kitten->lits / 2; - kitten->propagated = 0; - kitten->level = 0; - - update_search (kitten, kitten->queue.last); - - memset (kitten->values, 0, kitten->lits); - - for (all_kits (lit)) - CLEAR_STACK (KATCHES (lit)); - - assert (kitten->inconsistent != INVALID); - klause *inconsistent = dereference_klause (kitten, kitten->inconsistent); - if (is_learned_klause (inconsistent) || inconsistent->size) { - ROG (kitten->inconsistent, "resetting inconsistent"); - kitten->inconsistent = INVALID; - } else - ROG (kitten->inconsistent, "keeping inconsistent"); - - CLEAR_STACK (kitten->units); - - klause *begin = begin_klauses (kitten), *q = begin; - klause const *const end = end_original_klauses (kitten); -#ifdef LOGGING - unsigned original = 0; -#endif - for (klause *c = begin, *next; c != end; c = next) { - next = next_klause (kitten, c); - // assert (!is_learned_klause (c)); not necessarily true - if (is_learned_klause (c)) - continue; - if (!is_core_klause (c)) - continue; - unset_core_klause (c); - const unsigned dst = (unsigned *) q - (unsigned *) begin; - const unsigned size = c->size; - if (!size) { - if (kitten->inconsistent != INVALID) - kitten->inconsistent = dst; - } else if (size == 1) { - PUSH_STACK (kitten->units, dst); - ROG (dst, "keeping"); - } else { - watch_klause (kitten, c->lits[0], dst); - watch_klause (kitten, c->lits[1], dst); - } - if (c == q) - q = next; - else { - const size_t bytes = (char *) next - (char *) c; - memmove (q, c, bytes); - q = (klause *) ((char *) q + bytes); - } -#ifdef LOGGING - original++; -#endif - } - SET_END_OF_STACK (kitten->klauses, (unsigned *) q); - kitten->end_original_ref = SIZE_STACK (kitten->klauses); - LOG ("end of original clauses at %zu", kitten->end_original_ref); - LOG ("%u original clauses left", original); - - CLEAR_STACK (kitten->core); - - UPDATE_STATUS (0); -} - -signed char kitten_signed_value (kitten *kitten, int selit) { - REQUIRE_STATUS (10); - const unsigned elit = int2u (selit); - const unsigned eidx = elit / 2; - if (eidx >= kitten->evars) - return 0; - unsigned iidx = kitten->import[eidx]; - if (!iidx) - return 0; - const unsigned ilit = 2 * (iidx - 1) + (elit & 1); - return kitten->values[ilit]; -} - -signed char kitten_value (kitten *kitten, unsigned elit) { - REQUIRE_STATUS (10); - const unsigned eidx = elit / 2; - if (eidx >= kitten->evars) - return 0; - unsigned iidx = kitten->import[eidx]; - if (!iidx) - return 0; - const unsigned ilit = 2 * (iidx - 1) + (elit & 1); - return kitten->values[ilit]; -} - -signed char kitten_fixed (kitten *kitten, unsigned elit) { - const unsigned eidx = elit / 2; - if (eidx >= kitten->evars) - return 0; - unsigned iidx = kitten->import[eidx]; - if (!iidx) - return 0; - iidx--; - const unsigned ilit = 2 * iidx + (elit & 1); - signed char res = kitten->values[ilit]; - if (!res) - return 0; - kar *v = kitten->vars + iidx; - if (v->level) - return 0; - return res; -} - -signed char kitten_fixed_signed (kitten *kitten, int elit) { - unsigned kelit = int2u (elit); - return kitten_fixed (kitten, kelit); -} - -bool kitten_flip_literal (kitten *kitten, unsigned elit) { - REQUIRE_STATUS (10); - const unsigned eidx = elit / 2; - if (eidx >= kitten->evars) - return false; - unsigned iidx = kitten->import[eidx]; - if (!iidx) - return false; - const unsigned ilit = 2 * (iidx - 1) + (elit & 1); - if (kitten_fixed (kitten, elit)) - return false; - return flip_literal (kitten, ilit); -} - -bool kitten_flip_signed_literal (kitten *kitten, int elit) { - REQUIRE_STATUS (10); - unsigned kelit = int2u (elit); - return kitten_flip_literal (kitten, kelit); -} - -bool kitten_failed (kitten *kitten, unsigned elit) { - REQUIRE_STATUS (20); - const unsigned eidx = elit / 2; - if (eidx >= kitten->evars) - return false; - unsigned iidx = kitten->import[eidx]; - if (!iidx) - return false; - const unsigned ilit = 2 * (iidx - 1) + (elit & 1); - return kitten->failed[ilit]; -} - -// checks both watches for clauses with only one literal positively -// assigned. if such a clause is found, return false. Otherwise fix watch -// invariant and return true -static bool prime_propagate (kitten *kitten, const unsigned idx, - void *state, const bool ignoring, - bool (*ignore) (void *, unsigned)) { - unsigned lit = 2 * idx; - unsigned conflict = INVALID; - value *values = kitten->values; - for (int i = 0; i < 2; i++) { - if (conflict != INVALID) - break; - lit = lit ^ i; - const unsigned not_lit = lit ^ 1; - katches *watches = kitten->watches + not_lit; - unsigned *q = BEGIN_STACK (*watches); - const unsigned *const end_watches = END_STACK (*watches); - unsigned const *p = q; - uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; - while (p != end_watches) { - const unsigned ref = *q++ = *p++; - klause *c = dereference_klause (kitten, ref); - if (is_learned_klause (c) || ignore (state, c->aux) == ignoring) - continue; - assert (c->size > 1); - unsigned *lits = c->lits; - const unsigned other = lits[0] ^ lits[1] ^ not_lit; - const value other_value = values[other]; - ticks++; - if (other_value > 0) - continue; - value replacement_value = -1; - unsigned replacement = INVALID; - const unsigned *const end_lits = lits + c->size; - unsigned *r; - for (r = lits + 2; r != end_lits; r++) { - replacement = *r; - replacement_value = values[replacement]; - if (replacement_value > 0) - break; - } - if (replacement_value > 0) { - assert (replacement != INVALID); - ROG (ref, "unwatching %u in", not_lit); - lits[0] = other; - lits[1] = replacement; - *r = not_lit; - watch_klause (kitten, replacement, ref); - q--; - } else { - ROG (ref, "idx %u forced prime by", idx); - conflict = ref; - break; - } - } - while (p != end_watches) - *q++ = *p++; - SET_END_OF_STACK (*watches, q); - ADD (kitten_ticks, ticks); - } - return conflict == INVALID; -} - -void kitten_add_prime_implicant (kitten *kitten, void *state, int side, - void (*add_implicant) (void *, int, size_t, - const unsigned *)) { - REQUIRE_STATUS (11); - // might be possible in some edge cases - unsigneds *prime = &kitten->prime[side]; - unsigneds *prime2 = &kitten->prime[!side]; - assert (!EMPTY_STACK (*prime) || !EMPTY_STACK (*prime2)); - CLEAR_STACK (*prime2); - - for (all_stack (unsigned, lit, *prime)) { - const unsigned not_lit = lit ^ 1; - const unsigned elit = export_literal (kitten, not_lit); - PUSH_STACK (*prime2, elit); - } - - // adds a clause which will reset kitten status and backtrack - add_implicant (state, side, SIZE_STACK (*prime2), BEGIN_STACK (*prime2)); - CLEAR_STACK (*prime); - CLEAR_STACK (*prime2); -} - -// computes two prime implicants, only considering clauses based on ignore -// return -1 if no prime implicant has been computed, otherwise returns -// index of shorter implicant. -// TODO does not work if flip has been called beforehand -int kitten_compute_prime_implicant (kitten *kitten, void *state, - bool (*ignore) (void *, unsigned)) { - REQUIRE_STATUS (10); - - value *values = kitten->values; - kar *vars = kitten->vars; - unsigneds unassigned; - INIT_STACK (unassigned); - bool limit_hit = 0; - assert (EMPTY_STACK (kitten->prime[0]) && EMPTY_STACK (kitten->prime[1])); - for (int i = 0; i < 2; i++) { - const bool ignoring = i; - for (all_stack (unsigned, lit, kitten->trail)) { - if (KITTEN_TICKS >= kitten->limits.ticks) { - LOG ("ticks limit %" PRIu64 " hit after %" PRIu64 " ticks", - kitten->limits.ticks, KITTEN_TICKS); - limit_hit = 1; - break; - } - assert (values[lit] > 0); - const unsigned idx = lit / 2; - const unsigned ref = vars[idx].reason; - assert (vars[idx].level); - klause *c = 0; - if (ref != INVALID) - c = dereference_klause (kitten, ref); - if (ref == INVALID || is_learned_klause (c) || - ignore (state, c->aux) == ignoring) { - LOG ("non-prime candidate var %d", idx); - if (prime_propagate (kitten, idx, state, ignoring, ignore)) { - values[lit] = 0; - values[lit ^ 1] = 0; - PUSH_STACK (unassigned, lit); - } else - assert (values[lit] > 0); - } - } - unsigneds *prime = &kitten->prime[i]; - // push on prime implicant stack. - for (all_kits (lit)) { - if (values[lit] > 0) - PUSH_STACK (*prime, lit); - } - // reassign all literals on - for (all_stack (unsigned, lit, unassigned)) { - assert (!values[lit]); - values[lit] = 1; - values[lit ^ 1] = -1; - } - CLEAR_STACK (unassigned); - } - RELEASE_STACK (unassigned); - - if (limit_hit) { - CLEAR_STACK (kitten->prime[0]); - CLEAR_STACK (kitten->prime[1]); - return -1; - } - // the only case when one of the prime implicants is allowed to be empty - // is if ignore returns always true or always false. - assert (!EMPTY_STACK (kitten->prime[0]) || - !EMPTY_STACK (kitten->prime[1])); - UPDATE_STATUS (11); - - int res = SIZE_STACK (kitten->prime[0]) > SIZE_STACK (kitten->prime[1]); - return res; -} - -static bool contains_blit (kitten *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 (kitten *kitten, const unsigned idx, - const unsigned blit) { - unsigned lit = 2 * idx; - unsigned conflict = INVALID; - value *values = kitten->values; - LOG ("prime propagating idx %u for blit %u", idx, blit); - for (int i = 0; i < 2; i++) { - if (conflict != INVALID) - break; - lit = lit ^ i; - if (lit == blit) - continue; - const unsigned not_lit = lit ^ 1; - katches *watches = kitten->watches + not_lit; - unsigned *q = BEGIN_STACK (*watches); - const unsigned *const end_watches = END_STACK (*watches); - unsigned const *p = q; - uint64_t ticks = (((char *) end_watches - (char *) q) >> 7) + 1; - while (p != end_watches) { - const unsigned ref = *q++ = *p++; - klause *c = dereference_klause (kitten, ref); - if (is_learned_klause (c)) - continue; - ROG (ref, "checking with blit %u", blit); - assert (c->size > 1); - unsigned *lits = c->lits; - const unsigned other = lits[0] ^ lits[1] ^ not_lit; - const value other_value = values[other]; - ticks++; - bool use = other == blit || not_lit == blit; - if (other_value > 0) - continue; - value replacement_value = -1; - unsigned replacement = INVALID; - const unsigned *const end_lits = lits + c->size; - unsigned *r; - for (r = lits + 2; r != end_lits; r++) { - replacement = *r; - replacement_value = values[replacement]; - use = use || replacement == blit; - if (replacement_value > 0) - break; - } - if (replacement_value > 0) { - assert (replacement != INVALID); - ROG (ref, "unwatching %u in", not_lit); - lits[0] = other; - lits[1] = replacement; - *r = not_lit; - watch_klause (kitten, replacement, ref); - q--; - } else if (!use) { - continue; - } else { - ROG (ref, "idx %u forced prime by", idx); - conflict = ref; - break; - } - } - while (p != end_watches) - *q++ = *p++; - SET_END_OF_STACK (*watches, q); - ADD (kitten_ticks, ticks); - } - return conflict == INVALID; -} - -static int compute_prime_implicant_for (kitten *kitten, unsigned blit) { - value *values = kitten->values; - kar *vars = kitten->vars; - unsigneds unassigned; - INIT_STACK (unassigned); - bool limit_hit = false; - assert (EMPTY_STACK (kitten->prime[0]) && EMPTY_STACK (kitten->prime[1])); - for (int i = 0; i < 2; i++) { - const unsigned block = blit ^ i; - const bool ignoring = i; - if (prime_propagate_blit (kitten, block / 2, block)) { - value tmp = values[blit]; - assert (tmp); - values[blit] = 0; - values[blit ^ 1] = 0; - PUSH_STACK (unassigned, tmp > 0 ? blit : blit ^ 1); - PUSH_STACK (kitten->prime[i], block); // will be negated! - } else - assert (false); - for (all_stack (unsigned, lit, kitten->trail)) { - if (KITTEN_TICKS >= kitten->limits.ticks) { - LOG ("ticks limit %" PRIu64 " hit after %" PRIu64 " ticks", - kitten->limits.ticks, KITTEN_TICKS); - limit_hit = true; - break; - } - if (!values[lit]) - continue; - assert (values[lit]); // not true when flipping is involved - const unsigned idx = lit / 2; - const unsigned ref = vars[idx].reason; - assert (vars[idx].level); - LOG ("non-prime candidate var %d", idx); - if (prime_propagate_blit (kitten, idx, block)) { - value tmp = values[lit]; - assert (tmp); - values[lit] = 0; - values[lit ^ 1] = 0; - PUSH_STACK (unassigned, tmp > 0 ? lit : lit ^ 1); - } - } - unsigneds *prime = &kitten->prime[i]; - // push on prime implicant stack. - for (all_kits (lit)) { - if (values[lit] > 0) - PUSH_STACK (*prime, lit); - } - // reassign all literals on - for (all_stack (unsigned, lit, unassigned)) { - assert (!values[lit]); - values[lit] = 1; - values[lit ^ 1] = -1; - } - CLEAR_STACK (unassigned); - } - RELEASE_STACK (unassigned); - - if (limit_hit) { - CLEAR_STACK (kitten->prime[0]); - CLEAR_STACK (kitten->prime[1]); - return -1; - } - // the only case when one of the prime implicants is allowed to be empty - // is if ignore returns always true or always false. - assert (!EMPTY_STACK (kitten->prime[0]) || - !EMPTY_STACK (kitten->prime[1])); - LOGLITS (BEGIN_STACK (kitten->prime[0]), SIZE_STACK (kitten->prime[0]), - "first implicant %u", blit); - LOGLITS (BEGIN_STACK (kitten->prime[1]), SIZE_STACK (kitten->prime[1]), - "second implicant %u", blit ^ 1); - UPDATE_STATUS (11); - - int res = SIZE_STACK (kitten->prime[0]) > SIZE_STACK (kitten->prime[1]); - return res; -} - -int kitten_flip_and_implicant_for_signed_literal (kitten *kitten, - int elit) { - REQUIRE_STATUS (10); - unsigned kelit = int2u (elit); - if (!kitten_flip_literal (kitten, kelit)) { - return -2; - } - const unsigned eidx = kelit / 2; - unsigned iidx = kitten->import[eidx]; - assert (iidx); - const unsigned ilit = 2 * (iidx - 1) + (kelit & 1); - return compute_prime_implicant_for (kitten, ilit); -} diff --git a/src/sat/cadical/kitten.h b/src/sat/cadical/kitten.h index 3132cda4d..c70d47b0d 100644 --- a/src/sat/cadical/kitten.h +++ b/src/sat/cadical/kitten.h @@ -1,97 +1,95 @@ -#ifndef _kitten_h_INCLUDED -#define _kitten_h_INCLUDED +#ifndef _cadical_kitten_h_INCLUDED +#define _cadical_kitten_h_INCLUDED + +#include "global.h" #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> -#ifdef __cplusplus -extern "C" { -#endif +ABC_NAMESPACE_HEADER_START -typedef struct kitten kitten; +typedef struct cadical_kitten cadical_kitten; -kitten *kitten_init (void); -void kitten_clear (kitten *); -void kitten_release (kitten *); +cadical_kitten *cadical_kitten_init (void); +void cadical_kitten_clear (cadical_kitten *); +void cadical_kitten_release (cadical_kitten *); #ifdef LOGGING -void kitten_set_logging (kitten *kitten); +void cadical_kitten_set_logging (cadical_kitten *cadical_kitten); #endif -void kitten_track_antecedents (kitten *); +void cadical_kitten_track_antecedents (cadical_kitten *); -void kitten_shuffle_clauses (kitten *); -void kitten_flip_phases (kitten *); -void kitten_randomize_phases (kitten *); +void cadical_kitten_shuffle_clauses (cadical_kitten *); +void cadical_kitten_flip_phases (cadical_kitten *); +void cadical_kitten_randomize_phases (cadical_kitten *); -void kitten_assume (kitten *, unsigned lit); -void kitten_assume_signed (kitten *, int lit); +void cadical_kitten_assume (cadical_kitten *, unsigned lit); +void cadical_kitten_assume_signed (cadical_kitten *, int lit); -void kitten_clause (kitten *, size_t size, unsigned *); -void citten_clause_with_id (kitten *, unsigned id, size_t size, int *); -void kitten_unit (kitten *, unsigned); -void kitten_binary (kitten *, unsigned, unsigned); +void cadical_kitten_clause (cadical_kitten *, size_t size, unsigned *); +void citten_clause_with_id (cadical_kitten *, unsigned id, size_t size, int *); +void cadical_kitten_unit (cadical_kitten *, unsigned); +void cadical_kitten_binary (cadical_kitten *, unsigned, unsigned); -void kitten_clause_with_id_and_exception (kitten *, unsigned id, +void cadical_kitten_clause_with_id_and_exception (cadical_kitten *, unsigned id, size_t size, const unsigned *, unsigned except); -void citten_clause_with_id_and_exception (kitten *, unsigned id, +void citten_clause_with_id_and_exception (cadical_kitten *, unsigned id, size_t size, const int *, unsigned except); -void citten_clause_with_id_and_equivalence (kitten *, unsigned id, +void citten_clause_with_id_and_equivalence (cadical_kitten *, unsigned id, size_t size, const int *, unsigned, unsigned); -void kitten_no_ticks_limit (kitten *); -void kitten_set_ticks_limit (kitten *, uint64_t); -uint64_t kitten_current_ticks (kitten *); +void cadical_kitten_no_ticks_limit (cadical_kitten *); +void cadical_kitten_set_ticks_limit (cadical_kitten *, uint64_t); +uint64_t cadical_kitten_current_ticks (cadical_kitten *); -void kitten_no_terminator (kitten *); -void kitten_set_terminator (kitten *, void *, int (*) (void *)); +void cadical_kitten_no_terminator (cadical_kitten *); +void cadical_kitten_set_terminator (cadical_kitten *, void *, int (*) (void *)); -int kitten_solve (kitten *); -int kitten_status (kitten *); +int cadical_kitten_solve (cadical_kitten *); +int cadical_kitten_status (cadical_kitten *); -signed char kitten_value (kitten *, unsigned); -signed char kitten_signed_value (kitten *, int); // converts second argument -signed char kitten_fixed (kitten *, unsigned); -signed char kitten_fixed_signed (kitten *, int); // converts -bool kitten_failed (kitten *, unsigned); -bool kitten_flip_literal (kitten *, unsigned); -bool kitten_flip_signed_literal (kitten *, int); +signed char cadical_kitten_value (cadical_kitten *, unsigned); +signed char cadical_kitten_signed_value (cadical_kitten *, int); // converts second argument +signed char cadical_kitten_fixed (cadical_kitten *, unsigned); +signed char cadical_kitten_fixed_signed (cadical_kitten *, int); // converts +bool cadical_kitten_failed (cadical_kitten *, unsigned); +bool cadical_kitten_flip_literal (cadical_kitten *, unsigned); +bool cadical_kitten_flip_signed_literal (cadical_kitten *, int); -unsigned kitten_compute_clausal_core (kitten *, uint64_t *learned); -void kitten_shrink_to_clausal_core (kitten *); +unsigned cadical_kitten_compute_clausal_core (cadical_kitten *, uint64_t *learned); +void cadical_kitten_shrink_to_clausal_core (cadical_kitten *); -void kitten_traverse_core_ids (kitten *, void *state, +void cadical_kitten_traverse_core_ids (cadical_kitten *, void *state, void (*traverse) (void *state, unsigned id)); -void kitten_traverse_core_clauses (kitten *, void *state, +void cadical_kitten_traverse_core_clauses (cadical_kitten *, void *state, void (*traverse) (void *state, bool learned, size_t, const unsigned *)); -void kitten_traverse_core_clauses_with_id ( - kitten *, void *state, +void cadical_kitten_traverse_core_clauses_with_id ( + cadical_kitten *, void *state, void (*traverse) (void *state, unsigned, bool learned, size_t, const unsigned *)); -void kitten_trace_core (kitten *, void *state, +void cadical_kitten_trace_core (cadical_kitten *, void *state, void (*trace) (void *, unsigned, unsigned, bool, size_t, const unsigned *, size_t, const unsigned *)); -int kitten_compute_prime_implicant (kitten *kitten, void *state, +int cadical_kitten_compute_prime_implicant (cadical_kitten *cadical_kitten, void *state, bool (*ignore) (void *, unsigned)); -void kitten_add_prime_implicant (kitten *kitten, void *state, int side, +void cadical_kitten_add_prime_implicant (cadical_kitten *cadical_kitten, void *state, int side, void (*add_implicant) (void *, int, size_t, const unsigned *)); -int kitten_flip_and_implicant_for_signed_literal (kitten *kitten, int elit); +int cadical_kitten_flip_and_implicant_for_signed_literal (cadical_kitten *cadical_kitten, int elit); -#ifdef __cplusplus -} -#endif +ABC_NAMESPACE_HEADER_END #endif diff --git a/src/sat/cadical/level.hpp b/src/sat/cadical/level.hpp index cf20a77b1..bbfe774ff 100644 --- a/src/sat/cadical/level.hpp +++ b/src/sat/cadical/level.hpp @@ -1,8 +1,12 @@ #ifndef _level_hpp_INCLUDED #define _level_hpp_INCLUDED +#include "global.h" + #include <climits> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { // For each new decision we increase the decision level and push a 'Level' @@ -30,4 +34,6 @@ struct Level { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/lidruptracer.hpp b/src/sat/cadical/lidruptracer.hpp index 196e74670..02cd33fcd 100644 --- a/src/sat/cadical/lidruptracer.hpp +++ b/src/sat/cadical/lidruptracer.hpp @@ -1,6 +1,10 @@ #ifndef _lidruptracer_h_INCLUDED #define _lidruptracer_h_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + class FileTracer; namespace CaDiCaL { @@ -50,7 +54,7 @@ class LidrupTracer : public FileTracer { bool find_and_delete (const int64_t); // find clause position in hash table -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t added, deleted, weakened, restore, original, solved, batched; #endif @@ -104,7 +108,7 @@ public: // logging and file io void connect_internal (Internal *i) override; -#ifndef QUIET +#ifndef CADICAL_QUIET void print_statistics (); #endif bool closed () override; @@ -114,4 +118,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/limit.hpp b/src/sat/cadical/limit.hpp index 89004d36d..0f6923214 100644 --- a/src/sat/cadical/limit.hpp +++ b/src/sat/cadical/limit.hpp @@ -1,9 +1,13 @@ #ifndef _limit_hpp_INCLUDED #define _limit_hpp_INCLUDED +#include "global.h" + #include <cstdint> #include <limits> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct Internal; @@ -153,4 +157,6 @@ struct Inc { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/logging.hpp b/src/sat/cadical/logging.hpp index 46b0e07bf..edbd60f8c 100644 --- a/src/sat/cadical/logging.hpp +++ b/src/sat/cadical/logging.hpp @@ -1,6 +1,8 @@ #ifndef _logging_hpp_INCLUDED #define _logging_hpp_INCLUDED +#include "global.h" + /*------------------------------------------------------------------------*/ #ifdef LOGGING /*------------------------------------------------------------------------*/ @@ -8,6 +10,8 @@ #include <cstdint> #include <vector> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { // For debugging purposes and to help understanding what the solver is doing @@ -82,6 +86,8 @@ struct Logger { #define LOGLIT(lit) Logger::loglit (internal, lit).c_str () +ABC_NAMESPACE_CXX_HEADER_END + /*------------------------------------------------------------------------*/ #else // end of 'then' part of 'ifdef LOGGING' /*------------------------------------------------------------------------*/ diff --git a/src/sat/cadical/lratchecker.hpp b/src/sat/cadical/lratchecker.hpp index ff9a5ba90..0e791e9f4 100644 --- a/src/sat/cadical/lratchecker.hpp +++ b/src/sat/cadical/lratchecker.hpp @@ -1,9 +1,13 @@ #ifndef _lratchecker_hpp_INCLUDED #define _lratchecker_hpp_INCLUDED +#include "global.h" + /*------------------------------------------------------------------------*/ #include <unordered_map> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -161,4 +165,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/lrattracer.hpp b/src/sat/cadical/lrattracer.hpp index 208cf81e2..9d8e92b6a 100644 --- a/src/sat/cadical/lrattracer.hpp +++ b/src/sat/cadical/lrattracer.hpp @@ -1,6 +1,10 @@ #ifndef _lrattracer_h_INCLUDED #define _lrattracer_h_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { class LratTracer : public FileTracer { @@ -9,7 +13,7 @@ class LratTracer : public FileTracer { File *file; bool binary; -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t added, deleted; #endif int64_t latest_id; @@ -44,7 +48,7 @@ public: void report_status (int, int64_t) override {} // skip -#ifndef QUIET +#ifndef CADICAL_QUIET void print_statistics (); #endif bool closed () override; @@ -54,4 +58,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/message.hpp b/src/sat/cadical/message.hpp index ca2c00587..e0209fed8 100644 --- a/src/sat/cadical/message.hpp +++ b/src/sat/cadical/message.hpp @@ -1,11 +1,15 @@ #ifndef _message_h_INCLUDED #define _message_h_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + /*------------------------------------------------------------------------*/ // Macros for compact message code. -#ifndef QUIET +#ifndef CADICAL_QUIET #define LINE() \ do { \ @@ -62,4 +66,6 @@ /*------------------------------------------------------------------------*/ +ABC_NAMESPACE_CXX_HEADER_END + #endif // ifndef _message_h_INCLUDED diff --git a/src/sat/cadical/module.make b/src/sat/cadical/module.make new file mode 100644 index 000000000..74f5d23cb --- /dev/null +++ b/src/sat/cadical/module.make @@ -0,0 +1,91 @@ +SRC +=src/sat/cadical/cadicalSolver.c \ +src/sat/cadical/cadicalTest.c \ +src/sat/cadical/cadical_analyze.cpp \ +src/sat/cadical/cadical_arena.cpp \ +src/sat/cadical/cadical_assume.cpp \ +src/sat/cadical/cadical_averages.cpp \ +src/sat/cadical/cadical_backtrack.cpp \ +src/sat/cadical/cadical_backward.cpp \ +src/sat/cadical/cadical_bins.cpp \ +src/sat/cadical/cadical_block.cpp \ +src/sat/cadical/cadical_ccadical.cpp \ +src/sat/cadical/cadical_checker.cpp \ +src/sat/cadical/cadical_clause.cpp \ +src/sat/cadical/cadical_collect.cpp \ +src/sat/cadical/cadical_compact.cpp \ +src/sat/cadical/cadical_condition.cpp \ +src/sat/cadical/cadical_config.cpp \ +src/sat/cadical/cadical_congruence.cpp \ +src/sat/cadical/cadical_constrain.cpp \ +src/sat/cadical/cadical_contract.cpp \ +src/sat/cadical/cadical_cover.cpp \ +src/sat/cadical/cadical_decide.cpp \ +src/sat/cadical/cadical_decompose.cpp \ +src/sat/cadical/cadical_deduplicate.cpp \ +src/sat/cadical/cadical_definition.cpp \ +src/sat/cadical/cadical_drattracer.cpp \ +src/sat/cadical/cadical_elim.cpp \ +src/sat/cadical/cadical_elimfast.cpp \ +src/sat/cadical/cadical_ema.cpp \ +src/sat/cadical/cadical_extend.cpp \ +src/sat/cadical/cadical_external.cpp \ +src/sat/cadical/cadical_external_propagate.cpp \ +src/sat/cadical/cadical_factor.cpp \ +src/sat/cadical/cadical_file.cpp \ +src/sat/cadical/cadical_flags.cpp \ +src/sat/cadical/cadical_flip.cpp \ +src/sat/cadical/cadical_format.cpp \ +src/sat/cadical/cadical_frattracer.cpp \ +src/sat/cadical/cadical_gates.cpp \ +src/sat/cadical/cadical_idruptracer.cpp \ +src/sat/cadical/cadical_instantiate.cpp \ +src/sat/cadical/cadical_internal.cpp \ +src/sat/cadical/cadical_ipasir.cpp \ +src/sat/cadical/cadical_lidruptracer.cpp \ +src/sat/cadical/cadical_limit.cpp \ +src/sat/cadical/cadical_logging.cpp \ +src/sat/cadical/cadical_lookahead.cpp \ +src/sat/cadical/cadical_lratchecker.cpp \ +src/sat/cadical/cadical_lrattracer.cpp \ +src/sat/cadical/cadical_lucky.cpp \ +src/sat/cadical/cadical_message.cpp \ +src/sat/cadical/cadical_minimize.cpp \ +src/sat/cadical/cadical_occs.cpp \ +src/sat/cadical/cadical_options.cpp \ +src/sat/cadical/cadical_parse.cpp \ +src/sat/cadical/cadical_phases.cpp \ +src/sat/cadical/cadical_probe.cpp \ +src/sat/cadical/cadical_profile.cpp \ +src/sat/cadical/cadical_proof.cpp \ +src/sat/cadical/cadical_propagate.cpp \ +src/sat/cadical/cadical_queue.cpp \ +src/sat/cadical/cadical_random.cpp \ +src/sat/cadical/cadical_reap.cpp \ +src/sat/cadical/cadical_reduce.cpp \ +src/sat/cadical/cadical_rephase.cpp \ +src/sat/cadical/cadical_report.cpp \ +src/sat/cadical/cadical_resources.cpp \ +src/sat/cadical/cadical_restart.cpp \ +src/sat/cadical/cadical_restore.cpp \ +src/sat/cadical/cadical_score.cpp \ +src/sat/cadical/cadical_shrink.cpp \ +src/sat/cadical/cadical_signal.cpp \ +src/sat/cadical/cadical_solution.cpp \ +src/sat/cadical/cadical_solver.cpp \ +src/sat/cadical/cadical_stable.cpp \ +src/sat/cadical/cadical_stats.cpp \ +src/sat/cadical/cadical_subsume.cpp \ +src/sat/cadical/cadical_sweep.cpp \ +src/sat/cadical/cadical_terminal.cpp \ +src/sat/cadical/cadical_ternary.cpp \ +src/sat/cadical/cadical_tier.cpp \ +src/sat/cadical/cadical_transred.cpp \ +src/sat/cadical/cadical_unstable.cpp \ +src/sat/cadical/cadical_util.cpp \ +src/sat/cadical/cadical_var.cpp \ +src/sat/cadical/cadical_veripbtracer.cpp \ +src/sat/cadical/cadical_version.cpp \ +src/sat/cadical/cadical_vivify.cpp \ +src/sat/cadical/cadical_walk.cpp \ +src/sat/cadical/cadical_watch.cpp \ +src/sat/cadical/cadical_kitten.c diff --git a/src/sat/cadical/occs.hpp b/src/sat/cadical/occs.hpp index 6988725d6..28a9246bc 100644 --- a/src/sat/cadical/occs.hpp +++ b/src/sat/cadical/occs.hpp @@ -1,8 +1,12 @@ #ifndef _occs_h_INCLUDED #define _occs_h_INCLUDED +#include "global.h" + #include <vector> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { // Full occurrence lists used in a one-watch scheme for all clauses in @@ -24,7 +28,7 @@ inline void remove_occs (Occs &os, Clause *c) { if (c == d) i--; } - assert (i + 1 == end); + CADICAL_assert (i + 1 == end); os.resize (i - os.begin ()); } @@ -33,4 +37,6 @@ typedef Occs::const_iterator const_occs_iterator; } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/options.hpp b/src/sat/cadical/options.hpp index 79005a952..1527da2e1 100644 --- a/src/sat/cadical/options.hpp +++ b/src/sat/cadical/options.hpp @@ -1,6 +1,10 @@ #ifndef _options_hpp_INCLUDED #define _options_hpp_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + /*------------------------------------------------------------------------*/ // In order to add a new option, simply add a new line below. Make sure that @@ -81,9 +85,9 @@ OPTION( elimbackward, 1, 0, 1,0,0,1, "eager backward subsumption") \ OPTION( elimboundmax, 16, -1,2e6,1,0,1, "maximum elimination bound") \ OPTION( elimboundmin, 0, -1,2e6,0,0,1, "minimum elimination bound") \ OPTION( elimclslim, 1e2, 2,2e9,2,0,1, "resolvent size limit") \ -OPTION( elimdef, 0, 0, 1,0,0,1, "mine definitions with kitten") \ +OPTION( elimdef, 0, 0, 1,0,0,1, "mine definitions with cadical_kitten") \ OPTION( elimdefcores, 1, 1,100,0,0,1, "number of unsat cores") \ -OPTION( elimdefticks, 2e5, 0,2e9,1,0,1, "kitten ticks limit") \ +OPTION( elimdefticks, 2e5, 0,2e9,1,0,1, "cadical_kitten ticks limit") \ OPTION( elimeffort, 1e3, 1,1e5,1,0,1, "relative efficiency per mille") \ OPTION( elimequivs, 1, 0, 1,0,0,1, "find equivalence gates") \ OPTION( elimint, 2e3, 1,2e9,0,0,1, "elimination interval") \ @@ -271,7 +275,7 @@ OPTION( walkredundant, 0, 0, 1,0,0,1, "walk redundant clauses too") \ #define LOGOPT(...) /**/ #endif -#ifdef QUIET +#ifdef CADICAL_QUIET #define QUTOPT(...) /**/ #else #define QUTOPT OPTION @@ -357,7 +361,7 @@ public: // that the following options are really allocated directly after it. // inline int &val (size_t idx) { - assert (idx < number_of_options); + CADICAL_assert (idx < number_of_options); return (&__start_of_options__ + 1)[idx]; } @@ -406,11 +410,13 @@ public: }; inline int &Option::val (Options *opts) { - assert (Options::table <= this && + CADICAL_assert (Options::table <= this && this < Options::table + number_of_options); return opts->val (this - Options::table); } } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/parse.hpp b/src/sat/cadical/parse.hpp index c90c9f5a9..a37117f43 100644 --- a/src/sat/cadical/parse.hpp +++ b/src/sat/cadical/parse.hpp @@ -1,9 +1,13 @@ #ifndef _parse_hpp_INCLUDED #define _parse_hpp_INCLUDED +#include "global.h" + #include <cassert> #include <vector> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { // Factors out common functions for parsing of DIMACS and solution files. @@ -69,4 +73,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/phases.hpp b/src/sat/cadical/phases.hpp index fe3459415..98e54d439 100644 --- a/src/sat/cadical/phases.hpp +++ b/src/sat/cadical/phases.hpp @@ -1,6 +1,10 @@ #ifndef _phases_hpp_INCLUDED #define _phases_hpp_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct Phases { @@ -15,4 +19,6 @@ struct Phases { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/profile.hpp b/src/sat/cadical/profile.hpp index 4267eb29c..f3799389b 100644 --- a/src/sat/cadical/profile.hpp +++ b/src/sat/cadical/profile.hpp @@ -1,8 +1,12 @@ #ifndef _profiles_h_INCLUDED #define _profiles_h_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + /*------------------------------------------------------------------------*/ -#ifndef QUIET +#ifndef CADICAL_QUIET /*------------------------------------------------------------------------*/ namespace CaDiCaL { @@ -131,11 +135,11 @@ struct Profiles { } // namespace CaDiCaL -#define NON_QUIET_PROFILE_CODE(CODE) CODE +#define NON_CADICAL_QUIET_PROFILE_CODE(CODE) CODE -#else // !defined(QUIET) +#else // !defined(CADICAL_QUIET) -#define NON_QUIET_PROFILE_CODE(CODE) /**/ +#define NON_CADICAL_QUIET_PROFILE_CODE(CODE) /**/ #endif @@ -145,7 +149,7 @@ struct Profiles { #define START(P) \ do { \ - NON_QUIET_PROFILE_CODE ( \ + NON_CADICAL_QUIET_PROFILE_CODE ( \ if (internal->profiles.P.level <= internal->opts.profile) \ internal->start_profiling (internal->profiles.P, \ internal->time ());) \ @@ -153,7 +157,7 @@ struct Profiles { #define STOP(P) \ do { \ - NON_QUIET_PROFILE_CODE ( \ + NON_CADICAL_QUIET_PROFILE_CODE ( \ if (internal->profiles.P.level <= internal->opts.profile) \ internal->stop_profiling (internal->profiles.P, \ internal->time ());) \ @@ -167,10 +171,10 @@ struct Profiles { #define START_SIMPLIFIER(S, M) \ do { \ - NON_QUIET_PROFILE_CODE (const double N = time (); \ + NON_CADICAL_QUIET_PROFILE_CODE (const double N = time (); \ const int L = internal->opts.profile;) \ if (!preprocessing && !lookingahead) { \ - NON_QUIET_PROFILE_CODE ( \ + NON_CADICAL_QUIET_PROFILE_CODE ( \ if (stable && internal->profiles.stable.level <= L) \ internal->stop_profiling (internal->profiles.stable, N); \ if (!stable && internal->profiles.unstable.level <= L) \ @@ -179,7 +183,7 @@ struct Profiles { internal->stop_profiling (internal->profiles.search, N);) \ reset_mode (SEARCH); \ } \ - NON_QUIET_PROFILE_CODE ( \ + NON_CADICAL_QUIET_PROFILE_CODE ( \ if (internal->profiles.simplify.level <= L) \ internal->start_profiling (internal->profiles.simplify, N); \ if (internal->profiles.S.level <= L) \ @@ -192,7 +196,7 @@ struct Profiles { #define STOP_SIMPLIFIER(S, M) \ do { \ - NON_QUIET_PROFILE_CODE ( \ + NON_CADICAL_QUIET_PROFILE_CODE ( \ const double N = time (); const int L = internal->opts.profile; \ if (internal->profiles.S.level <= L) \ internal->stop_profiling (internal->profiles.S, N); \ @@ -201,7 +205,7 @@ struct Profiles { reset_mode (M); \ reset_mode (SIMPLIFY); \ if (!preprocessing && !lookingahead) { \ - NON_QUIET_PROFILE_CODE ( \ + 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) \ @@ -218,8 +222,8 @@ struct Profiles { #define START_INNER_WALK() \ do { \ require_mode (SEARCH); \ - assert (!preprocessing); \ - NON_QUIET_PROFILE_CODE ( \ + CADICAL_assert (!preprocessing); \ + NON_CADICAL_QUIET_PROFILE_CODE ( \ const double N = time (); const int L = internal->opts.profile; \ if (stable && internal->profiles.stable.level <= L) \ internal->stop_profiling (internal->profiles.stable, N); \ @@ -236,9 +240,9 @@ struct Profiles { #define STOP_INNER_WALK() \ do { \ require_mode (SEARCH); \ - assert (!preprocessing); \ + CADICAL_assert (!preprocessing); \ reset_mode (WALK); \ - NON_QUIET_PROFILE_CODE ( \ + 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); \ @@ -255,8 +259,8 @@ struct Profiles { #define START_OUTER_WALK() \ do { \ require_mode (SEARCH); \ - assert (!preprocessing); \ - NON_QUIET_PROFILE_CODE (START (walk);) \ + CADICAL_assert (!preprocessing); \ + NON_CADICAL_QUIET_PROFILE_CODE (START (walk);) \ set_mode (WALK); \ } while (0) @@ -266,9 +270,11 @@ struct Profiles { #define STOP_OUTER_WALK() \ do { \ require_mode (SEARCH); \ - assert (!preprocessing); \ + CADICAL_assert (!preprocessing); \ reset_mode (WALK); \ - NON_QUIET_PROFILE_CODE (STOP (walk);) \ + NON_CADICAL_QUIET_PROFILE_CODE (STOP (walk);) \ } while (0) +ABC_NAMESPACE_CXX_HEADER_END + #endif // ifndef _profiles_h_INCLUDED diff --git a/src/sat/cadical/proof.hpp b/src/sat/cadical/proof.hpp index 36ae2f493..69be6b080 100644 --- a/src/sat/cadical/proof.hpp +++ b/src/sat/cadical/proof.hpp @@ -1,6 +1,10 @@ #ifndef _proof_h_INCLUDED #define _proof_h_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { /*------------------------------------------------------------------------*/ @@ -111,4 +115,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/queue.hpp b/src/sat/cadical/queue.hpp index 3b98bed17..14e1f2da7 100644 --- a/src/sat/cadical/queue.hpp +++ b/src/sat/cadical/queue.hpp @@ -1,8 +1,12 @@ #ifndef _queue_hpp_INCLUDED #define _queue_hpp_INCLUDED +#include "global.h" + #include <vector> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { // Links for double linked decision queue. @@ -61,4 +65,6 @@ struct Queue { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/radix.hpp b/src/sat/cadical/radix.hpp index a28a80aee..4c4d7b574 100644 --- a/src/sat/cadical/radix.hpp +++ b/src/sat/cadical/radix.hpp @@ -1,11 +1,15 @@ #ifndef _radix_hpp_INCLUDED #define _radix_hpp_INCLUDED +#include "global.h" + #include <cassert> #include <cstring> #include <iterator> #include <vector> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { using namespace std; @@ -42,7 +46,7 @@ template <class I, class Rank> void rsort (I first, I last, Rank rank) { typedef typename iterator_traits<I>::value_type T; typedef typename Rank::Type R; - assert (first <= last); + CADICAL_assert (first <= last); const size_t n = last - first; if (n <= 1) return; @@ -119,7 +123,7 @@ template <class I, class Rank> void rsort (I first, I last, Rank rank) { } if (!initialized) { - assert (&*c == &*a); // MS VC++ + CADICAL_assert (&*c == &*a); // MS VC++ v.resize (n); b = v.begin (); initialized = true; @@ -145,9 +149,9 @@ template <class I, class Rank> void rsort (I first, I last, Rank rank) { delete[] count; #endif -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG for (I p = first; p + 1 != last; p++) - assert (rank (p[0]) <= rank (p[1])); + CADICAL_assert (rank (p[0]) <= rank (p[1])); #endif } @@ -177,4 +181,6 @@ template <class I, class Rank> void rsort (I first, I last, Rank rank) { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/random.h b/src/sat/cadical/random.h index 7f00fbf99..62fe5ed19 100644 --- a/src/sat/cadical/random.h +++ b/src/sat/cadical/random.h @@ -1,10 +1,14 @@ #ifndef _random_h_INCLUDED #define _random_h_INCLUDED +#include "global.h" + #include <assert.h> #include <stdbool.h> #include <stdint.h> +ABC_NAMESPACE_HEADER_START + typedef uint64_t generator; static inline uint64_t kissat_next_random64 (generator *rng) { @@ -19,17 +23,17 @@ static inline unsigned kissat_next_random32 (generator *rng) { static inline unsigned kissat_pick_random (generator *rng, unsigned l, unsigned r) { - assert (l <= r); + CADICAL_assert (l <= r); if (l == r) return l; const unsigned delta = r - l; const unsigned tmp = kissat_next_random32 (rng); const double fraction = tmp / 4294967296.0; - assert (0 <= fraction), assert (fraction < 1); + CADICAL_assert (0 <= fraction), CADICAL_assert (fraction < 1); const unsigned scaled = delta * fraction; - assert (scaled < delta); + CADICAL_assert (scaled < delta); const unsigned res = l + scaled; - assert (l <= res), assert (res < r); + CADICAL_assert (l <= res), CADICAL_assert (res < r); return res; } @@ -41,4 +45,6 @@ static inline double kissat_pick_double (generator *rng) { return kissat_next_random32 (rng) / 4294967296.0; } +ABC_NAMESPACE_HEADER_END + #endif diff --git a/src/sat/cadical/random.hpp b/src/sat/cadical/random.hpp index c57c03bef..a5c78d38a 100644 --- a/src/sat/cadical/random.hpp +++ b/src/sat/cadical/random.hpp @@ -1,8 +1,12 @@ #ifndef _random_hpp_INCLUDED #define _random_hpp_INCLUDED +#include "global.h" + #include <cstdint> +ABC_NAMESPACE_CXX_HEADER_START + // Random number generator. namespace CaDiCaL { @@ -32,7 +36,7 @@ public: uint64_t next () { state *= 6364136223846793005ul; state += 1442695040888963407ul; - assert (state); + CADICAL_assert (state); return state; } @@ -50,7 +54,7 @@ public: // Generate 'int' value in the range '[l,r]'. // int pick_int (int l, int r) { - assert (l <= r); + CADICAL_assert (l <= r); const unsigned delta = 1 + r - (unsigned) l; unsigned tmp = generate (), scaled; if (delta) { @@ -59,13 +63,13 @@ public: } else scaled = tmp; const int res = scaled + l; - assert (l <= res); - assert (res <= r); + CADICAL_assert (l <= res); + CADICAL_assert (res <= r); return res; } int pick_log (int l, int r) { - assert (l <= r); + CADICAL_assert (l <= r); const unsigned delta = 1 + r - (unsigned) l; int log_delta = delta ? 0 : 32; while (log_delta < 32 && (1u << log_delta) < delta) @@ -77,22 +81,24 @@ public: if (delta) tmp %= delta; const int res = l + tmp; - assert (l <= res), assert (res <= r); + CADICAL_assert (l <= res), CADICAL_assert (res <= r); return res; } // Generate 'double' value in the range '[l,r]'. // double pick_double (double l, double r) { - assert (l <= r); + CADICAL_assert (l <= r); double res = (r - l) * generate_double (); res += l; - assert (l <= res); - assert (res <= r); + CADICAL_assert (l <= res); + CADICAL_assert (res <= r); return res; } }; } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/range.hpp b/src/sat/cadical/range.hpp index f30c34d4b..7049dab42 100644 --- a/src/sat/cadical/range.hpp +++ b/src/sat/cadical/range.hpp @@ -1,8 +1,12 @@ #ifndef _range_hpp_INCLUDED #define _range_hpp_INCLUDED +#include "global.h" + #include <cassert> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct Clause; @@ -62,9 +66,9 @@ class Range { int &n; public: - iterator begin () const { return assert (n >= 0), iterator (inc (0)); } - iterator end () const { return assert (n >= 0), iterator (inc (n)); } - Range (int &m) : n (m) { assert (m >= 0); } + iterator begin () const { return CADICAL_assert (n >= 0), iterator (inc (0)); } + iterator end () const { return CADICAL_assert (n >= 0), iterator (inc (n)); } + Range (int &m) : n (m) { CADICAL_assert (m >= 0); } }; // Same, but iterating over literals '-1,1,-2,2,....,-max_var,max_var'. @@ -89,11 +93,13 @@ class Sange { int &n; public: - iterator begin () const { return assert (n >= 0), iterator (inc (0)); } - iterator end () const { return assert (n >= 0), iterator (inc (n)); } - Sange (int &m) : n (m) { assert (m >= 0); } + iterator begin () const { return CADICAL_assert (n >= 0), iterator (inc (0)); } + iterator end () const { return CADICAL_assert (n >= 0), iterator (inc (n)); } + Sange (int &m) : n (m) { CADICAL_assert (m >= 0); } }; } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/reap.hpp b/src/sat/cadical/reap.hpp index 9986b95b9..1ea10e6e3 100644 --- a/src/sat/cadical/reap.hpp +++ b/src/sat/cadical/reap.hpp @@ -1,9 +1,13 @@ #ifndef _reap_h_INCLUDED #define _reap_h_INCLUDED +#include "global.h" + #include <cstddef> #include <vector> +ABC_NAMESPACE_CXX_HEADER_START + class Reap { public: Reap (); @@ -25,4 +29,6 @@ private: std::vector<unsigned> buckets[33]; }; +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/reluctant.hpp b/src/sat/cadical/reluctant.hpp index 0448672e4..495382b56 100644 --- a/src/sat/cadical/reluctant.hpp +++ b/src/sat/cadical/reluctant.hpp @@ -1,9 +1,13 @@ #ifndef _reluctant_hpp_INCLUDED #define _reluctant_hpp_INCLUDED +#include "global.h" + #include <cassert> #include <cstdint> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { // This is Donald Knuth's version of the Luby restart sequence which he @@ -31,7 +35,7 @@ public: Reluctant () : period (0), trigger (false) {} void enable (int p, int64_t l) { - assert (p > 0); + CADICAL_assert (p > 0); u = v = 1; period = countdown = p; trigger = false; @@ -79,4 +83,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/resources.hpp b/src/sat/cadical/resources.hpp index 377c95f3d..852668b32 100644 --- a/src/sat/cadical/resources.hpp +++ b/src/sat/cadical/resources.hpp @@ -1,8 +1,12 @@ #ifndef _resources_hpp_INCLUDED #define _resources_hpp_INCLUDED +#include "global.h" + #include <cstdint> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { double absolute_real_time (); @@ -13,4 +17,6 @@ uint64_t current_resident_set_size (); } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif // ifndef _resources_hpp_INCLUDED diff --git a/src/sat/cadical/score.hpp b/src/sat/cadical/score.hpp index 9fde98af3..88196121e 100644 --- a/src/sat/cadical/score.hpp +++ b/src/sat/cadical/score.hpp @@ -1,6 +1,10 @@ #ifndef _score_hpp_INCLUDED #define _score_hpp_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct score_smaller { @@ -13,4 +17,6 @@ typedef heap<score_smaller> ScoreSchedule; } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/signal.hpp b/src/sat/cadical/signal.hpp index 5984b66f1..1dc2fb3b7 100644 --- a/src/sat/cadical/signal.hpp +++ b/src/sat/cadical/signal.hpp @@ -1,6 +1,10 @@ #ifndef _signal_hpp_INCLUDED #define _signal_hpp_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { // Helper class for handling signals in applications. @@ -10,7 +14,7 @@ public: Handler () {} virtual ~Handler () {} virtual void catch_signal (int sig) = 0; -#ifndef __WIN32 +#ifndef WIN32 virtual void catch_alarm (); #endif }; @@ -20,7 +24,7 @@ class Signal { public: static void set (Handler *); static void reset (); -#ifndef __WIN32 +#ifndef WIN32 static void alarm (int seconds); static void reset_alarm (); #endif @@ -30,4 +34,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/stack.h b/src/sat/cadical/stack.h index 21082e525..89f270388 100644 --- a/src/sat/cadical/stack.h +++ b/src/sat/cadical/stack.h @@ -1,8 +1,12 @@ #ifndef _stack_h_INCLUDED #define _stack_h_INCLUDED +#include "global.h" + #include <stdlib.h> +ABC_NAMESPACE_HEADER_START + #define STACK(TYPE) \ struct { \ TYPE *begin; \ @@ -20,17 +24,17 @@ (S).begin = (S).end = (S).allocated = 0; \ } while (0) -#define TOP_STACK(S) (END_STACK (S)[assert (!EMPTY_STACK (S)), -1]) +#define TOP_STACK(S) (END_STACK (S)[CADICAL_assert (!EMPTY_STACK (S)), -1]) #define PEEK_STACK(S, N) \ - (BEGIN_STACK (S)[assert ((N) < SIZE_STACK (S)), (N)]) + (BEGIN_STACK (S)[CADICAL_assert ((N) < SIZE_STACK (S)), (N)]) #define POKE_STACK(S, N, E) \ do { \ PEEK_STACK (S, N) = (E); \ } while (0) -#define POP_STACK(S) (assert (!EMPTY_STACK (S)), *--(S).end) +#define POP_STACK(S) (CADICAL_assert (!EMPTY_STACK (S)), *--(S).end) #define PUSH_STACK(S, E) \ do { \ @@ -51,14 +55,14 @@ #define RESIZE_STACK(S, NEW_SIZE) \ do { \ const size_t TMP_NEW_SIZE = (NEW_SIZE); \ - assert (TMP_NEW_SIZE <= SIZE_STACK (S)); \ + CADICAL_assert (TMP_NEW_SIZE <= SIZE_STACK (S)); \ (S).end = (S).begin + TMP_NEW_SIZE; \ } while (0) #define SET_END_OF_STACK(S, P) \ do { \ - assert (BEGIN_STACK (S) <= (P)); \ - assert ((P) <= END_STACK (S)); \ + CADICAL_assert (BEGIN_STACK (S) <= (P)); \ + CADICAL_assert ((P) <= END_STACK (S)); \ if ((P) == END_STACK (S)) \ break; \ (S).end = (P); \ @@ -72,12 +76,12 @@ #define REMOVE_STACK(T, S, E) \ do { \ - assert (!EMPTY_STACK (S)); \ + CADICAL_assert (!EMPTY_STACK (S)); \ T *END_REMOVE_STACK = END_STACK (S); \ T *P_REMOVE_STACK = BEGIN_STACK (S); \ while (*P_REMOVE_STACK != (E)) { \ P_REMOVE_STACK++; \ - assert (P_REMOVE_STACK != END_REMOVE_STACK); \ + CADICAL_assert (P_REMOVE_STACK != END_REMOVE_STACK); \ } \ P_REMOVE_STACK++; \ while (P_REMOVE_STACK != END_REMOVE_STACK) { \ @@ -107,4 +111,6 @@ typedef STACK (unsigned) unsigneds; // clang-format on +ABC_NAMESPACE_HEADER_END + #endif diff --git a/src/sat/cadical/stats.hpp b/src/sat/cadical/stats.hpp index 7eb9e9be5..4a6afa9c9 100644 --- a/src/sat/cadical/stats.hpp +++ b/src/sat/cadical/stats.hpp @@ -1,10 +1,14 @@ #ifndef _stats_hpp_INCLUDED #define _stats_hpp_INCLUDED +#include "global.h" + #include <cstdint> #include <cstdlib> #include <vector> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct Internal; @@ -367,4 +371,6 @@ struct Stats { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/sweep.hpp b/src/sat/cadical/sweep.hpp index 04d40ead1..ad093e070 100644 --- a/src/sat/cadical/sweep.hpp +++ b/src/sat/cadical/sweep.hpp @@ -1,6 +1,10 @@ #ifndef _sweep_hpp_INCLUDED #define _sweep_hpp_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct Internal; @@ -8,7 +12,7 @@ struct Internal; struct sweep_proof_clause { unsigned sweep_id; // index for sweeper.clauses int64_t cad_id; // cadical id - unsigned kit_id; // kitten id + unsigned kit_id; // cadical_kitten id bool learned; vector<int> literals; vector<unsigned> chain; // lrat @@ -58,4 +62,6 @@ struct Sweeper { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/terminal.hpp b/src/sat/cadical/terminal.hpp index 71090a1d5..a99346b7c 100644 --- a/src/sat/cadical/terminal.hpp +++ b/src/sat/cadical/terminal.hpp @@ -1,6 +1,10 @@ #ifndef _terminal_hpp_INCLUDED #define _terminal_hpp_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { class Terminal { @@ -11,14 +15,14 @@ class Terminal { bool reset_on_exit; // Reset on exit. void escape () { - assert (connected); + CADICAL_assert (connected); fputs ("\033[", file); } void color (int color, bool bright) { if (!use_colors) return; - assert (connected); + CADICAL_assert (connected); escape (); fputc (bright ? '1' : '0', file); fprintf (file, ";%dm", color); @@ -95,4 +99,6 @@ extern Terminal terr; // Terminal of 'stderr' (file descriptor '2') } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/testing.hpp b/src/sat/cadical/testing.hpp index 16dabb4b4..f90569e5c 100644 --- a/src/sat/cadical/testing.hpp +++ b/src/sat/cadical/testing.hpp @@ -1,12 +1,16 @@ #ifndef _testing_hpp_INCLUDED #define _testing_hpp_INCLUDED +#include "global.h" + // This class provides access to 'internal' which should only be used for // internal testing and not for any other purpose as 'internal' is supposed // to be hidden. #include "cadical.hpp" +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { class Testing { @@ -21,4 +25,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/tracer.hpp b/src/sat/cadical/tracer.hpp index 6af089ed3..d1a9f1137 100644 --- a/src/sat/cadical/tracer.hpp +++ b/src/sat/cadical/tracer.hpp @@ -1,9 +1,13 @@ #ifndef _tracer_hpp_INCLUDED #define _tracer_hpp_INCLUDED +#include "global.h" + #include <cstdint> #include <vector> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct Internal; @@ -174,4 +178,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/util.hpp b/src/sat/cadical/util.hpp index 9a971709c..3882c6ede 100644 --- a/src/sat/cadical/util.hpp +++ b/src/sat/cadical/util.hpp @@ -1,10 +1,14 @@ #ifndef _util_hpp_INCLUDED #define _util_hpp_INCLUDED +#include "global.h" + #include <cassert> #include <cstdint> #include <vector> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { using namespace std; @@ -47,25 +51,25 @@ inline bool contained (int64_t c, int64_t l, int64_t u) { } inline bool aligned (size_t n, size_t alignment) { - assert (is_power_of_two (alignment)); + CADICAL_assert (is_power_of_two (alignment)); const size_t mask = alignment - 1; return !(n & mask); } inline size_t align (size_t n, size_t alignment) { size_t res = n; - assert (is_power_of_two (alignment)); + CADICAL_assert (is_power_of_two (alignment)); const size_t mask = alignment - 1; if (res & mask) res = (res | mask) + 1; - assert (aligned (res, alignment)); + CADICAL_assert (aligned (res, alignment)); return res; } /*------------------------------------------------------------------------*/ inline bool parity (unsigned a) { - assert (sizeof a == 4); + CADICAL_assert (sizeof a == 4); unsigned tmp = a; tmp ^= (tmp >> 16); tmp ^= (tmp >> 8); @@ -87,7 +91,7 @@ template <class T> void erase_vector (std::vector<T> &v) { if (v.capacity ()) { std::vector<T> ().swap (v); } - assert (!v.capacity ()); // not guaranteed though + CADICAL_assert (!v.capacity ()); // not guaranteed though } // The standard 'Effective STL' way (though not guaranteed) to shrink the @@ -98,7 +102,7 @@ template <class T> void shrink_vector (std::vector<T> &v) { if (v.capacity () > v.size ()) { std::vector<T> (v).swap (v); } - assert (v.capacity () == v.size ()); // not guaranteed though + CADICAL_assert (v.capacity () == v.size ()); // not guaranteed though } template <class T> @@ -164,4 +168,6 @@ uint64_t hash_string (const char *str); } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/var.hpp b/src/sat/cadical/var.hpp index 38b52c811..8793afbc4 100644 --- a/src/sat/cadical/var.hpp +++ b/src/sat/cadical/var.hpp @@ -1,6 +1,10 @@ #ifndef _var_hpp_INCLUDED #define _var_hpp_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct Clause; @@ -19,4 +23,6 @@ struct Var { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/veripbtracer.hpp b/src/sat/cadical/veripbtracer.hpp index d8e3dafdf..b9c47362f 100644 --- a/src/sat/cadical/veripbtracer.hpp +++ b/src/sat/cadical/veripbtracer.hpp @@ -1,6 +1,10 @@ #ifndef _veripbtracer_h_INCLUDED #define _veripbtracer_h_INCLUDED +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + class FileTracer; namespace CaDiCaL { @@ -15,7 +19,7 @@ class VeripbTracer : public FileTracer { Internal *internal; File *file; -#ifndef NDEBUG +#ifndef CADICAL_NDEBUG bool binary; #endif bool with_antecedents; @@ -47,7 +51,7 @@ class VeripbTracer : public FileTracer { bool find_and_delete (const int64_t); // find clause position in hash table -#ifndef QUIET +#ifndef CADICAL_QUIET int64_t added, deleted; #endif vector<int64_t> delete_ids; @@ -89,7 +93,7 @@ public: void weaken_minus (int64_t, const vector<int> &) override; void strengthen (int64_t) override; -#ifndef QUIET +#ifndef CADICAL_QUIET void print_statistics (); #endif bool closed () override; @@ -99,4 +103,6 @@ public: } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/version.hpp b/src/sat/cadical/version.hpp index aaa07788e..d63aefa35 100644 --- a/src/sat/cadical/version.hpp +++ b/src/sat/cadical/version.hpp @@ -1,3 +1,7 @@ +#include "global.h" + +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { const char *version (); @@ -11,3 +15,5 @@ const char *date (); const char *flags (); } // namespace CaDiCaL + +ABC_NAMESPACE_CXX_HEADER_END diff --git a/src/sat/cadical/vivify.hpp b/src/sat/cadical/vivify.hpp index a23acb9ff..a5c6c0e10 100644 --- a/src/sat/cadical/vivify.hpp +++ b/src/sat/cadical/vivify.hpp @@ -1,12 +1,16 @@ #ifndef _vivify_hpp_INCLUDED #define _vivify_hpp_INCLUDED +#include "global.h" + #include "util.hpp" #include <array> #include <cstdint> #include <vector> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { struct Clause; @@ -59,4 +63,6 @@ struct Vivifier { } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif diff --git a/src/sat/cadical/watch.hpp b/src/sat/cadical/watch.hpp index 3adc4c47f..1acab094b 100644 --- a/src/sat/cadical/watch.hpp +++ b/src/sat/cadical/watch.hpp @@ -1,9 +1,13 @@ #ifndef _watch_hpp_INCLUDED #define _watch_hpp_INCLUDED +#include "global.h" + #include <cassert> #include <vector> +ABC_NAMESPACE_CXX_HEADER_START + namespace CaDiCaL { // Watch lists for CDCL search. The blocking literal (see also comments @@ -51,7 +55,7 @@ inline void remove_watch (Watches &ws, Clause *clause) { if (w.clause == clause) i--; } - assert (i + 1 == end); + CADICAL_assert (i + 1 == end); ws.resize (i - ws.begin ()); } @@ -62,11 +66,13 @@ inline void update_watch_size (Watches &ws, int blit, Clause *conflict) { for (Watch &w : ws) { if (w.clause == conflict) w.size = size, w.blit = blit, found = true; - assert (w.clause->garbage || w.size == 2 || w.clause->size != 2); + CADICAL_assert (w.clause->garbage || w.size == 2 || w.clause->size != 2); } - assert (found), (void) found; + CADICAL_assert (found), (void) found; } } // namespace CaDiCaL +ABC_NAMESPACE_CXX_HEADER_END + #endif