1058 lines
28 KiB
C
1058 lines
28 KiB
C
#ifndef NDEBUG
|
|
|
|
#include "check.h"
|
|
#include "error.h"
|
|
#include "internal.h"
|
|
#include "literal.h"
|
|
#include "logging.h"
|
|
#include "print.h"
|
|
|
|
#include <stdio.h>
|
|
#include <limits.h>
|
|
|
|
void
|
|
kissat_check_satisfying_assignment (kissat * solver)
|
|
{
|
|
LOG ("checking satisfying assignment");
|
|
const int *begin = BEGIN_STACK (solver->original);
|
|
const int *end = END_STACK (solver->original), *q;
|
|
#ifdef LOGGING
|
|
size_t count = 0;
|
|
#endif
|
|
for (const int *p = begin; p != end; p = q + 1)
|
|
{
|
|
bool satisfied = false;
|
|
int lit;
|
|
for (q = p; (lit = *q); q++)
|
|
if (!satisfied && kissat_value (solver, lit) == lit)
|
|
satisfied = true;
|
|
#ifdef LOGGING
|
|
count++;
|
|
#endif
|
|
if (satisfied)
|
|
continue;
|
|
kissat_fatal_message_start ();
|
|
fputs ("unsatisfied clause:\n", stderr);
|
|
for (q = p; (lit = *q); q++)
|
|
fprintf (stderr, "%d ", lit);
|
|
fputs ("0\n", stderr);
|
|
fflush (stderr);
|
|
kissat_abort ();
|
|
}
|
|
LOG ("assignment satisfies all %zu original clauses", count);
|
|
}
|
|
|
|
#include "allocate.h"
|
|
#include "inline.h"
|
|
#include "sort.h"
|
|
|
|
typedef struct hash hash;
|
|
typedef struct line line;
|
|
|
|
// *INDENT-OFF*
|
|
|
|
typedef STACK (line *) lines;
|
|
|
|
// *INDENT-ON*
|
|
|
|
struct line
|
|
{
|
|
line *next;
|
|
unsigned size;
|
|
unsigned hash;
|
|
unsigned lits[];
|
|
};
|
|
|
|
struct checker
|
|
{
|
|
bool inconsistent;
|
|
|
|
unsigned vars;
|
|
unsigned size;
|
|
|
|
unsigned lines;
|
|
unsigned hashed;
|
|
|
|
line **table;
|
|
|
|
lines *watches;
|
|
bool *marks;
|
|
signed char *values;
|
|
|
|
bool marked;
|
|
unsigneds imported;
|
|
|
|
unsigneds trail;
|
|
unsigned propagated;
|
|
|
|
unsigned nonces[32];
|
|
|
|
uint64_t added;
|
|
uint64_t checked;
|
|
uint64_t collisions;
|
|
uint64_t decisions;
|
|
uint64_t propagations;
|
|
uint64_t removed;
|
|
uint64_t searches;
|
|
uint64_t unchecked;
|
|
};
|
|
|
|
#define LOGIMPORTED3(...) \
|
|
LOGUNSIGNEDS3 (SIZE_STACK (checker->imported), \
|
|
BEGIN_STACK (checker->imported), __VA_ARGS__)
|
|
|
|
#define LOGLINE3(...) \
|
|
LOGUNSIGNEDS3 (line->size, line->lits, __VA_ARGS__)
|
|
|
|
#define MAX_NONCES \
|
|
(sizeof checker->nonces / sizeof *checker->nonces)
|
|
|
|
static inline bool
|
|
less_unsigned (unsigned a, unsigned b)
|
|
{
|
|
return a < b;
|
|
}
|
|
|
|
static void
|
|
sort_line (kissat * solver, checker * checker)
|
|
{
|
|
SORT_STACK (unsigned, checker->imported, less_unsigned);
|
|
LOGIMPORTED3 ("sorted checker");
|
|
}
|
|
|
|
static unsigned
|
|
hash_line (checker * checker)
|
|
{
|
|
unsigned res = 0, pos = 0;
|
|
for (all_stack (unsigned, lit, checker->imported))
|
|
{
|
|
res += checker->nonces[pos++] * lit;
|
|
if (pos == MAX_NONCES)
|
|
pos = 0;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static size_t
|
|
bytes_line (unsigned size)
|
|
{
|
|
return sizeof (line) + size * sizeof (unsigned);
|
|
}
|
|
|
|
static void
|
|
init_nonces (kissat * solver, checker * checker)
|
|
{
|
|
generator random = 42;
|
|
for (unsigned i = 0; i < MAX_NONCES; i++)
|
|
checker->nonces[i] = 1 | kissat_next_random32 (&random);
|
|
LOG3 ("initialized %zu checker nonces", MAX_NONCES);
|
|
#ifndef LOGGING
|
|
(void) solver;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
kissat_init_checker (kissat * solver)
|
|
{
|
|
LOG ("initializing internal proof checker");
|
|
checker *checker = kissat_calloc (solver, 1, sizeof (struct checker));
|
|
solver->checker = checker;
|
|
init_nonces (solver, checker);
|
|
}
|
|
|
|
static void
|
|
release_hash (kissat * solver, checker * checker)
|
|
{
|
|
for (unsigned h = 0; h < checker->hashed; h++)
|
|
{
|
|
for (line * line = checker->table[h], *next; line; line = next)
|
|
{
|
|
next = line->next;
|
|
kissat_free (solver, line, bytes_line (line->size));
|
|
}
|
|
}
|
|
kissat_dealloc (solver, checker->table, checker->hashed, sizeof (line *));
|
|
}
|
|
|
|
static void
|
|
release_watches (kissat * solver, checker * checker)
|
|
{
|
|
const unsigned lits = 2 * checker->vars;
|
|
for (unsigned i = 0; i < lits; i++)
|
|
RELEASE_STACK (checker->watches[i]);
|
|
kissat_dealloc (solver, checker->watches, 2 * checker->size,
|
|
sizeof (lines));
|
|
}
|
|
|
|
void
|
|
kissat_release_checker (kissat * solver)
|
|
{
|
|
LOG ("releasing internal proof checker");
|
|
checker *checker = solver->checker;
|
|
release_hash (solver, checker);
|
|
RELEASE_STACK (checker->imported);
|
|
RELEASE_STACK (checker->trail);
|
|
kissat_free (solver, checker->marks, 2 * checker->size * sizeof (bool));
|
|
kissat_free (solver, checker->values, 2 * checker->size);
|
|
release_watches (solver, checker);
|
|
kissat_free (solver, checker, sizeof (struct checker));
|
|
}
|
|
|
|
#ifndef QUIET
|
|
|
|
#include <inttypes.h>
|
|
|
|
#define PERCENT_ADDED(NAME) \
|
|
kissat_percent (checker->NAME, checker->added)
|
|
|
|
void
|
|
kissat_print_checker_statistics (kissat * solver, bool verbose)
|
|
{
|
|
checker *checker = solver->checker;
|
|
if (verbose)
|
|
PRINT_STAT ("checker_added", checker->added, 100, "%", "");
|
|
PRINT_STAT ("checker_checked", checker->checked,
|
|
PERCENT_ADDED (checked), "%", "added");
|
|
if (verbose)
|
|
PRINT_STAT ("checker_collisions", checker->collisions,
|
|
kissat_percent (checker->collisions, checker->searches),
|
|
"%", "per search");
|
|
PRINT_STAT ("checker_decisions", checker->decisions,
|
|
kissat_average (checker->decisions, checker->checked),
|
|
"", "per check");
|
|
PRINT_STAT ("checker_propagations", checker->propagations,
|
|
kissat_average (checker->propagations, checker->checked),
|
|
"", "per check");
|
|
PRINT_STAT ("checker_removed", checker->removed,
|
|
PERCENT_ADDED (removed), "%", "added");
|
|
PRINT_STAT ("checker_unchecked", checker->unchecked,
|
|
PERCENT_ADDED (unchecked), "%", "added");
|
|
}
|
|
|
|
#endif
|
|
|
|
#define MAX_VARS (1u<<29)
|
|
#define MAX_SIZE (1u<<30)
|
|
|
|
static unsigned
|
|
reduce_hash (unsigned hash, unsigned mod)
|
|
{
|
|
if (mod < 2)
|
|
return 0;
|
|
assert (mod);
|
|
unsigned res = hash;
|
|
for (unsigned shift = 16, mask = 0xffff; res >= mod; mask >>= (shift >>= 1))
|
|
res = (res >> shift) & mask;
|
|
assert (res < mod);
|
|
return res;
|
|
}
|
|
|
|
static void
|
|
resize_hash (kissat * solver, checker * checker)
|
|
{
|
|
const unsigned old_hashed = checker->hashed;
|
|
assert (old_hashed < MAX_SIZE);
|
|
const unsigned new_hashed = old_hashed ? 2 * old_hashed : 1;
|
|
line **table = kissat_calloc (solver, new_hashed, sizeof (line *));
|
|
line **old_table = checker->table;
|
|
for (unsigned i = 0; i < old_hashed; i++)
|
|
{
|
|
for (line * line = old_table[i], *next; line; line = next)
|
|
{
|
|
next = line->next;
|
|
const unsigned reduced = reduce_hash (line->hash, new_hashed);
|
|
line->next = table[reduced];
|
|
table[reduced] = line;
|
|
}
|
|
}
|
|
kissat_dealloc (solver, checker->table, old_hashed, sizeof (line *));
|
|
checker->hashed = new_hashed;
|
|
checker->table = table;
|
|
}
|
|
|
|
static line *
|
|
new_line (kissat * solver, checker * checker, unsigned size, unsigned hash)
|
|
{
|
|
line *res = kissat_malloc (solver, bytes_line (size));
|
|
res->next = 0;
|
|
res->size = size;
|
|
res->hash = hash;
|
|
memcpy (res->lits, BEGIN_STACK (checker->imported),
|
|
size * sizeof *res->lits);
|
|
return res;
|
|
}
|
|
|
|
#define CHECKER_LITS (2*(checker)->vars)
|
|
#define VALID_CHECKER_LIT(LIT) ((LIT) < CHECKER_LITS)
|
|
|
|
static line decision_line;
|
|
static line unit_line;
|
|
|
|
static void
|
|
checker_assign (kissat * solver, checker * checker, unsigned lit, line * line)
|
|
{
|
|
#ifdef LOGGING
|
|
if (line == &decision_line)
|
|
LOG3 ("checker assign %u (decision)", lit);
|
|
else if (line == &unit_line)
|
|
LOG3 ("checker assign %u (unit)", lit);
|
|
else
|
|
LOGLINE3 ("checker assign %u reason", lit);
|
|
#else
|
|
(void) line;
|
|
#endif
|
|
assert (VALID_CHECKER_LIT (lit));
|
|
const unsigned not_lit = lit ^ 1;
|
|
signed char *values = checker->values;
|
|
assert (!values[lit]);
|
|
assert (!values[not_lit]);
|
|
values[lit] = 1;
|
|
values[not_lit] = -1;
|
|
PUSH_STACK (checker->trail, lit);
|
|
}
|
|
|
|
static lines *
|
|
checker_watches (checker * checker, unsigned lit)
|
|
{
|
|
assert (VALID_CHECKER_LIT (lit));
|
|
return checker->watches + lit;
|
|
}
|
|
|
|
static void
|
|
watch_checker_literal (kissat * solver, checker * checker, line * line,
|
|
unsigned lit)
|
|
{
|
|
LOGLINE3 ("checker watches %u in", lit);
|
|
lines *lines = checker_watches (checker, lit);
|
|
PUSH_STACK (*lines, line);
|
|
}
|
|
|
|
static void
|
|
unwatch_checker_literal (kissat * solver, checker * checker, line * line,
|
|
unsigned lit)
|
|
{
|
|
LOGLINE3 ("checker unwatches %u in", lit);
|
|
lines *lines = checker_watches (checker, lit);
|
|
REMOVE_STACK (struct line *, *lines, line);
|
|
#ifndef LOGGING
|
|
(void) solver;
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
unwatch_line (kissat * solver, checker * checker, line * line)
|
|
{
|
|
assert (line->size > 1);
|
|
const unsigned *lits = line->lits;
|
|
unwatch_checker_literal (solver, checker, line, lits[0]);
|
|
unwatch_checker_literal (solver, checker, line, lits[1]);
|
|
}
|
|
|
|
static bool
|
|
satisfied_or_trivial_imported (kissat * solver, checker * checker)
|
|
{
|
|
const unsigned *lits = BEGIN_STACK (checker->imported);
|
|
const unsigned *end_of_lits = END_STACK (checker->imported);
|
|
const signed char *values = checker->values;
|
|
bool *marks = checker->marks;
|
|
const unsigned *p;
|
|
bool res = false;
|
|
for (p = lits; !res && p != end_of_lits; p++)
|
|
{
|
|
const unsigned lit = *p;
|
|
if (marks[lit])
|
|
continue;
|
|
marks[lit] = true;
|
|
const unsigned not_lit = lit ^ 1;
|
|
if (marks[not_lit])
|
|
{
|
|
LOGIMPORTED3 ("trivial by %u and %u imported checker",
|
|
not_lit, lit);
|
|
res = true;
|
|
}
|
|
else if (values[lit] > 0)
|
|
{
|
|
LOGIMPORTED3 ("satisfied by %u imported checker", lit);
|
|
res = true;
|
|
}
|
|
}
|
|
for (const unsigned *q = lits; q != p; q++)
|
|
marks[*q] = 0;
|
|
#ifndef LOGGING
|
|
(void) solver;
|
|
#endif
|
|
return res;
|
|
}
|
|
|
|
static void
|
|
mark_line (checker * checker)
|
|
{
|
|
bool *marks = checker->marks;
|
|
for (all_stack (unsigned, lit, checker->imported))
|
|
marks[lit] = 1;
|
|
checker->marked = true;
|
|
}
|
|
|
|
static void
|
|
unmark_line (checker * checker)
|
|
{
|
|
bool *marks = checker->marks;
|
|
for (all_stack (unsigned, lit, checker->imported))
|
|
marks[lit] = 0;
|
|
checker->marked = false;
|
|
}
|
|
|
|
static bool
|
|
simplify_imported (kissat * solver, checker * checker)
|
|
{
|
|
if (checker->inconsistent)
|
|
{
|
|
LOG3 ("skipping addition since checker already inconsistent");
|
|
return true;
|
|
}
|
|
unsigned non_false = 0;
|
|
#ifdef LOGGING
|
|
unsigned num_false = 0;
|
|
#endif
|
|
const unsigned *end_of_lits = END_STACK (checker->imported);
|
|
unsigned *lits = BEGIN_STACK (checker->imported);
|
|
const signed char *values = checker->values;
|
|
bool *marks = checker->marks;
|
|
bool res = false;
|
|
unsigned *p;
|
|
for (p = lits; !res && p != end_of_lits; p++)
|
|
{
|
|
const unsigned lit = *p;
|
|
if (marks[lit])
|
|
continue;
|
|
marks[lit] = true;
|
|
const unsigned not_lit = lit ^ 1;
|
|
if (marks[not_lit])
|
|
{
|
|
LOG3 ("simplified checker clause trivial (contains %u and %u)",
|
|
not_lit, lit);
|
|
res = true;
|
|
}
|
|
else
|
|
{
|
|
signed char lit_value = values[lit];
|
|
if (lit_value < 0)
|
|
{
|
|
#ifdef LOGGING
|
|
num_false++;
|
|
#endif
|
|
}
|
|
else if (lit_value > 0)
|
|
{
|
|
LOG3 ("simplified checker clause satisfied by %u", lit);
|
|
res = true;
|
|
}
|
|
else
|
|
{
|
|
if (!non_false)
|
|
SWAP (unsigned, *p, lits[0]);
|
|
else if (non_false == 1)
|
|
SWAP (unsigned, *p, lits[1]);
|
|
non_false++;
|
|
}
|
|
}
|
|
}
|
|
for (const unsigned *q = lits; q != p; q++)
|
|
marks[*q] = 0;
|
|
if (!res)
|
|
{
|
|
if (!non_false)
|
|
{
|
|
LOG3 ("simplified checker clause inconsistent");
|
|
checker->inconsistent = true;
|
|
res = true;
|
|
}
|
|
else if (non_false == 1)
|
|
{
|
|
LOG3 ("simplified checker clause unit");
|
|
checker_assign (solver, checker, lits[0], &unit_line);
|
|
res = true;
|
|
}
|
|
}
|
|
if (!res)
|
|
{
|
|
LOG3 ("non-trivial and non-satisfied imported checker clause "
|
|
"has %u false and %u non-false literals", num_false, non_false);
|
|
LOGIMPORTED3 ("simplified checker");
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static void
|
|
insert_imported (kissat * solver, checker * checker, unsigned hash)
|
|
{
|
|
size_t size = SIZE_STACK (checker->imported);
|
|
assert (size <= UINT_MAX);
|
|
if (checker->lines == checker->hashed)
|
|
resize_hash (solver, checker);
|
|
line *line = new_line (solver, checker, size, hash);
|
|
const unsigned reduced = reduce_hash (hash, checker->hashed);
|
|
struct line **p = checker->table + reduced;
|
|
line->next = *p;
|
|
*p = line;
|
|
LOGLINE3 ("inserted checker");
|
|
const unsigned *lits = BEGIN_STACK (checker->imported);
|
|
const signed char *values = checker->values;
|
|
assert (!values[lits[0]]);
|
|
assert (!values[lits[1]]);
|
|
watch_checker_literal (solver, checker, line, lits[0]);
|
|
watch_checker_literal (solver, checker, line, lits[1]);
|
|
checker->lines++;
|
|
checker->added++;
|
|
}
|
|
|
|
static void
|
|
insert_imported_if_not_simplified (kissat * solver, checker * checker)
|
|
{
|
|
sort_line (solver, checker);
|
|
const unsigned hash = hash_line (checker);
|
|
if (!simplify_imported (solver, checker))
|
|
insert_imported (solver, checker, hash);
|
|
}
|
|
|
|
static bool
|
|
match_line (checker * checker, unsigned size, unsigned hash, line * line)
|
|
{
|
|
if (line->size != size)
|
|
return false;
|
|
if (line->hash != hash)
|
|
return false;
|
|
if (!checker->marked)
|
|
mark_line (checker);
|
|
const unsigned *lits = line->lits;
|
|
const unsigned *end_of_lits = lits + line->size;
|
|
const bool *marks = checker->marks;
|
|
for (const unsigned *p = lits; p != end_of_lits; p++)
|
|
if (!marks[*p])
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
resize_checker (kissat * solver, checker * checker, unsigned new_vars)
|
|
{
|
|
const unsigned vars = checker->vars;
|
|
const unsigned size = checker->size;
|
|
if (new_vars > size)
|
|
{
|
|
assert (new_vars <= MAX_SIZE);
|
|
unsigned new_size = size ? 2 * size : 1;
|
|
while (new_size < new_vars)
|
|
new_size *= 2;
|
|
assert (new_size <= MAX_SIZE);
|
|
LOG3 ("resizing checker form %u to %u", size, new_size);
|
|
const unsigned size2 = 2 * size;
|
|
const unsigned new_size2 = 2 * new_size;
|
|
checker->marks =
|
|
kissat_realloc (solver, checker->marks, size2,
|
|
new_size2 * sizeof (bool));
|
|
checker->values =
|
|
kissat_realloc (solver, checker->values, size2, new_size2);
|
|
checker->watches =
|
|
kissat_realloc (solver, checker->watches,
|
|
size2 * sizeof *checker->watches,
|
|
new_size2 * sizeof *checker->watches);
|
|
checker->size = new_size;
|
|
}
|
|
const unsigned delta = new_vars - vars;
|
|
if (delta == 1)
|
|
LOG3 ("initializing one checker variable %u", vars);
|
|
else
|
|
LOG3 ("initializing %u checker variables from %u to %u",
|
|
delta, vars, new_vars - 1);
|
|
const unsigned vars2 = 2 * vars;
|
|
const unsigned new_vars2 = 2 * new_vars;
|
|
const unsigned delta2 = 2 * delta;
|
|
assert (delta2 == new_vars2 - vars2);
|
|
memset (checker->watches + vars2, 0, delta2 * sizeof *checker->watches);
|
|
memset (checker->marks + vars2, 0, delta2);
|
|
memset (checker->values + vars2, 0, delta2);
|
|
checker->vars = new_vars;
|
|
}
|
|
|
|
static inline unsigned
|
|
import_external_checker (kissat * solver, checker * checker, int elit)
|
|
{
|
|
assert (elit);
|
|
const unsigned var = ABS (elit) - 1;
|
|
if (var >= checker->vars)
|
|
resize_checker (solver, checker, var + 1);
|
|
assert (var < checker->vars);
|
|
return 2 * var + (elit < 0);
|
|
}
|
|
|
|
static inline unsigned
|
|
import_internal_checker (kissat * solver, checker * checker, unsigned ilit)
|
|
{
|
|
const int elit = kissat_export_literal (solver, ilit);
|
|
return import_external_checker (solver, checker, elit);
|
|
}
|
|
|
|
static inline int
|
|
export_checker (checker * checker, unsigned ilit)
|
|
{
|
|
assert (ilit <= 2 * checker->vars);
|
|
return (1 + (ilit >> 1)) * ((ilit & 1) ? -1 : 1);
|
|
}
|
|
|
|
static line *
|
|
find_line (kissat * solver, checker * checker, size_t size, bool remove)
|
|
{
|
|
if (!checker->hashed)
|
|
return 0;
|
|
sort_line (solver, checker);
|
|
checker->searches++;
|
|
const unsigned hash = hash_line (checker);
|
|
const unsigned reduced = reduce_hash (hash, checker->hashed);
|
|
struct line **p, *line;
|
|
for (p = checker->table + reduced;
|
|
(line = *p) && !match_line (checker, size, hash, line);
|
|
p = &line->next)
|
|
checker->collisions++;
|
|
if (checker->marked)
|
|
unmark_line (checker);
|
|
if (line && remove)
|
|
*p = line->next;
|
|
return line;
|
|
}
|
|
|
|
static void
|
|
remove_line (kissat * solver, checker * checker, size_t size)
|
|
{
|
|
line *line = find_line (solver, checker, size, true);
|
|
if (!line)
|
|
{
|
|
kissat_fatal_message_start ();
|
|
fputs ("trying to remove non-existing clause:\n", stderr);
|
|
for (all_stack (unsigned, lit, checker->imported))
|
|
fprintf (stderr, "%d ", export_checker (checker, lit));
|
|
fputs ("0\n", stderr);
|
|
fflush (stderr);
|
|
kissat_abort ();
|
|
}
|
|
unwatch_line (solver, checker, line);
|
|
LOGLINE3 ("removed checker");
|
|
kissat_free (solver, line, bytes_line (size));
|
|
assert (checker->lines > 0);
|
|
checker->lines--;
|
|
checker->removed++;
|
|
}
|
|
|
|
static void
|
|
import_external_literals (kissat * solver, checker * checker,
|
|
size_t size, int *elits)
|
|
{
|
|
if (size > UINT_MAX)
|
|
kissat_fatal ("can not check handle original clause of size %zu", size);
|
|
CLEAR_STACK (checker->imported);
|
|
for (size_t i = 0; i < size; i++)
|
|
{
|
|
const unsigned lit =
|
|
import_external_checker (solver, checker, elits[i]);
|
|
PUSH_STACK (checker->imported, lit);
|
|
}
|
|
LOGIMPORTED3 ("checker imported external");
|
|
}
|
|
|
|
static void
|
|
import_internal_literals (kissat * solver, checker * checker,
|
|
size_t size, const unsigned *ilits)
|
|
{
|
|
assert (size <= UINT_MAX);
|
|
CLEAR_STACK (checker->imported);
|
|
for (size_t i = 0; i < size; i++)
|
|
{
|
|
const unsigned ilit = ilits[i];
|
|
const unsigned lit = import_internal_checker (solver, checker, ilit);
|
|
PUSH_STACK (checker->imported, lit);
|
|
}
|
|
LOGIMPORTED3 ("checker imported internal");
|
|
}
|
|
|
|
|
|
static void
|
|
import_clause (kissat * solver, checker * checker, clause * c)
|
|
{
|
|
import_internal_literals (solver, checker, c->size, c->lits);
|
|
LOGIMPORTED3 ("checker imported clause");
|
|
}
|
|
|
|
static void
|
|
import_binary (kissat * solver, checker * checker, unsigned a, unsigned b)
|
|
{
|
|
CLEAR_STACK (checker->imported);
|
|
const unsigned c = import_internal_checker (solver, checker, a);
|
|
const unsigned d = import_internal_checker (solver, checker, b);
|
|
PUSH_STACK (checker->imported, c);
|
|
PUSH_STACK (checker->imported, d);
|
|
LOGIMPORTED3 ("checker imported binary");
|
|
}
|
|
|
|
static void
|
|
import_internal_unit (kissat * solver, checker * checker, unsigned a)
|
|
{
|
|
CLEAR_STACK (checker->imported);
|
|
const unsigned b = import_internal_checker (solver, checker, a);
|
|
PUSH_STACK (checker->imported, b);
|
|
LOGIMPORTED3 ("checker imported unit");
|
|
}
|
|
|
|
static bool
|
|
checker_propagate (kissat * solver, checker * checker)
|
|
{
|
|
unsigned propagated = checker->propagated;
|
|
signed char *values = checker->values;
|
|
bool res = true;
|
|
while (res && propagated < SIZE_STACK (checker->trail))
|
|
{
|
|
const unsigned lit = PEEK_STACK (checker->trail, propagated);
|
|
const unsigned not_lit = lit ^ 1;
|
|
LOG3 ("checker propagate %u", lit);
|
|
assert (values[lit] > 0);
|
|
assert (values[not_lit] < 0);
|
|
propagated++;
|
|
lines *lines = checker_watches (checker, not_lit);
|
|
line **begin_of_line = BEGIN_STACK (*lines), **q = begin_of_line;
|
|
line *const *end_of_lines = END_STACK (*lines), *const *p = q;
|
|
while (p != end_of_lines)
|
|
{
|
|
line *line = *q++ = *p++;
|
|
if (!res)
|
|
continue;
|
|
unsigned *lits = line->lits;
|
|
const unsigned other = not_lit ^ lits[0] ^ lits[1];
|
|
const signed char other_value = values[other];
|
|
if (other_value > 0)
|
|
continue;
|
|
const unsigned *end_of_lits = lits + line->size;
|
|
unsigned replacement;
|
|
signed char replacement_value = -1;
|
|
unsigned *r;
|
|
for (r = lits + 2; r != end_of_lits; r++)
|
|
{
|
|
replacement = *r;
|
|
if (replacement == other)
|
|
continue;
|
|
if (replacement == not_lit)
|
|
continue;
|
|
replacement_value = values[replacement];
|
|
if (replacement_value >= 0)
|
|
break;
|
|
}
|
|
if (replacement_value >= 0)
|
|
{
|
|
lits[0] = other;
|
|
lits[1] = replacement;
|
|
*r = not_lit;
|
|
LOGLINE3 ("checker unwatching %u in", not_lit);
|
|
watch_checker_literal (solver, checker, line, replacement);
|
|
q--;
|
|
}
|
|
else if (other_value < 0)
|
|
{
|
|
LOGLINE3 ("checker conflict");
|
|
res = false;
|
|
}
|
|
else
|
|
checker_assign (solver, checker, other, line);
|
|
}
|
|
SET_END_OF_STACK (*lines, q);
|
|
}
|
|
checker->propagations += propagated - checker->propagated;
|
|
checker->propagated = propagated;
|
|
return res;
|
|
}
|
|
|
|
static bool
|
|
line_redundant (kissat * solver, checker * checker, size_t size)
|
|
{
|
|
if (!checker_propagate (solver, checker))
|
|
{
|
|
LOG3 ("root level checker unit propagations leads to conflict");
|
|
LOG2 ("checker becomes inconsistent");
|
|
checker->inconsistent = true;
|
|
return true;
|
|
}
|
|
if (checker->inconsistent)
|
|
{
|
|
LOG3 ("skipping removal since checker already inconsistent");
|
|
return true;
|
|
}
|
|
if (!size)
|
|
kissat_fatal ("checker can not remove empty checker clause");
|
|
if (size == 1)
|
|
{
|
|
const unsigned unit = PEEK_STACK (checker->imported, 0);
|
|
const signed char value = checker->values[unit];
|
|
if (value < 0 && !checker->inconsistent)
|
|
kissat_fatal ("consistent checker can not remove falsified unit %d",
|
|
export_checker (checker, unit));
|
|
if (!value)
|
|
kissat_fatal ("checker can not remove unassigned unit %d",
|
|
export_checker (checker, unit));
|
|
LOG3 ("checker skips removal of satisfied unit %u", unit);
|
|
return true;
|
|
}
|
|
else if (satisfied_or_trivial_imported (solver, checker))
|
|
{
|
|
LOGIMPORTED3 ("satisfied imported checker");
|
|
return true;
|
|
}
|
|
else
|
|
return false;
|
|
}
|
|
|
|
static void
|
|
remove_line_if_not_redundant (kissat * solver, checker * checker)
|
|
{
|
|
size_t size = SIZE_STACK (checker->imported);
|
|
if (!line_redundant (solver, checker, size))
|
|
remove_line (solver, checker, size);
|
|
}
|
|
|
|
static void
|
|
checker_backtrack (checker * checker, unsigned saved)
|
|
{
|
|
unsigned *begin = BEGIN_STACK (checker->trail) + saved;
|
|
unsigned *p = END_STACK (checker->trail);
|
|
signed char *values = checker->values;
|
|
while (p != begin)
|
|
{
|
|
const unsigned lit = *--p;
|
|
assert (VALID_CHECKER_LIT (lit));
|
|
const unsigned not_lit = lit ^ 1;
|
|
assert (values[lit] > 0);
|
|
assert (values[not_lit] < 0);
|
|
values[lit] = values[not_lit] = 0;
|
|
}
|
|
checker->propagated = saved;
|
|
SET_END_OF_STACK (checker->trail, begin);
|
|
}
|
|
|
|
static void
|
|
check_line (kissat * solver, checker * checker)
|
|
{
|
|
checker->checked++;
|
|
if (checker->inconsistent)
|
|
return;
|
|
if (!checker_propagate (solver, checker))
|
|
{
|
|
LOG3 ("root level checker unit propagations leads to conflict");
|
|
LOG2 ("checker becomes inconsistent");
|
|
checker->inconsistent = true;
|
|
return;
|
|
}
|
|
const unsigned saved = SIZE_STACK (checker->trail);
|
|
signed char *values = checker->values;
|
|
bool satisfied = false;
|
|
unsigned decisions = 0;
|
|
for (all_stack (unsigned, lit, checker->imported))
|
|
{
|
|
signed char lit_value = values[lit];
|
|
if (lit_value < 0)
|
|
continue;
|
|
if (lit_value > 0)
|
|
{
|
|
satisfied = true;
|
|
break;
|
|
}
|
|
const unsigned not_lit = lit ^ 1;
|
|
checker_assign (solver, checker, not_lit, &decision_line);
|
|
decisions++;
|
|
}
|
|
checker->decisions += decisions;
|
|
if (!satisfied && checker_propagate (solver, checker))
|
|
{
|
|
kissat_fatal_message_start ();
|
|
fputs ("failed to check clause:\n", stderr);
|
|
for (all_stack (unsigned, lit, checker->imported))
|
|
fprintf (stderr, "%d ", export_checker (checker, lit));
|
|
fputs ("0\n", stderr);
|
|
fflush (stderr);
|
|
kissat_abort ();
|
|
}
|
|
LOG3 ("checker imported clause unit implied");
|
|
checker_backtrack (checker, saved);
|
|
}
|
|
|
|
void
|
|
kissat_add_unchecked_external (kissat * solver, size_t size, int *elits)
|
|
{
|
|
LOGINTS3 (size, elits, "adding unchecked external checker");
|
|
checker *checker = solver->checker;
|
|
checker->unchecked++;
|
|
import_external_literals (solver, checker, size, elits);
|
|
insert_imported_if_not_simplified (solver, checker);
|
|
}
|
|
|
|
void
|
|
kissat_add_unchecked_internal (kissat * solver, size_t size, unsigned *lits)
|
|
{
|
|
LOGLITS3 (size, lits, "adding unchecked internal checker");
|
|
checker *checker = solver->checker;
|
|
checker->unchecked++;
|
|
assert (size <= UINT_MAX);
|
|
import_internal_literals (solver, checker, size, lits);
|
|
insert_imported_if_not_simplified (solver, checker);
|
|
}
|
|
|
|
void
|
|
kissat_check_and_add_binary (kissat * solver, unsigned a, unsigned b)
|
|
{
|
|
LOGBINARY3 (a, b, "checking and adding internal checker");
|
|
checker *checker = solver->checker;
|
|
assert (VALID_INTERNAL_LITERAL (a));
|
|
assert (VALID_INTERNAL_LITERAL (b));
|
|
import_binary (solver, checker, a, b);
|
|
check_line (solver, checker);
|
|
insert_imported_if_not_simplified (solver, checker);
|
|
}
|
|
|
|
void
|
|
kissat_check_and_add_clause (kissat * solver, clause * clause)
|
|
{
|
|
LOGCLS3 (clause, "checking and adding internal checker");
|
|
checker *checker = solver->checker;
|
|
import_clause (solver, checker, clause);
|
|
check_line (solver, checker);
|
|
insert_imported_if_not_simplified (solver, checker);
|
|
}
|
|
|
|
void
|
|
kissat_check_and_add_empty (kissat * solver)
|
|
{
|
|
LOG3 ("checking and adding empty checker clause");
|
|
checker *checker = solver->checker;
|
|
CLEAR_STACK (checker->imported);
|
|
check_line (solver, checker);
|
|
insert_imported_if_not_simplified (solver, checker);
|
|
}
|
|
|
|
void
|
|
kissat_check_and_add_internal (kissat * solver, size_t size, unsigned *lits)
|
|
{
|
|
LOGLITS3 (size, lits, "checking and adding internal checker");
|
|
checker *checker = solver->checker;
|
|
import_internal_literals (solver, checker, size, lits);
|
|
check_line (solver, checker);
|
|
insert_imported_if_not_simplified (solver, checker);
|
|
}
|
|
|
|
void
|
|
kissat_check_and_add_unit (kissat * solver, unsigned a)
|
|
{
|
|
LOG3 ("checking and adding internal checker internal unit %u", a);
|
|
checker *checker = solver->checker;
|
|
assert (VALID_INTERNAL_LITERAL (a));
|
|
import_internal_unit (solver, checker, a);
|
|
check_line (solver, checker);
|
|
insert_imported_if_not_simplified (solver, checker);
|
|
}
|
|
|
|
void
|
|
kissat_check_shrink_clause (kissat * solver, clause * c,
|
|
unsigned remove, unsigned keep)
|
|
{
|
|
LOGCLS3 (c, "checking and shrinking by %u internal checker", remove);
|
|
checker *checker = solver->checker;
|
|
CLEAR_STACK (checker->imported);
|
|
const value *values = solver->values;
|
|
for (all_literals_in_clause (ilit, c))
|
|
{
|
|
if (ilit == remove)
|
|
continue;
|
|
if (ilit != keep && values[ilit] < 0 && !LEVEL (ilit))
|
|
continue;
|
|
const unsigned lit = import_internal_checker (solver, checker, ilit);
|
|
PUSH_STACK (checker->imported, lit);
|
|
}
|
|
LOGIMPORTED3 ("checker imported internal");
|
|
check_line (solver, checker);
|
|
insert_imported_if_not_simplified (solver, checker);
|
|
import_clause (solver, checker, c);
|
|
remove_line_if_not_redundant (solver, checker);
|
|
}
|
|
|
|
void
|
|
kissat_remove_checker_binary (kissat * solver, unsigned a, unsigned b)
|
|
{
|
|
LOGBINARY3 (a, b, "removing internal checker");
|
|
checker *checker = solver->checker;
|
|
assert (VALID_INTERNAL_LITERAL (a));
|
|
assert (VALID_INTERNAL_LITERAL (b));
|
|
import_binary (solver, checker, a, b);
|
|
remove_line_if_not_redundant (solver, checker);
|
|
}
|
|
|
|
void
|
|
kissat_remove_checker_clause (kissat * solver, clause * clause)
|
|
{
|
|
LOGCLS3 (clause, "removing internal checker");
|
|
checker *checker = solver->checker;
|
|
import_clause (solver, checker, clause);
|
|
remove_line_if_not_redundant (solver, checker);
|
|
}
|
|
|
|
bool
|
|
kissat_checker_contains_clause (kissat * solver, clause * clause)
|
|
{
|
|
checker *checker = solver->checker;
|
|
import_clause (solver, checker, clause);
|
|
size_t size = SIZE_STACK (checker->imported);
|
|
if (line_redundant (solver, checker, size))
|
|
return true;
|
|
return find_line (solver, checker, size, false);
|
|
}
|
|
|
|
void
|
|
kissat_remove_checker_external (kissat * solver, size_t size, int *elits)
|
|
{
|
|
LOGINTS3 (size, elits, "removing external checker");
|
|
checker *checker = solver->checker;
|
|
import_external_literals (solver, checker, size, elits);
|
|
remove_line_if_not_redundant (solver, checker);
|
|
}
|
|
|
|
void
|
|
kissat_remove_checker_internal (kissat * solver, size_t size, unsigned *ilits)
|
|
{
|
|
LOGLITS3 (size, ilits, "removing internal checker");
|
|
checker *checker = solver->checker;
|
|
import_internal_literals (solver, checker, size, ilits);
|
|
remove_line_if_not_redundant (solver, checker);
|
|
}
|
|
|
|
void
|
|
dump_line (line * line)
|
|
{
|
|
printf ("line[%p]", (void *) line);
|
|
for (unsigned i = 0; i < line->size; i++)
|
|
printf (" %u", line->lits[i]);
|
|
fputc ('\n', stdout);
|
|
}
|
|
|
|
void
|
|
dump_checker (kissat * solver)
|
|
{
|
|
checker *checker = solver->checker;
|
|
printf ("%s\n", checker->inconsistent ? "inconsistent" : "consistent");
|
|
printf ("vars %u\n", checker->vars);
|
|
printf ("size %u\n", checker->size);
|
|
printf ("lines %u\n", checker->lines);
|
|
printf ("hashed %u\n", checker->hashed);
|
|
for (unsigned i = 0; i < SIZE_STACK (checker->trail); i++)
|
|
printf ("trail[%u] %u\n", i, PEEK_STACK (checker->trail, i));
|
|
for (unsigned h = 0; h < checker->hashed; h++)
|
|
for (line * line = checker->table[h]; line; line = line->next)
|
|
dump_line (line);
|
|
}
|
|
|
|
#else
|
|
int kissat_check_dummy_to_avoid_warning;
|
|
#endif
|