#include "internal.hpp"
namespace CaDiCaL {
struct Coveror {
std::vector<int> added; std::vector<int> extend; std::vector<int> covered; std::vector<int> intersection;
size_t alas, clas;
struct {
size_t added, covered;
} next;
Coveror () : alas (0), clas (0) {}
};
inline void Internal::cover_push_extension (int lit, Coveror &coveror) {
coveror.extend.push_back (0);
coveror.extend.push_back (lit); bool found = false;
for (const auto &other : coveror.covered)
if (lit == other)
assert (!found), found = true;
else
coveror.extend.push_back (other);
assert (found);
(void) found;
}
inline void Internal::covered_literal_addition (int lit, Coveror &coveror) {
require_mode (COVER);
assert (level == 1);
cover_push_extension (lit, coveror);
for (const auto &other : coveror.intersection) {
LOG ("covered literal addition %d", other);
assert (!vals[other]), assert (!vals[-other]);
set_val (other, -1);
coveror.covered.push_back (other);
coveror.added.push_back (other);
coveror.clas++;
}
coveror.next.covered = 0;
}
inline void Internal::asymmetric_literal_addition (int lit,
Coveror &coveror) {
require_mode (COVER);
assert (level == 1);
LOG ("initial asymmetric literal addition %d", lit);
assert (!vals[lit]), assert (!vals[-lit]);
set_val (lit, -1);
coveror.added.push_back (lit);
coveror.alas++;
coveror.next.covered = 0;
}
bool Internal::cover_propagate_asymmetric (int lit, Clause *ignore,
Coveror &coveror) {
require_mode (COVER);
stats.propagations.cover++;
assert (val (lit) < 0);
bool subsumed = false;
LOG ("asymmetric literal propagation of %d", lit);
Watches &ws = watches (lit);
const const_watch_iterator eow = ws.end ();
watch_iterator j = ws.begin ();
const_watch_iterator i = j;
while (!subsumed && i != eow) {
const Watch w = *j++ = *i++;
if (w.clause == ignore)
continue; const signed char b = val (w.blit);
if (b > 0)
continue;
if (w.clause->garbage)
j--;
else if (w.binary ()) {
if (b < 0) {
LOG (w.clause, "found subsuming");
subsumed = true;
} else
asymmetric_literal_addition (-w.blit, coveror);
} else {
literal_iterator lits = w.clause->begin ();
const int other = lits[0] ^ lits[1] ^ lit;
lits[0] = other, lits[1] = lit;
const signed char u = val (other);
if (u > 0)
j[-1].blit = other;
else {
const int size = w.clause->size;
const const_literal_iterator end = lits + size;
const literal_iterator middle = lits + w.clause->pos;
literal_iterator k = middle;
signed char v = -1;
int r = 0;
while (k != end && (v = val (r = *k)) < 0)
k++;
if (v < 0) {
k = lits + 2;
assert (w.clause->pos <= size);
while (k != middle && (v = val (r = *k)) < 0)
k++;
}
w.clause->pos = k - lits;
assert (lits + 2 <= k), assert (k <= w.clause->end ());
if (v > 0)
j[-1].blit = r;
else if (!v) {
LOG (w.clause, "unwatch %d in", lit);
lits[1] = r;
*k = lit;
watch_literal (r, lit, w.clause);
j--;
} else if (!u) {
assert (v < 0);
asymmetric_literal_addition (-other, coveror);
} else {
assert (u < 0), assert (v < 0);
LOG (w.clause, "found subsuming");
subsumed = true;
break;
}
}
}
}
if (j != i) {
while (i != eow)
*j++ = *i++;
ws.resize (j - ws.begin ());
}
return subsumed;
}
bool Internal::cover_propagate_covered (int lit, Coveror &coveror) {
require_mode (COVER);
assert (val (lit) < 0);
if (frozen (lit)) {
LOG ("no covered propagation on frozen literal %d", lit);
return false;
}
stats.propagations.cover++;
LOG ("covered propagation of %d", lit);
assert (coveror.intersection.empty ());
Occs &os = occs (-lit);
const auto end = os.end ();
bool first = true;
for (auto i = os.begin (); i != end; i++) {
Clause *c = *i;
if (c->garbage)
continue;
bool blocked = false;
for (const auto &other : *c) {
if (other == -lit)
continue;
const signed char tmp = val (other);
if (tmp < 0)
continue;
if (tmp > 0) {
blocked = true;
break;
}
}
if (blocked) { LOG (c, "blocked");
continue; }
if (first) {
for (const auto &other : *c) {
if (other == -lit)
continue;
const signed char tmp = val (other);
if (tmp < 0)
continue;
assert (!tmp);
coveror.intersection.push_back (other);
mark (other);
}
first = false;
} else {
for (const auto &other : *c) {
if (other == -lit)
continue;
signed char tmp = val (other);
if (tmp < 0)
continue;
assert (!tmp);
tmp = marked (other);
if (tmp > 0)
unmark (other);
}
const auto end = coveror.intersection.end ();
auto j = coveror.intersection.begin ();
for (auto k = j; k != end; k++) {
const int other = *j++ = *k;
const int tmp = marked (other);
assert (tmp >= 0);
if (tmp)
j--, unmark (other); else
mark (other); }
const size_t new_size = j - coveror.intersection.begin ();
coveror.intersection.resize (new_size);
if (!coveror.intersection.empty ())
continue;
auto begin = os.begin ();
while (i != begin) {
auto prev = i - 1;
*i = *prev;
i = prev;
}
*begin = c;
break; }
}
bool res = false;
if (first) {
LOG ("all resolution candidates with %d blocked", -lit);
assert (coveror.intersection.empty ());
cover_push_extension (lit, coveror);
res = true;
} else if (coveror.intersection.empty ()) {
LOG ("empty intersection of resolution candidate literals");
} else {
LOG (coveror.intersection,
"non-empty intersection of resolution candidate literals");
covered_literal_addition (lit, coveror);
unmark (coveror.intersection);
coveror.intersection.clear ();
coveror.next.covered = 0; }
unmark (coveror.intersection);
coveror.intersection.clear ();
return res;
}
bool Internal::cover_clause (Clause *c, Coveror &coveror) {
require_mode (COVER);
assert (!c->garbage);
LOG (c, "trying covered clauses elimination on");
bool satisfied = false;
for (const auto &lit : *c)
if (val (lit) > 0)
satisfied = true;
if (satisfied) {
LOG (c, "clause already satisfied");
mark_garbage (c);
return false;
}
assert (coveror.added.empty ());
assert (coveror.extend.empty ());
assert (coveror.covered.empty ());
assert (!level);
level = 1;
LOG ("assuming literals of candidate clause");
for (const auto &lit : *c) {
if (val (lit))
continue;
asymmetric_literal_addition (lit, coveror);
coveror.covered.push_back (lit);
}
bool tautological = false;
coveror.next.added = coveror.next.covered = 0;
while (!tautological) {
if (coveror.next.added < coveror.added.size ()) {
const int lit = coveror.added[coveror.next.added++];
tautological = cover_propagate_asymmetric (lit, c, coveror);
} else if (coveror.next.covered < coveror.covered.size ()) {
const int lit = coveror.covered[coveror.next.covered++];
tautological = cover_propagate_covered (lit, coveror);
} else
break;
}
if (tautological) {
if (coveror.extend.empty ()) {
stats.cover.asymmetric++;
stats.cover.total++;
LOG (c, "asymmetric tautological");
} else {
stats.cover.blocked++;
stats.cover.total++;
int prev = INT_MIN;
bool already_pushed = false;
int64_t last_id = 0;
LOG (c, "covered tautological");
assert (clause.empty ());
LOG (coveror.extend, "extension = ");
for (const auto &other : coveror.extend) {
if (!prev) {
if (already_pushed) {
for (auto i = 0, j = 0; i < c->size; ++i, ++j) {
const int lit = c->literals[i];
if (j >= (int) coveror.covered.size () ||
c->literals[i] != coveror.covered[j]) {
--j;
LOG ("adding lit %d not needed for ATA", lit);
clause.push_back (lit);
external->push_clause_literal_on_extension_stack (lit);
}
}
}
if (proof && already_pushed) {
if (lrat)
lrat_chain.push_back (c->id);
LOG ("LEARNING clause with id %" PRId64, last_id);
proof->add_derived_clause (last_id, false, clause, lrat_chain);
proof->weaken_plus (last_id, clause);
lrat_chain.clear ();
}
last_id = ++clause_id;
external->push_zero_on_extension_stack ();
external->push_witness_literal_on_extension_stack (other);
external->push_zero_on_extension_stack ();
external->push_id_on_extension_stack (last_id);
external->push_zero_on_extension_stack ();
clause.clear ();
already_pushed = true;
}
if (other) {
external->push_clause_literal_on_extension_stack (other);
clause.push_back (other);
LOG (clause, "current clause is");
}
prev = other;
}
if (proof) {
for (auto i = 0, j = 0; i < c->size; ++i, ++j) {
const int lit = c->literals[i];
if (j >= (int) coveror.covered.size () ||
c->literals[i] != coveror.covered[j]) {
--j;
LOG ("adding lit %d not needed for ATA", lit);
clause.push_back (lit);
external->push_clause_literal_on_extension_stack (lit);
}
}
if (lrat)
lrat_chain.push_back (c->id);
proof->add_derived_clause (last_id, false, clause, lrat_chain);
proof->weaken_plus (last_id, clause);
lrat_chain.clear ();
}
clause.clear ();
mark_garbage (c);
}
}
assert (level == 1);
for (const auto &lit : coveror.added)
set_val (lit, 0);
level = 0;
coveror.covered.clear ();
coveror.extend.clear ();
coveror.added.clear ();
return tautological;
}
struct clause_covered_or_smaller {
bool operator() (const Clause *a, const Clause *b) {
if (a->covered && !b->covered)
return true;
if (!a->covered && b->covered)
return false;
return a->size < b->size;
}
};
int64_t Internal::cover_round () {
if (unsat)
return 0;
init_watches ();
connect_watches (true);
int64_t delta = stats.propagations.search;
delta *= 1e-3 * opts.covereffort;
if (delta < opts.covermineff)
delta = opts.covermineff;
if (delta > opts.covermaxeff)
delta = opts.covermaxeff;
delta = max (delta, ((int64_t) 2) * active ());
PHASE ("cover", stats.cover.count,
"covered clause elimination limit of %" PRId64 " propagations",
delta);
int64_t limit = stats.propagations.cover + delta;
init_occs ();
vector<Clause *> schedule;
Coveror coveror;
#ifndef QUIET
int64_t untried = 0;
#endif
for (auto c : clauses) {
assert (!c->frozen);
if (c->garbage)
continue;
if (c->redundant)
continue;
bool satisfied = false, allfrozen = true;
for (const auto &lit : *c)
if (val (lit) > 0) {
satisfied = true;
break;
} else if (allfrozen && !frozen (lit))
allfrozen = false;
if (satisfied) {
mark_garbage (c);
continue;
}
if (allfrozen) {
c->frozen = true;
continue;
}
for (const auto &lit : *c)
occs (lit).push_back (c);
if (c->size < opts.coverminclslim)
continue;
if (c->size > opts.covermaxclslim)
continue;
if (c->covered)
continue;
schedule.push_back (c);
#ifndef QUIET
untried++;
#endif
}
if (schedule.empty ()) {
PHASE ("cover", stats.cover.count, "no previously untried clause left");
for (auto c : clauses) {
if (c->garbage)
continue;
if (c->redundant)
continue;
if (c->frozen) {
c->frozen = false;
continue;
}
if (c->size < opts.coverminclslim)
continue;
if (c->size > opts.covermaxclslim)
continue;
assert (c->covered);
c->covered = false;
schedule.push_back (c);
}
} else {
for (auto c : clauses) {
if (c->garbage)
continue;
if (c->redundant)
continue;
if (c->frozen) {
c->frozen = false;
continue;
}
if (c->size < opts.coverminclslim)
continue;
if (c->size > opts.covermaxclslim)
continue;
if (!c->covered)
continue;
schedule.push_back (c);
}
}
stable_sort (schedule.begin (), schedule.end (),
clause_covered_or_smaller ());
#ifndef QUIET
const size_t scheduled = schedule.size ();
PHASE ("cover", stats.cover.count,
"scheduled %zd clauses %.0f%% with %" PRId64 " untried %.0f%%",
scheduled, percent (scheduled, stats.current.irredundant), untried,
percent (untried, scheduled));
#endif
for (auto lit : lits) {
if (!active (lit))
continue;
Occs &os = occs (lit);
stable_sort (os.begin (), os.end (), clause_smaller_size ());
}
int64_t covered = 0;
while (!terminated_asynchronously () && !schedule.empty () &&
stats.propagations.cover < limit) {
Clause *c = schedule.back ();
schedule.pop_back ();
c->covered = true;
if (cover_clause (c, coveror))
covered++;
}
#ifndef QUIET
const size_t remain = schedule.size ();
const size_t tried = scheduled - remain;
PHASE ("cover", stats.cover.count,
"eliminated %" PRId64 " covered clauses out of %zd tried %.0f%%",
covered, tried, percent (covered, tried));
if (remain)
PHASE ("cover", stats.cover.count,
"remaining %zu clauses %.0f%% untried", remain,
percent (remain, scheduled));
else
PHASE ("cover", stats.cover.count, "all scheduled clauses tried");
#endif
reset_occs ();
reset_watches ();
return covered;
}
bool Internal::cover () {
if (!opts.cover)
return false;
if (unsat)
return false;
if (terminated_asynchronously ())
return false;
if (!stats.current.irredundant)
return false;
if (opts.restoreflush)
return false;
START_SIMPLIFIER (cover, COVER);
stats.cover.count++;
if (propagated < trail.size ()) {
init_watches ();
connect_watches (); LOG ("elimination produced %zd units",
(size_t) (trail.size () - propagated));
if (!propagate ()) {
LOG ("propagating units before covered clause elimination "
"results in empty clause");
learn_empty_clause ();
assert (unsat);
}
reset_watches ();
}
assert (unsat || propagated == trail.size ());
int64_t covered = cover_round ();
STOP_SIMPLIFIER (cover, COVER);
report ('c', !opts.reportall && !covered);
return covered;
}
}