#include "internal.hpp"
namespace CaDiCaL {
int64_t Internal::flush_elimfast_occs (int lit) {
const int64_t occslim = opts.fastelimocclim;
const int64_t clslim = opts.fastelimclslim;
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;
}
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;
}
int64_t resolvents = 0;
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;
}
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);
}
void Internal::try_to_fasteliminate_variable (Eliminator &eliminator,
int pivot,
bool &deleted_binary_clause) {
if (!active (pivot))
return;
assert (!frozen (pivot));
const int64_t occ_bound = opts.fastelimocclim;
int64_t bound = opts.fastelimbound;
int64_t pos = flush_elimfast_occs (pivot);
int64_t neg = flush_elimfast_occs (-pivot);
if (neg && pos > occ_bound) {
LOG ("too many occurrences thus not eliminated %d", pivot);
assert (!eliminator.schedule.contains (abs (pivot)));
return;
}
if (pos && neg > occ_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 ());
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);
}
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;
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 ();
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); else {
for (const auto &lit : *c) {
if (!active (lit))
continue;
if (falsified)
mark_elim (lit); noccs (lit)++;
}
}
}
init_occs ();
Eliminator eliminator (this);
ElimSchedule &schedule = eliminator.schedule;
assert (schedule.empty ());
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 ()));
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;
const int64_t garbage_limit = (2 * stats.irrlits / 3) + (1 << 20);
#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 ();
}
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 ();
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; }
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 ();
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;
}
if (subsume_round ())
continue;
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;
}
}
}