443 lines
11 KiB
C
443 lines
11 KiB
C
#include "allocate.h"
|
|
#include "autarky.h"
|
|
#include "decide.h"
|
|
#include "dense.h"
|
|
#include "inline.h"
|
|
#include "print.h"
|
|
#include "report.h"
|
|
#include "terminate.h"
|
|
#include "weaken.h"
|
|
|
|
static inline void
|
|
autarky_unassign (kissat * solver,
|
|
value * autarky, unsigneds * work, unsigned lit)
|
|
{
|
|
const unsigned not_lit = NOT (lit);
|
|
assert (autarky[lit] > 0);
|
|
LOG ("autarky unassign %s", LOGLIT (lit));
|
|
autarky[lit] = autarky[not_lit] = 0;
|
|
if (work)
|
|
PUSH_STACK (*work, lit);
|
|
}
|
|
|
|
static inline unsigned
|
|
propagate_clause (kissat * solver,
|
|
const value * values, value * autarky,
|
|
unsigneds * work, clause * c)
|
|
{
|
|
assert (!c->garbage);
|
|
assert (!c->redundant);
|
|
unsigned unassigned = 0;
|
|
bool satisfied = false, falsified = false;
|
|
for (all_literals_in_clause (other, c))
|
|
{
|
|
value other_value = values[other];
|
|
if (other_value > 0)
|
|
{
|
|
LOGCLS (c, "%s satisfied", LOGLIT (other));
|
|
kissat_mark_clause_as_garbage (solver, c);
|
|
return 0;
|
|
}
|
|
if (other_value < 0)
|
|
continue;
|
|
other_value = autarky[other];
|
|
if (other_value > 0)
|
|
satisfied = true;
|
|
else if (other_value < 0)
|
|
falsified = true;
|
|
}
|
|
if (satisfied)
|
|
return 0;
|
|
if (!falsified)
|
|
return 0;
|
|
for (all_literals_in_clause (other, c))
|
|
{
|
|
value other_value = values[other];
|
|
if (other_value < 0)
|
|
continue;
|
|
assert (!other_value);
|
|
other_value = autarky[other];
|
|
if (!other_value)
|
|
continue;
|
|
assert (other_value < 0);
|
|
autarky_unassign (solver, autarky, work, NOT (other));
|
|
assert (unassigned < UINT_MAX);
|
|
unassigned++;
|
|
}
|
|
return unassigned;
|
|
}
|
|
|
|
static inline unsigned
|
|
propagate_unassigned (kissat * solver, word * arena,
|
|
const value * values, value * autarky,
|
|
unsigneds * work, unsigned lit)
|
|
{
|
|
assert (!autarky[lit]);
|
|
assert (!solver->watching);
|
|
assert (!solver->level);
|
|
LOG ("autarky propagate unassigned %s", LOGLIT (lit));
|
|
unsigned unassigned = 0;
|
|
watches *watches = &WATCHES (lit);
|
|
watch *begin = BEGIN_WATCHES (*watches), *q = begin;
|
|
const watch *end = END_WATCHES (*watches), *p = q;
|
|
while (p != end)
|
|
{
|
|
const watch watch = *p++;
|
|
if (watch.type.binary)
|
|
{
|
|
*q++ = watch;
|
|
assert (!watch.binary.redundant);
|
|
const unsigned other = watch.binary.lit;
|
|
value other_value = values[other];
|
|
if (other_value > 0)
|
|
continue;
|
|
assert (!other_value);
|
|
other_value = autarky[other];
|
|
if (other_value >= 0)
|
|
continue;
|
|
assert (other_value < 0);
|
|
autarky_unassign (solver, autarky, work, NOT (other));
|
|
assert (unassigned < UINT_MAX);
|
|
unassigned++;
|
|
}
|
|
else
|
|
{
|
|
const reference ref = watch.large.ref;
|
|
clause *c = (clause *) (arena + ref);
|
|
assert (kissat_clause_in_arena (solver, c));
|
|
unassigned += propagate_clause (solver, values, autarky, work, c);
|
|
}
|
|
}
|
|
SET_END_OF_WATCHES (*watches, q);
|
|
return unassigned;
|
|
}
|
|
|
|
static inline unsigned
|
|
propagate_autarky (kissat * solver,
|
|
const value * values, value * autarky, unsigneds * work)
|
|
{
|
|
unsigned unassigned = 0;
|
|
word *arena = BEGIN_STACK (solver->arena);
|
|
while (!EMPTY_STACK (*work))
|
|
{
|
|
unsigned lit = POP_STACK (*work);
|
|
unassigned += propagate_unassigned (solver, arena,
|
|
values, autarky, work, lit);
|
|
}
|
|
return unassigned;
|
|
}
|
|
|
|
static unsigned
|
|
determine_autarky (kissat * solver, value * autarky, unsigneds * work)
|
|
{
|
|
assert (!solver->level);
|
|
const phase *phases = solver->phases;
|
|
unsigned assigned = 0;
|
|
for (all_variables (idx))
|
|
{
|
|
if (!ACTIVE (idx))
|
|
continue;
|
|
value value = phases[idx].saved;
|
|
if (!value)
|
|
value = INITIAL_PHASE;
|
|
const unsigned lit = LIT (idx);
|
|
const unsigned not_lit = NOT (lit);
|
|
autarky[lit] = value;
|
|
autarky[not_lit] = -value;
|
|
LOG ("autarky assign %s", LOGLIT (value < 0 ? not_lit : lit));
|
|
assigned++;
|
|
}
|
|
|
|
const value *values = solver->values;
|
|
|
|
clause *last_irredundant = kissat_last_irredundant_clause (solver);
|
|
|
|
for (all_clauses (c))
|
|
{
|
|
if (last_irredundant && c > last_irredundant)
|
|
break;
|
|
if (c->garbage)
|
|
continue;
|
|
if (c->redundant)
|
|
continue;
|
|
if (TERMINATED (0))
|
|
return UINT_MAX;
|
|
unsigned unassigned = propagate_clause (solver, values, autarky, 0, c);
|
|
if (!unassigned)
|
|
continue;
|
|
assert (assigned >= unassigned);
|
|
assigned -= unassigned;
|
|
if (!assigned)
|
|
break;
|
|
}
|
|
|
|
if (assigned)
|
|
kissat_phase (solver, "autarky", GET (autarky_determined),
|
|
"preliminary large clause autarky of size %u", assigned);
|
|
else
|
|
{
|
|
kissat_phase (solver, "autarky", GET (autarky_determined),
|
|
"empty preliminary large clause autarky");
|
|
return UINT_MAX;
|
|
}
|
|
|
|
for (all_literals (lit))
|
|
{
|
|
if (!assigned)
|
|
break;
|
|
if (!ACTIVE (IDX (lit)))
|
|
continue;
|
|
value lit_value = autarky[lit];
|
|
if (lit_value > 0)
|
|
continue;
|
|
if (TERMINATED (1))
|
|
return UINT_MAX;
|
|
watches *watches = &WATCHES (lit);
|
|
for (all_binary_large_watches (watch, *watches))
|
|
{
|
|
assert (watch.type.binary);
|
|
assert (!watch.binary.redundant);
|
|
const unsigned other = watch.binary.lit;
|
|
assert (!values[other]);
|
|
const value other_value = autarky[other];
|
|
if (other_value > 0)
|
|
continue;
|
|
if (lit_value)
|
|
{
|
|
assert (lit_value < 0);
|
|
autarky_unassign (solver, autarky, work, NOT (lit));
|
|
lit_value = 0;
|
|
assert (assigned > 0);
|
|
if (!--assigned)
|
|
break;
|
|
}
|
|
if (!other_value)
|
|
continue;
|
|
assert (other_value < 0);
|
|
autarky_unassign (solver, autarky, work, NOT (other));
|
|
assert (assigned > 0);
|
|
if (!--assigned)
|
|
break;
|
|
}
|
|
if (EMPTY_STACK (*work))
|
|
continue;
|
|
const unsigned unassigned =
|
|
propagate_autarky (solver, values, autarky, work);
|
|
assert (unassigned <= assigned);
|
|
assigned -= unassigned;
|
|
}
|
|
|
|
if (assigned)
|
|
kissat_phase (solver, "autarky", GET (autarky_determined),
|
|
"preliminary binary clause autarky of size %u", assigned);
|
|
else
|
|
{
|
|
kissat_phase (solver, "autarky", GET (autarky_determined),
|
|
"empty preliminary binary clause autarky");
|
|
return UINT_MAX;
|
|
}
|
|
|
|
for (all_clauses (c))
|
|
{
|
|
if (last_irredundant && c > last_irredundant)
|
|
break;
|
|
if (c->garbage)
|
|
continue;
|
|
if (c->redundant)
|
|
continue;
|
|
if (TERMINATED (2))
|
|
return UINT_MAX;
|
|
unsigned unassigned =
|
|
propagate_clause (solver, values, autarky, work, c);
|
|
if (unassigned)
|
|
{
|
|
unassigned += propagate_autarky (solver, values, autarky, work);
|
|
assert (unassigned <= assigned);
|
|
assigned -= unassigned;
|
|
if (!assigned)
|
|
break;
|
|
}
|
|
else if (!c->garbage)
|
|
{
|
|
reference ref = INVALID_REF;
|
|
for (all_literals_in_clause (lit, c))
|
|
{
|
|
value lit_value = values[lit];
|
|
if (lit_value < 0)
|
|
continue;
|
|
assert (!lit_value);
|
|
lit_value = autarky[lit];
|
|
if (lit_value <= 0)
|
|
continue;
|
|
if (ref == INVALID_REF)
|
|
ref = kissat_reference_clause (solver, c);
|
|
kissat_connect_literal (solver, lit, ref);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (assigned)
|
|
kissat_phase (solver, "autarky", GET (autarky_determined),
|
|
"final autarky of size %u", assigned);
|
|
else
|
|
kissat_phase (solver, "autarky", GET (autarky_determined),
|
|
"empty final autarky");
|
|
|
|
return assigned;
|
|
}
|
|
|
|
static void
|
|
flush_large_connected_and_autarky_binaries (kissat * solver)
|
|
{
|
|
assert (!solver->watching);
|
|
LOG ("flushing large connected clause references and autarky binaries");
|
|
size_t flushed_large = 0, flushed_binaries = 0;
|
|
const flags *flags = solver->flags;
|
|
for (all_literals (lit))
|
|
{
|
|
watches *watches = &WATCHES (lit);
|
|
watch *begin = BEGIN_WATCHES (*watches), *q = begin;
|
|
const watch *end_watches = END_WATCHES (*watches), *p = q;
|
|
const unsigned lit_idx = IDX (lit);
|
|
const struct flags *lit_flags = flags + lit_idx;
|
|
const bool lit_eliminated = lit_flags->eliminated;
|
|
while (p != end_watches)
|
|
{
|
|
const watch watch = *p++;
|
|
if (!watch.type.binary)
|
|
{
|
|
flushed_large++;
|
|
continue;
|
|
}
|
|
const unsigned other = watch.binary.lit;
|
|
const unsigned idx_other = IDX (other);
|
|
const struct flags *other_flags = flags + idx_other;
|
|
const bool other_eliminated = other_flags->eliminated;
|
|
if (!lit_eliminated && !other_eliminated)
|
|
*q++ = watch;
|
|
else if (lit < other)
|
|
{
|
|
assert (!watch.binary.redundant);
|
|
kissat_delete_binary (solver, false, false, lit, other);
|
|
flushed_binaries++;
|
|
}
|
|
}
|
|
SET_END_OF_WATCHES (*watches, q);
|
|
}
|
|
#ifndef QUIET
|
|
if (flushed_large)
|
|
kissat_very_verbose (solver, "flushed %zu large clause references",
|
|
flushed_large);
|
|
if (flushed_binaries)
|
|
kissat_very_verbose (solver, "flushed %zu autarky binary clauses",
|
|
flushed_binaries);
|
|
#else
|
|
(void) flushed_binaries;
|
|
(void) flushed_large;
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
autarky_literal (kissat * solver, unsigned lit)
|
|
{
|
|
LOG ("autarky literal %s", LOGLIT (lit));
|
|
INC (autarky);
|
|
kissat_mark_eliminated_variable (solver, IDX (lit));
|
|
word *arena = BEGIN_STACK (solver->arena);
|
|
if (GET_OPTION (incremental))
|
|
{
|
|
watches *watches = &WATCHES (lit);
|
|
watch *q = BEGIN_WATCHES (*watches);
|
|
const watch *end = END_WATCHES (*watches);
|
|
const watch *p = q;
|
|
while (p != end)
|
|
{
|
|
const watch watch = *p++;
|
|
if (watch.type.binary)
|
|
{
|
|
assert (!watch.binary.redundant);
|
|
const unsigned other = watch.binary.lit;
|
|
kissat_weaken_binary (solver, lit, other);
|
|
*q++ = watch;
|
|
}
|
|
else
|
|
{
|
|
const reference ref = watch.large.ref;
|
|
clause *c = (clause *) (arena + ref);
|
|
assert (kissat_clause_in_arena (solver, c));
|
|
assert (!c->redundant);
|
|
if (!c->garbage)
|
|
{
|
|
kissat_weaken_clause (solver, lit, c);
|
|
kissat_mark_clause_as_garbage (solver, c);
|
|
}
|
|
}
|
|
}
|
|
SET_END_OF_WATCHES (*watches, q);
|
|
}
|
|
else
|
|
kissat_weaken_unit (solver, lit);
|
|
}
|
|
|
|
static void
|
|
apply_autarky (kissat * solver, unsigned size, value * autarky)
|
|
{
|
|
unsigned eliminated = 0;
|
|
for (all_variables (idx))
|
|
{
|
|
unsigned lit = LIT (idx);
|
|
const value value = autarky[lit];
|
|
if (!value)
|
|
continue;
|
|
if (value < 0)
|
|
lit = NOT (lit);
|
|
assert (autarky[lit] > 0);
|
|
autarky_literal (solver, lit);
|
|
eliminated++;
|
|
}
|
|
LOG ("eliminated %u autarky variables", eliminated);
|
|
assert (eliminated == size);
|
|
(void) eliminated;
|
|
(void) size;
|
|
}
|
|
|
|
void
|
|
kissat_autarky (kissat * solver)
|
|
{
|
|
if (solver->inconsistent)
|
|
return;
|
|
if (TERMINATED (3))
|
|
return;
|
|
if (!solver->enabled.autarky)
|
|
return;
|
|
RETURN_IF_DELAYED (autarky);
|
|
assert (solver->watching);
|
|
assert (!solver->level);
|
|
STOP_SEARCH_AND_START_SIMPLIFIER (autarky);
|
|
INC (autarky_determined);
|
|
litwatches saved;
|
|
INIT_STACK (saved);
|
|
kissat_enter_dense_mode (solver, 0, &saved);
|
|
solver->watching = false;
|
|
value *autarky = kissat_calloc (solver, LITS, sizeof (value));
|
|
unsigneds work;
|
|
INIT_STACK (work);
|
|
unsigned autarkic = determine_autarky (solver, autarky, &work);
|
|
RELEASE_STACK (work);
|
|
if (autarkic && autarkic < UINT_MAX)
|
|
{
|
|
apply_autarky (solver, autarkic, autarky);
|
|
flush_large_connected_and_autarky_binaries (solver);
|
|
}
|
|
else if (autarkic != UINT_MAX)
|
|
kissat_flush_large_connected (solver);
|
|
kissat_dealloc (solver, autarky, LITS, sizeof (value));
|
|
kissat_resume_sparse_mode (solver, true, 0, &saved);
|
|
RELEASE_STACK (saved);
|
|
bool success = (autarkic && autarkic < UINT_MAX);
|
|
UPDATE_DELAY (success, autarky);
|
|
REPORT (!success, 'a');
|
|
STOP_SIMPLIFIER_AND_RESUME_SEARCH (autarky);
|
|
kissat_check_statistics (solver);
|
|
}
|