2023-03-26 19:15:17 +08:00

809 lines
21 KiB
C

#define INLINE_SORT
#include "allocate.h"
#include "colors.h"
#include "collect.h"
#include "compact.h"
#include "inline.h"
#include "print.h"
#include "report.h"
#include "trail.h"
#include "sort.c"
#include <inttypes.h>
static void
flush_watched_clauses_by_literal (kissat * solver, litpairs * hyper,
unsigned lit, bool compact, reference start)
{
assert (start != INVALID_REF);
const value *values = solver->values;
const assigned *all_assigned = solver->assigned;
const value lit_value = values[lit];
const assigned *lit_assigned = all_assigned + IDX (lit);
const value lit_fixed = (lit_value && !lit_assigned->level) ? lit_value : 0;
const unsigned mlit = kissat_map_literal (solver, lit, true);
watches *lit_watches = &WATCHES (lit);
watch *begin = BEGIN_WATCHES (*lit_watches), *q = begin;
const watch *end_of_watches = END_WATCHES (*lit_watches), *p = q;
while (p != end_of_watches)
{
watch head = *p++;
if (head.type.binary)
{
const unsigned other = head.binary.lit;
const unsigned other_idx = IDX (other);
const value other_value = values[other];
const value other_fixed =
(other_value && !all_assigned[other_idx].level) ? other_value : 0;
const unsigned mother = kissat_map_literal (solver, other, compact);
if (lit_fixed > 0 || other_fixed > 0 || mother == INVALID_LIT)
{
if (lit < other)
kissat_delete_binary (solver,
head.binary.redundant,
head.binary.hyper, lit, other);
}
else
{
assert (!lit_fixed);
assert (!other_fixed);
if (head.binary.hyper)
{
assert (head.binary.redundant);
assert (mlit != INVALID_LIT);
assert (mother != INVALID_LIT);
if (lit < other)
{
const litpair litpair = kissat_litpair (lit, other);
PUSH_STACK (*hyper, litpair);
LOGBINARY (lit, other, "saved SRC hyper");
}
}
else
{
head.binary.lit = mother;
*q++ = head;
#ifdef LOGGING
if (lit < other)
{
LOGBINARY (lit, other, "SRC");
LOGBINARY (mlit, mother, "DST");
}
#endif
}
}
}
else
{
assert (solver->watching);
const watch tail = *p++;
if (!lit_fixed)
{
const reference ref = tail.large.ref;
if (ref < start)
{
*q++ = head;
*q++ = tail;
}
}
}
}
assert (!lit_fixed || q == begin);
SET_END_OF_WATCHES (*lit_watches, q);
LOG ("keeping %" SECTOR_FORMAT " watches[%u]", lit_watches->size, lit);
if (!compact)
return;
if (mlit == INVALID_LIT)
return;
watches *mlit_watches = &WATCHES (mlit);
if (lit_fixed)
assert (!mlit_watches->size);
else if (mlit < lit)
{
assert (mlit != INVALID_LIT);
assert (mlit < lit);
*mlit_watches = *lit_watches;
LOG ("copied watches[%u] = watches[%u] "
"(size %" SECTOR_FORMAT ")", mlit, lit, mlit_watches->size);
memset (lit_watches, 0, sizeof *lit_watches);
}
else
assert (mlit == lit);
}
static void
flush_hyper_binary_watches (kissat * solver, litpairs * hyper, bool compact)
{
assert (!solver->probing);
const litpair *end = END_STACK (*hyper);
const value *values = solver->values;
size_t flushed = 0;
for (const litpair * p = BEGIN_STACK (*hyper); p != end; p++)
{
const unsigned lit = p->lits[0];
const unsigned other = p->lits[1];
assert (lit < other);
const value lit_value = values[lit];
const value other_value = values[other];
assert (!lit_value || LEVEL (lit));
assert (!other_value || LEVEL (other));
if (((lit_value < 0 && other_value > 0) ||
((lit_value > 0 && other_value < 0))))
{
LOGBINARY (lit, other, "keeping potential reason hyper SRC");
const unsigned mlit = kissat_map_literal (solver, lit, compact);
const unsigned mother = kissat_map_literal (solver, other, compact);
LOGBINARY (mlit, mother, "keeping potential reason hyper DST");
kissat_watch_other (solver, true, true, mlit, mother);
kissat_watch_other (solver, true, true, mother, mlit);
}
else
{
LOGBINARY (lit, other, "flushing hyper SRC");
kissat_delete_binary (solver, true, true, lit, other);
flushed++;
}
}
if (flushed)
kissat_phase (solver, "collect",
GET (garbage_collections),
"flushed %zu unused hyper binary clauses %.0f%%",
flushed, kissat_percent (flushed, SIZE_STACK (*hyper)));
(void) flushed;
}
static void
flush_all_watched_clauses (kissat * solver, bool compact, reference start)
{
assert (solver->watching);
LOG ("starting to flush watches at clause[%zu]", start);
litpairs hyper;
INIT_STACK (hyper);
for (all_variables (idx))
{
const unsigned lit = LIT (idx);
flush_watched_clauses_by_literal (solver, &hyper, lit, compact, start);
const unsigned not_lit = NOT (lit);
flush_watched_clauses_by_literal (solver, &hyper,
not_lit, compact, start);
}
LOG ("saved %zu hyper binary watches", SIZE_STACK (hyper));
flush_hyper_binary_watches (solver, &hyper, compact);
RELEASE_STACK (hyper);
}
static void
update_large_reason (kissat * solver, assigned * assigned, unsigned forced,
clause * dst)
{
assert (dst->reason);
assert (forced != INVALID_LIT);
reference dst_ref = kissat_reference_clause (solver, dst);
const unsigned forced_idx = IDX (forced);
struct assigned *a = assigned + forced_idx;
assert (!a->binary);
if (a->reason != dst_ref)
{
LOG ("reason reference %u of %s updated to %u",
a->reason, LOGLIT (forced), dst_ref);
a->reason = dst_ref;
}
dst->reason = false;
}
static unsigned
get_forced (const value * values, clause * dst)
{
assert (dst->reason);
unsigned forced = INVALID_LIT;
for (all_literals_in_clause (lit, dst))
{
const value value = values[lit];
if (value <= 0)
continue;
forced = lit;
break;
}
assert (forced != INVALID_LIT);
return forced;
}
static void
get_forced_and_update_large_reason (kissat * solver, assigned * assigned,
const value * values, clause * dst)
{
const unsigned forced = get_forced (values, dst);
update_large_reason (solver, assigned, forced, dst);
}
static void
update_first_reducible (kissat * solver, const clause * end,
clause * first_reducible)
{
if (first_reducible >= end)
{
LOG ("first reducible after end of arena");
solver->first_reducible = INVALID_REF;
}
else if (first_reducible)
{
LOGCLS (first_reducible, "updating first reducible clause to");
solver->first_reducible =
kissat_reference_clause (solver, first_reducible);
}
else
{
LOG ("first reducible clause becomes invalid");
solver->first_reducible = INVALID_REF;
}
}
static void
update_last_irredundant (kissat * solver, const clause * end,
clause * last_irredundant)
{
if (!last_irredundant)
{
LOG ("no more large irredundant clauses left");
solver->last_irredundant = INVALID_REF;
}
else if (end <= last_irredundant)
{
LOG ("last irredundant clause after end of arena");
solver->last_irredundant = INVALID_REF;
}
else
{
LOGCLS (last_irredundant, "updating last irredundant clause to");
reference ref = kissat_reference_clause (solver, last_irredundant);
solver->last_irredundant = ref;
}
}
static void
move_redundant_clauses_to_the_end (kissat * solver, reference ref)
{
INC (moved);
assert (ref != INVALID_REF);
#ifndef NDEBUG
const size_t size = SIZE_STACK (solver->arena);
assert ((size_t) ref <= size);
#endif
clause *begin = (clause *) (BEGIN_STACK (solver->arena) + ref);
clause *end = (clause *) END_STACK (solver->arena);
size_t bytes_redundant = (char *) end - (char *) begin;
kissat_phase (solver, "move",
GET (moved),
"moving redundant clauses of %s to the end",
FORMAT_BYTES (bytes_redundant));
kissat_mark_reason_clauses (solver, ref);
clause *redundant = (clause *) kissat_malloc (solver, bytes_redundant);
clause *p = begin, *q = begin, *r = redundant;
const value *values = solver->values;
assigned *assigned = solver->assigned;
clause *last_irredundant = kissat_last_irredundant_clause (solver);
while (p != end)
{
assert (!p->shrunken);
size_t bytes = kissat_bytes_of_clause (p->size);
if (p->redundant)
{
memcpy (r, p, bytes);
r = (clause *) (bytes + (char *) r);
}
else
{
LOGCLS (p, "old DST");
memmove (q, p, bytes);
LOGCLS (q, "new DST");
last_irredundant = q;
if (q->reason)
get_forced_and_update_large_reason (solver, assigned, values, q);
q = (clause *) (bytes + (char *) q);
}
p = (clause *) (bytes + (char *) p);
}
r = redundant;
clause *first_reducible = 0;
while (q != end)
{
size_t bytes = kissat_bytes_of_clause (r->size);
memcpy (q, r, bytes);
LOGCLS (q, "new DST");
if (q->reason)
get_forced_and_update_large_reason (solver, assigned, values, q);
assert (q->redundant);
if (!first_reducible && !q->keep)
first_reducible = q;
r = (clause *) (bytes + (char *) r);
q = (clause *) (bytes + (char *) q);
}
assert ((char *) r <= (char *) redundant + bytes_redundant);
kissat_free (solver, redundant, bytes_redundant);
assert (!first_reducible || first_reducible < q);
update_first_reducible (solver, q, first_reducible);
update_last_irredundant (solver, q, last_irredundant);
}
static reference
sparse_sweep_garbage_clauses (kissat * solver, bool compact, reference start)
{
assert (solver->watching);
LOG ("sparse garbage collection starting at clause[%zu]", start);
#ifdef CHECKING_OR_PROVING
const bool checking_or_proving = kissat_checking_or_proving (solver);
#endif
assert (EMPTY_STACK (solver->added));
assert (EMPTY_STACK (solver->removed));
const value *values = solver->values;
assigned *assigned = solver->assigned;
size_t flushed_garbage_clauses = 0;
size_t flushed_satisfied_clauses = 0;
size_t flushed_literals = 0;
clause *begin = (clause *) BEGIN_STACK (solver->arena);
const clause *end = (clause *) END_STACK (solver->arena);
clause *first, *src, *dst;
if (start)
first = kissat_dereference_clause (solver, start);
else
first = begin;
src = dst = first;
clause *first_redundant = 0;
clause *first_reducible = 0;
clause *last_irredundant;
if (start)
last_irredundant = kissat_last_irredundant_clause (solver);
else
last_irredundant = 0;
size_t redundant_bytes = 0;
for (clause * next; src != end; src = next)
{
if (src->garbage)
{
next = kissat_delete_clause (solver, src);
flushed_garbage_clauses++;
if (last_irredundant == src)
{
if (first == begin)
last_irredundant = 0;
else
last_irredundant = first;
}
continue;
}
assert (src->size > 1);
LOGCLS (src, "SRC");
next = kissat_next_clause (src);
#if !defined(NDEBUG) || defined(CHECKING_OR_PROVING)
const unsigned old_size = src->size;
#endif
assert (SIZE_OF_CLAUSE_HEADER == sizeof (unsigned));
*(unsigned *) dst = *(unsigned *) src;
unsigned *q = dst->lits;
unsigned mfirst = INVALID_LIT;
unsigned msecond = INVALID_LIT;
unsigned forced = INVALID_LIT;
unsigned other = INVALID_LIT;
unsigned non_false = 0;
bool satisfied = false;
for (all_literals_in_clause (lit, src))
{
#ifdef CHECKING_OR_PROVING
if (checking_or_proving)
PUSH_STACK (solver->removed, lit);
#endif
if (satisfied)
continue;
const value tmp = values[lit];
const unsigned idx = IDX (lit);
const unsigned level = tmp ? assigned[idx].level : INVALID_LEVEL;
if (tmp < 0 && !level)
flushed_literals++;
else if (tmp > 0 && !level)
{
assert (!satisfied);
assert (!dst->reason);
LOG ("SRC satisfied by %s", LOGLIT (lit));
satisfied = true;
}
else
{
const unsigned mlit = kissat_map_literal (solver, lit, compact);
if (tmp > 0)
{
assert (level);
forced = non_false++ ? INVALID_LIT : lit;
}
else if (tmp < 0)
other = lit;
if (mfirst == INVALID_LIT)
mfirst = mlit;
else if (msecond == INVALID_LIT)
msecond = mlit;
*q++ = mlit;
#ifdef CHECKING_OR_PROVING
if (checking_or_proving)
PUSH_STACK (solver->added, lit);
#endif
}
}
if (satisfied)
{
if (dst->redundant)
DEC (clauses_redundant);
else
DEC (clauses_irredundant);
if (dst->hyper)
DEC (hyper_ternaries);
flushed_satisfied_clauses++;
#ifdef CHECKING_OR_PROVING
if (checking_or_proving)
{
REMOVE_CHECKER_STACK (solver->removed);
DELETE_STACK_FROM_PROOF (solver->removed);
CLEAR_STACK (solver->added);
CLEAR_STACK (solver->removed);
}
#endif
if (last_irredundant == src)
{
if (first == begin)
last_irredundant = 0;
else
last_irredundant = first;
}
continue;
}
const unsigned new_size = q - dst->lits;
assert (new_size <= old_size);
assert (1 < new_size);
if (new_size == 2)
{
assert (mfirst != INVALID_LIT);
assert (msecond != INVALID_LIT);
const bool redundant = dst->redundant;
LOGBINARY (mfirst, msecond, "DST");
kissat_watch_binary (solver, redundant, false, mfirst, msecond);
if (dst->hyper)
DEC (hyper_ternaries);
if (dst->reason)
{
assert (non_false == 1);
assert (other != INVALID_LIT);
assert (forced != INVALID_LIT);
const unsigned forced_idx = IDX (forced);
struct assigned *a = assigned + forced_idx;
assert (!a->binary);
LOGBINARY (mfirst, msecond,
"reason clause[%u] of %s updated to binary reason",
a->reason, LOGLIT (forced));
a->binary = true;
a->reason = other;
}
if (!redundant && last_irredundant == src)
{
if (first == begin)
last_irredundant = 0;
else
last_irredundant = first;
}
}
else
{
assert (2 < new_size);
dst->size = new_size;
dst->shrunken = false;
dst->searched = 2;
LOGCLS (dst, "DST");
if (dst->reason)
update_large_reason (solver, assigned, forced, dst);
clause *next_dst = kissat_next_clause (dst);
if (dst->redundant)
{
if (!first_reducible && !dst->keep)
first_reducible = dst;
redundant_bytes += (char *) next_dst - (char *) dst;
if (!first_redundant)
first_redundant = dst;
}
else
last_irredundant = dst;
dst = next_dst;
}
#ifdef CHECKING_OR_PROVING
if (!checking_or_proving)
continue;
if (new_size != old_size)
{
assert (1 < new_size);
assert (new_size < old_size);
CHECK_AND_ADD_STACK (solver->added);
ADD_STACK_TO_PROOF (solver->added);
REMOVE_CHECKER_STACK (solver->removed);
DELETE_STACK_FROM_PROOF (solver->removed);
}
CLEAR_STACK (solver->added);
CLEAR_STACK (solver->removed);
#endif
}
update_first_reducible (solver, dst, first_reducible);
update_last_irredundant (solver, dst, last_irredundant);
if (first_redundant)
LOGCLS (first_redundant, "determined first redundant clause as");
#if !defined(QUIET) || !defined(NMETRICS)
size_t bytes = (char *) END_STACK (solver->arena) - (char *) dst;
#endif
#ifndef QUIET
if (flushed_literals)
kissat_phase (solver, "collect",
GET (garbage_collections),
"flushed %zu falsified literals in large clauses",
flushed_literals);
size_t flushed_clauses =
flushed_satisfied_clauses + flushed_garbage_clauses;
if (flushed_satisfied_clauses)
kissat_phase (solver, "collect",
GET (garbage_collections),
"flushed %zu satisfied large clauses %.0f%%",
flushed_satisfied_clauses,
kissat_percent (flushed_satisfied_clauses,
flushed_clauses));
if (flushed_garbage_clauses)
kissat_phase (solver, "collect",
GET (garbage_collections),
"flushed %zu large garbage clauses %.0f%%",
flushed_garbage_clauses,
kissat_percent (flushed_garbage_clauses, flushed_clauses));
kissat_phase (solver, "collect",
GET (garbage_collections),
"collected %s in total", FORMAT_BYTES (bytes));
#endif
ADD (literals_flushed, flushed_literals);
#ifndef NMETRICS
ADD (allocated_collected, bytes);
#endif
reference res = INVALID_REF;
if (first_redundant &&
last_irredundant && first_redundant < last_irredundant)
{
#ifdef LOGGING
size_t move_bytes = (char *) dst - (char *) first_redundant;
LOG ("redundant bytes %s (%.0f%) out of %s moving bytes",
FORMAT_BYTES (redundant_bytes),
kissat_percent (redundant_bytes, move_bytes),
FORMAT_BYTES (move_bytes));
#endif
assert (first_redundant < dst);
res = kissat_reference_clause (solver, first_redundant);
assert (res != INVALID_REF);
}
SET_END_OF_STACK (solver->arena, (word *) dst);
kissat_shrink_arena (solver);
kissat_clear_clueue (solver, &solver->clueue);
#ifndef NMETRICS
if (solver->statistics.arena_garbage)
kissat_very_verbose (solver, "still %s garbage left in arena",
FORMAT_BYTES (solver->statistics.arena_garbage));
else
kissat_very_verbose (solver, "all garbage clauses in arena collected");
#endif
return res;
}
static void
rewatch_clauses (kissat * solver, reference start)
{
LOG ("rewatching clause[%zu] and following clauses", start);
assert (solver->watching);
const value *values = solver->values;
const assigned *assigned = solver->assigned;
watches *watches = solver->watches;
const word *arena = BEGIN_STACK (solver->arena);
clause *end = (clause *) END_STACK (solver->arena);
clause *c = (clause *) (BEGIN_STACK (solver->arena) + start);
assert (c <= end);
for (clause * next; c != end; c = next)
{
next = kissat_next_clause (c);
unsigned *lits = c->lits;
kissat_sort_literals (solver, values, assigned, c->size, lits);
c->searched = 2;
const reference ref = (word *) c - arena;
const unsigned l0 = lits[0];
const unsigned l1 = lits[1];
kissat_push_blocking_watch (solver, watches + l0, l1, ref);
kissat_push_blocking_watch (solver, watches + l1, l0, ref);
}
}
void
kissat_sparse_collect (kissat * solver, bool compact, reference start)
{
assert (solver->watching);
START (collect);
INC (garbage_collections);
INC (sparse_garbage_collections);
REPORT (1, 'G');
unsigned vars, mfixed;
if (compact)
vars = kissat_compact_literals (solver, &mfixed);
else
{
vars = solver->vars;
mfixed = INVALID_LIT;
}
flush_all_watched_clauses (solver, compact, start);
reference move = sparse_sweep_garbage_clauses (solver, compact, start);
if (compact)
kissat_finalize_compacting (solver, vars, mfixed);
if (move != INVALID_REF)
move_redundant_clauses_to_the_end (solver, move);
rewatch_clauses (solver, start);
REPORT (1, 'C');
kissat_check_statistics (solver);
STOP (collect);
}
static void
dense_sweep_garbage_clauses (kissat * solver)
{
assert (!solver->level);
assert (!solver->watching);
LOG ("dense garbage collection");
size_t flushed_garbage_clauses = 0;
clause *first_reducible = 0;
clause *last_irredundant = 0;
clause *begin = (clause *) BEGIN_STACK (solver->arena);
const clause *end = (clause *) END_STACK (solver->arena);
clause *src = begin;
clause *dst = src;
for (clause * next; src != end; src = next)
{
if (src->garbage)
{
next = kissat_delete_clause (solver, src);
flushed_garbage_clauses++;
continue;
}
assert (src->size > 1);
LOGCLS (src, "SRC");
next = kissat_next_clause (src);
assert (SIZE_OF_CLAUSE_HEADER == sizeof (unsigned));
*(unsigned *) dst = *(unsigned *) src;
dst->searched = src->searched;
dst->size = src->size;
dst->shrunken = false;
memmove (dst->lits, src->lits, src->size * sizeof (unsigned));
LOGCLS (dst, "DST");
if (!dst->redundant)
last_irredundant = dst;
else if (!first_reducible && !dst->keep)
first_reducible = dst;
dst = kissat_next_clause (dst);
}
update_first_reducible (solver, dst, first_reducible);
update_last_irredundant (solver, dst, last_irredundant);
#if !defined(QUIET) || !defined(NMETRICS)
size_t bytes = (char *) END_STACK (solver->arena) - (char *) dst;
#endif
kissat_phase (solver, "collect",
GET (garbage_collections),
"flushed %zu large garbage clauses", flushed_garbage_clauses);
kissat_phase (solver, "collect",
GET (garbage_collections),
"collected %s in total", FORMAT_BYTES (bytes));
#ifndef NMETRICS
ADD (allocated_collected, bytes);
#endif
SET_END_OF_STACK (solver->arena, (word *) dst);
kissat_shrink_arena (solver);
kissat_clear_clueue (solver, &solver->clueue);
#ifndef NMETRICS
if (solver->statistics.arena_garbage)
kissat_very_verbose (solver, "still %s garbage left in arena",
FORMAT_BYTES (solver->statistics.arena_garbage));
else
kissat_very_verbose (solver, "all garbage clauses in arena collected");
#endif
}
void
kissat_dense_collect (kissat * solver)
{
assert (!solver->watching);
assert (!solver->level);
START (collect);
INC (garbage_collections);
INC (dense_garbage_collections);
REPORT (1, 'G');
dense_sweep_garbage_clauses (solver);
REPORT (1, 'C');
STOP (collect);
}