#include "internal.hpp"
namespace CaDiCaL {
struct literal_occ {
int lit;
int count;
bool operator< (const literal_occ &locc) const {
return (count > locc.count) || (count == locc.count && lit < locc.lit);
}
literal_occ operator++ () {
++count;
return *this;
}
};
std::vector<int> Internal::lookahead_populate_locc () {
std::vector<literal_occ> loccs ((std::size_t) max_var + 1);
for (std::size_t lit = 0; lit < loccs.size (); ++lit) {
loccs[lit].lit = lit;
}
for (const auto &c : clauses)
if (!c->redundant)
for (const auto &lit : *c)
if (active (lit))
++loccs[std::abs (lit)];
std::sort (begin (loccs), end (loccs));
std::vector<int> locc_map;
locc_map.reserve (max_var);
for (const auto &locc : loccs)
locc_map.push_back (locc.lit);
return locc_map;
}
int Internal::lookahead_locc (const std::vector<int> &loccs) {
for (auto lit : loccs)
if (active (abs (lit)) && !assumed (lit) && !assumed (-lit) &&
!val (lit))
return lit;
return 0;
}
int Internal::most_occurring_literal () {
init_noccs ();
for (const auto &c : clauses)
if (!c->redundant)
for (const auto &lit : *c)
if (active (lit))
noccs (lit)++;
int64_t max_noccs = 0;
int res = 0;
if (unsat)
return INT_MIN;
propagate ();
for (int idx = 1; idx <= max_var; idx++) {
if (!active (idx) || assumed (idx) || assumed (-idx) || val (idx))
continue;
for (int sign = -1; sign <= 1; sign += 2) {
const int lit = sign * idx;
if (!active (lit))
continue;
int64_t tmp = noccs (lit);
if (tmp <= max_noccs)
continue;
max_noccs = tmp;
res = lit;
}
}
MSG ("maximum occurrence %" PRId64 " of literal %d", max_noccs, res);
reset_noccs ();
return res;
}
struct probe_negated_noccs_rank {
Internal *internal;
probe_negated_noccs_rank (Internal *i) : internal (i) {}
typedef size_t Type;
Type operator() (int a) const { return internal->noccs (-a); }
};
void Internal::lookahead_flush_probes () {
assert (!probes.empty ());
init_noccs ();
for (const auto &c : clauses) {
int a, b;
if (!is_binary_clause (c, a, b))
continue;
noccs (a)++;
noccs (b)++;
}
const auto eop = probes.end ();
auto j = probes.begin ();
for (auto i = j; i != eop; i++) {
int lit = *i;
if (!active (lit))
continue;
const bool have_pos_bin_occs = noccs (lit) > 0;
const bool have_neg_bin_occs = noccs (-lit) > 0;
if (have_pos_bin_occs == have_neg_bin_occs)
continue;
if (have_pos_bin_occs)
lit = -lit;
assert (!noccs (lit)), assert (noccs (-lit) > 0);
if (propfixed (lit) >= stats.all.fixed)
continue;
MSG ("keeping probe %d negated occs %" PRId64 "", lit, noccs (-lit));
*j++ = lit;
}
size_t remain = j - probes.begin ();
#ifndef QUIET
size_t flushed = probes.size () - remain;
#endif
probes.resize (remain);
rsort (probes.begin (), probes.end (), probe_negated_noccs_rank (this));
reset_noccs ();
shrink_vector (probes);
PHASE ("probe-round", stats.probingrounds,
"flushed %zd literals %.0f%% remaining %zd", flushed,
percent (flushed, remain + flushed), remain);
}
void Internal::lookahead_generate_probes () {
assert (probes.empty ());
init_noccs ();
for (const auto &c : clauses) {
int a, b;
if (!is_binary_clause (c, a, b))
continue;
noccs (a)++;
noccs (b)++;
}
for (int idx = 1; idx <= max_var; idx++) {
const bool have_pos_bin_occs = noccs (idx) > 0;
const bool have_neg_bin_occs = noccs (-idx) > 0;
if (have_pos_bin_occs) {
int probe = -idx;
if (propfixed (probe) >= stats.all.fixed)
continue;
MSG ("scheduling probe %d negated occs %" PRId64 "", probe,
noccs (-probe));
probes.push_back (probe);
}
if (have_neg_bin_occs) {
int probe = idx;
if (propfixed (probe) >= stats.all.fixed)
continue;
MSG ("scheduling probe %d negated occs %" PRId64 "", probe,
noccs (-probe));
probes.push_back (probe);
}
}
rsort (probes.begin (), probes.end (), probe_negated_noccs_rank (this));
reset_noccs ();
shrink_vector (probes);
PHASE ("probe-round", stats.probingrounds,
"scheduled %zd literals %.0f%%", probes.size (),
percent (probes.size (), 2 * max_var));
}
int Internal::lookahead_next_probe () {
int generated = 0;
for (;;) {
if (probes.empty ()) {
if (generated++)
return 0;
lookahead_generate_probes ();
}
while (!probes.empty ()) {
int probe = probes.back ();
probes.pop_back ();
if (!active (probe) || assumed (probe) || assumed (-probe))
continue;
if (propfixed (probe) >= stats.all.fixed)
continue;
return probe;
}
}
}
bool non_tautological_cube (std::vector<int> cube) {
std::sort (begin (cube), end (cube), clause_lit_less_than ());
for (size_t i = 0, j = 1; j < cube.size (); ++i, ++j)
if (cube[i] == cube[j])
return false;
else if (cube[i] == -cube[j])
return false;
else if (cube[i] == 0)
return false;
return true;
}
bool Internal::terminating_asked () {
if (external->terminator && external->terminator->terminate ()) {
MSG ("connected terminator forces termination");
return true;
}
if (termination_forced) {
MSG ("termination forced");
return true;
}
return false;
}
int Internal::lookahead_probing () {
if (!active ())
return 0;
MSG ("lookahead-probe-round %" PRId64
" without propagations limit and %zu assumptions",
stats.probingrounds, assumptions.size ());
termination_forced = false;
#ifndef QUIET
int old_failed = stats.failed;
int64_t old_probed = stats.probed;
#endif
int64_t old_hbrs = stats.hbrs;
if (unsat)
return INT_MIN;
if (level)
backtrack ();
if (!propagate ()) {
MSG ("empty clause before probing");
learn_empty_clause ();
return INT_MIN;
}
if (terminating_asked ())
return most_occurring_literal ();
decompose ();
if (ternary ()) decompose ();
mark_duplicated_binary_clauses_as_garbage ();
lim.conflicts = -1;
if (!probes.empty ())
lookahead_flush_probes ();
for (int idx = 1; idx <= max_var; idx++)
propfixed (idx) = propfixed (-idx) = -1;
assert (unsat || propagated == trail.size ());
propagated = propagated2 = trail.size ();
int probe;
int res = most_occurring_literal ();
int max_hbrs = -1;
set_mode (PROBE);
MSG ("unsat = %d, terminating_asked () = %d ", unsat,
terminating_asked ());
init_probehbr_lrat ();
while (!unsat && !terminating_asked () &&
(probe = lookahead_next_probe ())) {
stats.probed++;
int hbrs;
probe_assign_decision (probe);
if (probe_propagate ())
hbrs = trail.size (), backtrack ();
else
hbrs = 0, failed_literal (probe);
clean_probehbr_lrat ();
if (max_hbrs < hbrs ||
(max_hbrs == hbrs &&
internal->bumped (probe) > internal->bumped (res))) {
res = probe;
max_hbrs = hbrs;
}
}
reset_mode (PROBE);
if (unsat) {
MSG ("probing derived empty clause");
res = INT_MIN;
} else if (propagated < trail.size ()) {
MSG ("probing produced %zd units",
(size_t) (trail.size () - propagated));
if (!propagate ()) {
MSG ("propagating units after probing results in empty clause");
learn_empty_clause ();
res = INT_MIN;
} else
sort_watches ();
}
#ifndef QUIET
int failed = stats.failed - old_failed;
int64_t probed = stats.probed - old_probed;
#endif
int64_t hbrs = stats.hbrs - old_hbrs;
MSG ("lookahead-probe-round %" PRId64 " probed %" PRId64
" and found %d failed literals",
stats.probingrounds, probed, failed);
if (hbrs)
PHASE ("lookahead-probe-round", stats.probingrounds,
"found %" PRId64 " hyper binary resolvents", hbrs);
MSG ("lookahead literal %d with %d\n", res, max_hbrs);
return res;
}
CubesWithStatus Internal::generate_cubes (int depth, int min_depth) {
if (!active () || depth == 0) {
CubesWithStatus cubes;
cubes.status = 0;
cubes.cubes.push_back (std::vector<int> ());
return cubes;
}
lookingahead = true;
START (lookahead);
MSG ("Generating cubes of depth %i", depth);
termination_forced = false;
int res = already_solved ();
if (res == 0)
res = restore_clauses ();
if (unsat)
res = 10;
if (res != 0)
res = solve (true);
if (res != 0) {
MSG ("Solved during preprocessing");
CubesWithStatus cubes;
cubes.status = res;
lookingahead = false;
STOP (lookahead);
return cubes;
}
reset_limits ();
MSG ("generate cubes with %zu assumptions\n", assumptions.size ());
assert (ntab.empty ());
std::vector<int> current_assumptions{assumptions};
std::vector<std::vector<int>> cubes{{assumptions}};
auto loccs{lookahead_populate_locc ()};
LOG ("loccs populated\n");
assert (ntab.empty ());
for (int i = 0; i < depth; ++i) {
LOG ("Probing at depth %i, currently %zu have been generated", i,
cubes.size ());
std::vector<std::vector<int>> cubes2{std::move (cubes)};
cubes.clear ();
for (size_t j = 0; j < cubes2.size (); ++j) {
assert (ntab.empty ());
assert (!unsat);
reset_assumptions ();
for (auto lit : cubes2[j])
assume (lit);
restore_clauses ();
propagate ();
if (unsat) {
LOG ("current cube is unsat; skipping");
unsat = false;
continue;
}
int res = terminating_asked () ? lookahead_locc (loccs)
: lookahead_probing ();
if (unsat) {
LOG ("current cube is unsat; skipping");
unsat = false;
continue;
}
if (res == 0) {
LOG ("no lit to split %i", res);
cubes.push_back (cubes2[j]);
continue;
}
assert (res != 0);
LOG ("splitting on lit %i", res);
std::vector<int> cube1{cubes2[j]};
cube1.push_back (res);
std::vector<int> cube2{std::move (cubes2[j])};
cube2.push_back (-res);
cubes.push_back (cube1);
cubes.push_back (cube2);
}
if (terminating_asked () && i >= min_depth)
break;
}
assert (std::for_each (
std::begin (cubes), std::end (cubes),
[] (std::vector<int> cube) { return non_tautological_cube (cube); }));
reset_assumptions ();
for (auto lit : current_assumptions)
assume (lit);
STOP (lookahead);
lookingahead = false;
if (unsat) {
LOG ("Solved during preprocessing");
CubesWithStatus cubes;
cubes.status = 20;
return cubes;
}
CubesWithStatus rcubes;
rcubes.status = 0;
rcubes.cubes = cubes;
return rcubes;
}
}