613 lines
16 KiB
C
613 lines
16 KiB
C
#include "allocate.h"
|
|
#include "backtrack.h"
|
|
#include "backward.h"
|
|
#include "collect.h"
|
|
#include "dense.h"
|
|
#include "eliminate.h"
|
|
#include "forward.h"
|
|
#include "inline.h"
|
|
#include "propdense.h"
|
|
#include "print.h"
|
|
#include "report.h"
|
|
#include "resolve.h"
|
|
#include "terminate.h"
|
|
#include "trail.h"
|
|
#include "weaken.h"
|
|
|
|
#include <inttypes.h>
|
|
#include <math.h>
|
|
|
|
static bool
|
|
really_eliminate (kissat * solver)
|
|
{
|
|
if (!GET_OPTION (really))
|
|
return true;
|
|
const uint64_t limit = 2 * CLAUSES;
|
|
statistics *statistics = &solver->statistics;
|
|
return limit < statistics->search_ticks + GET_OPTION (eliminatemineff);
|
|
}
|
|
|
|
bool
|
|
kissat_eliminating (kissat * solver)
|
|
{
|
|
if (!solver->enabled.eliminate)
|
|
return false;
|
|
statistics *statistics = &solver->statistics;
|
|
if (!statistics->clauses_irredundant)
|
|
return false;
|
|
if (solver->waiting.eliminate.reduce > statistics->reductions)
|
|
return false;
|
|
limits *limits = &solver->limits;
|
|
if (limits->eliminate.conflicts > statistics->conflicts)
|
|
return false;
|
|
if (!really_eliminate (solver))
|
|
return false;
|
|
if (limits->eliminate.variables.added < statistics->variables_added)
|
|
return true;
|
|
if (limits->eliminate.variables.removed < statistics->variables_removed)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static inline void
|
|
update_after_adding_variable (kissat * solver, heap * schedule, unsigned idx)
|
|
{
|
|
assert (schedule->size);
|
|
kissat_update_variable_score (solver, schedule, idx);
|
|
}
|
|
|
|
static inline void
|
|
update_after_adding_stack (kissat * solver, unsigneds * stack)
|
|
{
|
|
assert (!solver->probing);
|
|
heap *schedule = &solver->schedule;
|
|
if (!schedule->size)
|
|
return;
|
|
for (all_stack (unsigned, lit, *stack))
|
|
update_after_adding_variable (solver, schedule, IDX (lit));
|
|
}
|
|
|
|
static inline void
|
|
update_after_removing_variable (kissat * solver, unsigned idx)
|
|
{
|
|
assert (!solver->probing);
|
|
heap *schedule = &solver->schedule;
|
|
if (!schedule->size)
|
|
return;
|
|
kissat_update_variable_score (solver, schedule, idx);
|
|
if (!kissat_heap_contains (schedule, idx))
|
|
kissat_push_heap (solver, schedule, idx);
|
|
}
|
|
|
|
void
|
|
kissat_update_after_removing_variable (kissat * solver, unsigned idx)
|
|
{
|
|
update_after_removing_variable (solver, idx);
|
|
}
|
|
|
|
static inline void
|
|
update_after_removing_clause (kissat * solver, clause * c, unsigned except)
|
|
{
|
|
assert (c->garbage);
|
|
for (all_literals_in_clause (lit, c))
|
|
if (lit != except)
|
|
update_after_removing_variable (solver, IDX (lit));
|
|
}
|
|
|
|
void
|
|
kissat_update_after_removing_clause (kissat * solver,
|
|
clause * c, unsigned except)
|
|
{
|
|
update_after_removing_clause (solver, c, except);
|
|
}
|
|
|
|
void
|
|
kissat_eliminate_binary (kissat * solver, unsigned lit, unsigned other)
|
|
{
|
|
kissat_disconnect_binary (solver, other, lit);
|
|
kissat_delete_binary (solver, false, false, lit, other);
|
|
update_after_removing_variable (solver, IDX (other));
|
|
}
|
|
|
|
void
|
|
kissat_eliminate_clause (kissat * solver, clause * c, unsigned lit)
|
|
{
|
|
kissat_mark_clause_as_garbage (solver, c);
|
|
update_after_removing_clause (solver, c, lit);
|
|
}
|
|
|
|
static size_t
|
|
schedule_variables (kissat * solver)
|
|
{
|
|
LOG ("initializing variable schedule");
|
|
assert (!solver->schedule.size);
|
|
kissat_resize_heap (solver, &solver->schedule, solver->vars);
|
|
|
|
flags *all_flags = solver->flags;
|
|
|
|
for (all_variables (idx))
|
|
{
|
|
flags *flags = all_flags + idx;
|
|
if (!flags->active)
|
|
continue;
|
|
if (!flags->eliminate)
|
|
continue;
|
|
LOG ("scheduling variable %u", idx);
|
|
update_after_removing_variable (solver, idx);
|
|
}
|
|
size_t scheduled = kissat_size_heap (&solver->schedule);
|
|
#ifndef QUIET
|
|
size_t active = solver->active;
|
|
kissat_phase (solver, "eliminate", GET (eliminations),
|
|
"scheduled %zu variables %.0f%%",
|
|
scheduled, kissat_percent (scheduled, active));
|
|
#endif
|
|
return scheduled;
|
|
}
|
|
|
|
void
|
|
kissat_flush_units_while_connected (kissat * solver)
|
|
{
|
|
unsigned propagated = solver->propagated;
|
|
size_t units = SIZE_STACK (solver->trail) - propagated;
|
|
if (!units)
|
|
return;
|
|
#ifdef LOGGING
|
|
LOG ("propagating and flushing %zu units", units);
|
|
#endif
|
|
if (!kissat_dense_propagate (solver,
|
|
NO_DENSE_PROPAGATION_LIMIT, INVALID_IDX))
|
|
{
|
|
assert (!solver->inconsistent);
|
|
LOG ("inconsistent root propagation of resolved units");
|
|
CHECK_AND_ADD_EMPTY ();
|
|
ADD_EMPTY_TO_PROOF ();
|
|
solver->inconsistent = true;
|
|
return;
|
|
}
|
|
|
|
LOG ("marking and flushing unit satisfied clauses");
|
|
const value *values = solver->values;
|
|
while (propagated < solver->propagated)
|
|
{
|
|
const unsigned unit = PEEK_STACK (solver->trail, propagated);
|
|
propagated++;
|
|
assert (values[unit] > 0);
|
|
watches *unit_watches = &WATCHES (unit);
|
|
watch *begin = BEGIN_WATCHES (*unit_watches), *q = begin;
|
|
const watch *end = END_WATCHES (*unit_watches), *p = q;
|
|
if (begin == end)
|
|
continue;
|
|
LOG ("marking %s satisfied clauses as garbage", LOGLIT (unit));
|
|
while (p != end)
|
|
{
|
|
const watch watch = *q++ = *p++;
|
|
if (watch.type.binary)
|
|
{
|
|
const unsigned other = watch.binary.lit;
|
|
if (!values[other])
|
|
update_after_removing_variable (solver, IDX (other));
|
|
}
|
|
else
|
|
{
|
|
const reference ref = watch.large.ref;
|
|
clause *c = kissat_dereference_clause (solver, ref);
|
|
if (!c->garbage)
|
|
kissat_eliminate_clause (solver, c, unit);
|
|
assert (c->garbage);
|
|
q--;
|
|
}
|
|
}
|
|
assert (q <= end);
|
|
size_t flushed = end - q;
|
|
if (!flushed)
|
|
continue;
|
|
LOG ("flushing %zu references satisfied by %s", flushed, LOGLIT (unit));
|
|
SET_END_OF_WATCHES (*unit_watches, q);
|
|
}
|
|
}
|
|
|
|
static void
|
|
connect_resolvents (kissat * solver)
|
|
{
|
|
bool backward = GET_OPTION (backward);
|
|
const value *values = solver->values;
|
|
assert (EMPTY_STACK (solver->clause.lits));
|
|
uint64_t added = 0;
|
|
bool satisfied = false;
|
|
for (all_stack (unsigned, other, solver->resolvents))
|
|
{
|
|
if (other == INVALID_LIT)
|
|
{
|
|
if (satisfied)
|
|
satisfied = false;
|
|
else if (kissat_forward_subsume_temporary (solver))
|
|
LOGTMP ("temporary forward subsumed");
|
|
else
|
|
{
|
|
size_t size = SIZE_STACK (solver->clause.lits);
|
|
if (!size)
|
|
{
|
|
assert (!solver->inconsistent);
|
|
LOG ("resolved empty clause");
|
|
CHECK_AND_ADD_EMPTY ();
|
|
ADD_EMPTY_TO_PROOF ();
|
|
solver->inconsistent = true;
|
|
break;
|
|
}
|
|
else if (size == 1)
|
|
{
|
|
const unsigned unit = PEEK_STACK (solver->clause.lits, 0);
|
|
LOG ("resolved unit clause %s", LOGLIT (unit));
|
|
kissat_assign_unit (solver, unit);
|
|
CHECK_AND_ADD_UNIT (unit);
|
|
ADD_UNIT_TO_PROOF (unit);
|
|
}
|
|
else
|
|
{
|
|
assert (size > 1);
|
|
reference ref = kissat_new_irredundant_clause (solver);
|
|
update_after_adding_stack (solver, &solver->clause.lits);
|
|
added++;
|
|
|
|
if (backward)
|
|
backward =
|
|
kissat_backward_subsume_temporary (solver, ref);
|
|
}
|
|
}
|
|
CLEAR_STACK (solver->clause.lits);
|
|
}
|
|
else if (!satisfied)
|
|
{
|
|
const value value = values[other];
|
|
if (value > 0)
|
|
{
|
|
LOGTMP ("now %s satisfied resolvent", LOGLIT (other));
|
|
satisfied = true;
|
|
}
|
|
else if (value < 0)
|
|
LOG2 ("dropping now falsified literal %s", LOGLIT (other));
|
|
else
|
|
PUSH_STACK (solver->clause.lits, other);
|
|
}
|
|
}
|
|
LOG ("added %" PRIu64 " new clauses", added);
|
|
CLEAR_STACK (solver->resolvents);
|
|
}
|
|
|
|
static void
|
|
weaken_clauses (kissat * solver, unsigned lit)
|
|
{
|
|
const unsigned not_lit = NOT (lit);
|
|
|
|
const value *values = solver->values;
|
|
assert (!values[lit]);
|
|
|
|
watches *pos_watches = &WATCHES (lit);
|
|
|
|
for (all_binary_large_watches (watch, *pos_watches))
|
|
{
|
|
if (watch.type.binary)
|
|
{
|
|
const unsigned other = watch.binary.lit;
|
|
const value value = values[other];
|
|
if (value <= 0)
|
|
kissat_weaken_binary (solver, lit, other);
|
|
assert (!watch.binary.redundant);
|
|
kissat_eliminate_binary (solver, lit, other);
|
|
}
|
|
else
|
|
{
|
|
const reference ref = watch.large.ref;
|
|
clause *c = kissat_dereference_clause (solver, ref);
|
|
if (c->garbage)
|
|
continue;
|
|
bool satisfied = false;
|
|
for (all_literals_in_clause (other, c))
|
|
{
|
|
const value value = values[other];
|
|
if (value <= 0)
|
|
continue;
|
|
satisfied = true;
|
|
break;
|
|
}
|
|
if (!satisfied)
|
|
kissat_weaken_clause (solver, lit, c);
|
|
LOGCLS (c, "removing %s", LOGLIT (lit));
|
|
kissat_eliminate_clause (solver, c, lit);
|
|
}
|
|
}
|
|
RELEASE_WATCHES (*pos_watches);
|
|
|
|
watches *neg_watches = &WATCHES (not_lit);
|
|
|
|
bool optimize = !GET_OPTION (incremental);
|
|
for (all_binary_large_watches (watch, *neg_watches))
|
|
{
|
|
if (watch.type.binary)
|
|
{
|
|
const unsigned other = watch.binary.lit;
|
|
assert (!watch.binary.redundant);
|
|
const value value = values[other];
|
|
if (!optimize && value <= 0)
|
|
kissat_weaken_binary (solver, not_lit, other);
|
|
kissat_eliminate_binary (solver, not_lit, other);
|
|
}
|
|
else
|
|
{
|
|
const reference ref = watch.large.ref;
|
|
clause *d = kissat_dereference_clause (solver, ref);
|
|
if (d->garbage)
|
|
continue;
|
|
bool satisfied = false;
|
|
for (all_literals_in_clause (other, d))
|
|
{
|
|
const value value = values[other];
|
|
if (value <= 0)
|
|
continue;
|
|
satisfied = true;
|
|
break;
|
|
}
|
|
if (!optimize && !satisfied)
|
|
kissat_weaken_clause (solver, not_lit, d);
|
|
LOGCLS (d, "removing %s", LOGLIT (not_lit));
|
|
kissat_eliminate_clause (solver, d, not_lit);
|
|
}
|
|
}
|
|
if (optimize && neg_watches->size)
|
|
kissat_weaken_unit (solver, not_lit);
|
|
RELEASE_WATCHES (*neg_watches);
|
|
|
|
kissat_flush_units_while_connected (solver);
|
|
}
|
|
|
|
static void
|
|
try_to_eliminate_all_variables_again (kissat * solver)
|
|
{
|
|
LOG ("trying to elimination all variables again");
|
|
flags *all_flags = solver->flags;
|
|
for (all_variables (idx))
|
|
{
|
|
flags *flags = all_flags + idx;
|
|
flags->eliminate = true;
|
|
}
|
|
solver->limits.eliminate.variables.removed = 0;
|
|
}
|
|
|
|
static void
|
|
set_next_elimination_bound (kissat * solver, bool complete)
|
|
{
|
|
const unsigned max_bound = GET_OPTION (eliminatebound);
|
|
const unsigned current_bound = solver->bounds.eliminate.additional_clauses;
|
|
assert (current_bound <= max_bound);
|
|
|
|
if (complete)
|
|
{
|
|
if (current_bound == max_bound)
|
|
{
|
|
kissat_phase (solver, "eliminate", GET (eliminations),
|
|
"completed maximum elimination bound %u",
|
|
current_bound);
|
|
limits *limits = &solver->limits;
|
|
statistics *statistics = &solver->statistics;
|
|
limits->eliminate.variables.added = statistics->variables_added;
|
|
limits->eliminate.variables.removed = statistics->variables_removed;
|
|
#ifndef QUIET
|
|
bool first = !solver->bounds.eliminate.max_bound_completed++;
|
|
REPORT (!first, first ? '!' : ':');
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
const unsigned next_bound =
|
|
!current_bound ? 1 : MIN (2 * current_bound, max_bound);
|
|
kissat_phase (solver, "eliminate", GET (eliminations),
|
|
"completed elimination bound %u next %u",
|
|
current_bound, next_bound);
|
|
solver->bounds.eliminate.additional_clauses = next_bound;
|
|
try_to_eliminate_all_variables_again (solver);
|
|
REPORT (0, '^');
|
|
}
|
|
}
|
|
else
|
|
kissat_phase (solver, "eliminate", GET (eliminations),
|
|
"incomplete elimination bound %u", current_bound);
|
|
}
|
|
|
|
static bool
|
|
can_eliminate_variable (kissat * solver, unsigned idx)
|
|
{
|
|
flags *flags = FLAGS (idx);
|
|
|
|
if (!flags->active)
|
|
return false;
|
|
if (!flags->eliminate)
|
|
return false;
|
|
|
|
LOG ("next variable elimination candidate %u", idx);
|
|
|
|
LOG ("marking variable %u as not removed", idx);
|
|
flags->eliminate = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
eliminate_variable (kissat * solver, unsigned idx)
|
|
{
|
|
assert (!solver->inconsistent);
|
|
if (!can_eliminate_variable (solver, idx))
|
|
return false;
|
|
unsigned lit;
|
|
if (!kissat_generate_resolvents (solver, idx, &lit))
|
|
return false;
|
|
connect_resolvents (solver);
|
|
if (!solver->inconsistent)
|
|
weaken_clauses (solver, lit);
|
|
INC (eliminated);
|
|
kissat_mark_eliminated_variable (solver, idx);
|
|
if (solver->gate_eliminated)
|
|
{
|
|
INC (gates);
|
|
#ifndef NMETRICS
|
|
assert (*solver->gate_eliminated < UINT64_MAX);
|
|
*solver->gate_eliminated += 1;
|
|
#endif
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
eliminate_variables (kissat * solver)
|
|
{
|
|
kissat_very_verbose (solver,
|
|
"trying to eliminate variables with bound %u",
|
|
solver->bounds.eliminate.additional_clauses);
|
|
|
|
unsigned eliminated = 0;
|
|
const unsigned active = solver->active;
|
|
|
|
SET_EFFICIENCY_BOUND (resolution_limit, eliminate,
|
|
resolutions, search_ticks,
|
|
2 * CLAUSES + kissat_nlogn (active));
|
|
|
|
bool complete = false;
|
|
int round = 0;
|
|
|
|
const bool forward = GET_OPTION (forward);
|
|
|
|
for (;;)
|
|
{
|
|
if (forward)
|
|
{
|
|
unsigned propagated = solver->propagated;
|
|
kissat_forward_subsume_during_elimination (solver);
|
|
if (solver->inconsistent)
|
|
break;
|
|
kissat_flush_large_connected (solver);
|
|
kissat_connect_irredundant_large_clauses (solver);
|
|
solver->propagated = propagated;
|
|
kissat_flush_units_while_connected (solver);
|
|
if (solver->inconsistent)
|
|
break;
|
|
}
|
|
else
|
|
kissat_connect_irredundant_large_clauses (solver);
|
|
if (!schedule_variables (solver))
|
|
{
|
|
kissat_release_heap (solver, &solver->schedule);
|
|
complete = true;
|
|
break;
|
|
}
|
|
round++;
|
|
#ifndef QUIET
|
|
LOG ("entering variable elimination round %d", round);
|
|
const unsigned before = eliminated;
|
|
#endif
|
|
while (!kissat_empty_heap (&solver->schedule))
|
|
{
|
|
if (solver->statistics.resolutions > resolution_limit)
|
|
break;
|
|
if (TERMINATED (5))
|
|
break;
|
|
unsigned idx = kissat_max_heap (&solver->schedule);
|
|
kissat_pop_heap (solver, &solver->schedule, idx);
|
|
if (eliminate_variable (solver, idx))
|
|
eliminated++;
|
|
if (solver->inconsistent)
|
|
break;
|
|
}
|
|
if (!solver->inconsistent)
|
|
{
|
|
kissat_flush_large_connected (solver);
|
|
kissat_dense_collect (solver);
|
|
}
|
|
REPORT (before == eliminated, 'e');
|
|
if (solver->inconsistent)
|
|
break;
|
|
kissat_release_heap (solver, &solver->schedule);
|
|
if (round == GET_OPTION (eliminaterounds))
|
|
break;
|
|
if (solver->statistics.resolutions > resolution_limit)
|
|
break;
|
|
if (TERMINATED (6))
|
|
break;
|
|
}
|
|
kissat_phase (solver, "eliminate", GET (eliminations),
|
|
"eliminated %u variables %.0f%% out of %u in %zu rounds",
|
|
eliminated, kissat_percent (eliminated, active), active,
|
|
round);
|
|
if (!solver->inconsistent)
|
|
set_next_elimination_bound (solver, complete);
|
|
}
|
|
|
|
static void
|
|
setup_elim_bounds (kissat * solver)
|
|
{
|
|
solver->bounds.eliminate.clause_size =
|
|
SCALE_LIMIT (eliminations, eliminateclslim);
|
|
|
|
solver->bounds.eliminate.occurrences =
|
|
SCALE_LIMIT (eliminations, eliminateocclim);
|
|
|
|
kissat_phase (solver, "eliminate", GET (eliminations),
|
|
"occurrence limit %u and clause limit %u",
|
|
solver->bounds.eliminate.occurrences,
|
|
solver->bounds.eliminate.clause_size);
|
|
|
|
solver->bounds.subsume.clause_size =
|
|
SCALE_LIMIT (eliminations, subsumeclslim);
|
|
|
|
solver->bounds.subsume.occurrences =
|
|
SCALE_LIMIT (eliminations, subsumeocclim);
|
|
|
|
kissat_phase (solver, "subsume", GET (eliminations),
|
|
"occurrence limit %u clause limit %u",
|
|
solver->bounds.subsume.occurrences,
|
|
solver->bounds.subsume.clause_size);
|
|
|
|
solver->bounds.xork.clause_size =
|
|
log (MAX (solver->bounds.eliminate.occurrences, 1)) / log (2.0) + 0.5;
|
|
if (solver->bounds.xork.clause_size > (unsigned) GET_OPTION (xorsclslim))
|
|
solver->bounds.xork.clause_size = (unsigned) GET_OPTION (xorsclslim);
|
|
assert (solver->bounds.xork.clause_size < 32);
|
|
|
|
LOG ("maximum XOR base clause size %u", solver->bounds.xork.clause_size);
|
|
}
|
|
|
|
static void
|
|
eliminate (kissat * solver)
|
|
{
|
|
RETURN_IF_DELAYED (eliminate);
|
|
kissat_backtrack_propagate_and_flush_trail (solver);
|
|
assert (!solver->inconsistent);
|
|
STOP_SEARCH_AND_START_SIMPLIFIER (eliminate);
|
|
kissat_phase (solver, "eliminate", GET (eliminations),
|
|
"elimination limit of %" PRIu64 " conflicts hit",
|
|
solver->limits.eliminate.conflicts);
|
|
const changes before = kissat_changes (solver);
|
|
setup_elim_bounds (solver);
|
|
litwatches saved;
|
|
INIT_STACK (saved);
|
|
kissat_enter_dense_mode (solver, 0, &saved);
|
|
eliminate_variables (solver);
|
|
kissat_resume_sparse_mode (solver, true, 0, &saved);
|
|
RELEASE_STACK (saved);
|
|
kissat_check_statistics (solver);
|
|
const changes after = kissat_changes (solver);
|
|
const bool changed = kissat_changed (before, after);
|
|
UPDATE_DELAY (changed, eliminate);
|
|
STOP_SIMPLIFIER_AND_RESUME_SEARCH (eliminate);
|
|
}
|
|
|
|
int
|
|
kissat_eliminate (kissat * solver)
|
|
{
|
|
assert (!solver->inconsistent);
|
|
INC (eliminations);
|
|
eliminate (solver);
|
|
UPDATE_CONFLICT_LIMIT (eliminate, eliminations, NLOGNLOGN, true);
|
|
solver->waiting.eliminate.reduce = solver->statistics.reductions + 1;
|
|
return solver->inconsistent ? 20 : 0;
|
|
}
|