#include "vivify.hpp"
#include "internal.hpp"
#include "util.hpp"
#include <algorithm>
#include <limits>
#include <utility>
namespace CaDiCaL {
inline void Internal::vivify_subsume_clause (Clause *subsuming,
Clause *subsumed) {
assert (subsumed != subsuming);
stats.subsumed++;
stats.vivifysubs++;
#ifndef NDEBUG
assert (subsuming);
assert (subsumed);
assert (subsuming != subsumed);
assert (!subsumed->garbage);
int real_size_subsuming = 0, real_size_subsumed = 0;
for (auto lit : *subsuming) {
if (!val (lit) || var (lit).level)
++real_size_subsuming;
else
assert (val (lit) < 0);
}
for (auto lit : *subsumed) {
if (!val (lit) || var (lit).level)
++real_size_subsumed;
else
assert (val (lit) < 0);
}
assert (real_size_subsuming <= real_size_subsumed);
#endif
LOG (subsumed, "subsumed to be deleted");
LOG (subsuming, "subsuming to be (un)deleted");
if (subsumed->redundant && subsuming->redundant &&
subsuming->glue < subsumed->glue) {
promote_clause (subsuming, subsumed->glue);
}
if (subsumed->redundant) {
stats.subred++;
++stats.vivifysubred;
} else {
stats.subirr++;
++stats.vivifysubirr;
}
if (subsuming->garbage) {
assert (subsuming->size == 2);
LOG (subsuming,
"binary subsuming clause was already deleted, so undeleting");
subsuming->garbage = false;
subsuming->glue = 1;
++stats.current.total;
if (subsuming->redundant)
stats.current.redundant++;
else
stats.current.irredundant++, stats.irrlits += subsuming->size;
stats.garbage.literals -= subsuming->size;
--stats.garbage.clauses;
}
if (subsumed->redundant || !subsuming->redundant) {
mark_garbage (subsumed);
return;
}
LOG ("turning redundant subsuming clause into irredundant clause");
subsuming->redundant = false;
if (proof)
proof->strengthen (subsuming->id);
mark_garbage (subsumed);
mark_added (subsuming);
stats.current.irredundant++;
stats.added.irredundant++;
stats.irrlits += subsuming->size;
assert (stats.current.redundant > 0);
stats.current.redundant--;
assert (stats.added.redundant > 0);
stats.added.redundant--;
}
inline void Internal::demote_clause (Clause *c) {
stats.subsumed++;
stats.vivifydemote++;
LOG (c, "demoting");
assert (!c->redundant);
mark_removed (c);
c->redundant = true;
assert (stats.current.irredundant > 0);
stats.current.irredundant--;
assert (stats.added.irredundant > 0);
stats.added.irredundant--;
stats.irrlits -= c->size;
stats.current.redundant++;
stats.added.redundant++;
c->glue = c->size - 1;
}
inline void Internal::vivify_assign (int lit, Clause *reason) {
require_mode (VIVIFY);
const int idx = vidx (lit);
assert (!vals[idx]);
assert (!flags (idx).eliminated () || !reason);
Var &v = var (idx);
v.level = level; v.trail = (int) trail.size (); assert ((int) num_assigned < max_var);
num_assigned++;
v.reason = level ? reason : 0; if (!level)
learn_unit_clause (lit);
const signed char tmp = sign (lit);
vals[idx] = tmp;
vals[-idx] = -tmp;
assert (val (lit) > 0);
assert (val (-lit) < 0);
trail.push_back (lit);
LOG (reason, "vivify assign %d", lit);
}
void Internal::vivify_assume (int lit) {
require_mode (VIVIFY);
level++;
control.push_back (Level (lit, trail.size ()));
LOG ("vivify decide %d", lit);
assert (level > 0);
assert (propagated == trail.size ());
vivify_assign (lit, 0);
}
bool Internal::vivify_propagate (int64_t &ticks) {
require_mode (VIVIFY);
assert (!unsat);
START (propagate);
int64_t before = propagated2 = propagated;
for (;;) {
if (propagated2 != trail.size ()) {
const int lit = -trail[propagated2++];
LOG ("vivify propagating %d over binary clauses", -lit);
Watches &ws = watches (lit);
ticks +=
1 + cache_lines (ws.size (), sizeof (const_watch_iterator *));
for (const auto &w : ws) {
if (!w.binary ())
continue;
const signed char b = val (w.blit);
if (b > 0)
continue;
if (b < 0)
conflict = w.clause; else {
ticks++;
build_chain_for_units (w.blit, w.clause, 0);
vivify_assign (w.blit, w.clause);
lrat_chain.clear ();
}
}
} else if (!conflict && propagated != trail.size ()) {
const int lit = -trail[propagated++];
LOG ("vivify propagating %d over large clauses", -lit);
Watches &ws = watches (lit);
const const_watch_iterator eow = ws.end ();
const_watch_iterator i = ws.begin ();
ticks += 1 + cache_lines (ws.size (), sizeof (*i));
watch_iterator j = ws.begin ();
while (i != eow) {
const Watch w = *j++ = *i++;
if (w.binary ())
continue;
if (val (w.blit) > 0)
continue;
ticks++;
if (w.clause->garbage) {
j--;
continue;
}
literal_iterator lits = w.clause->begin ();
const int other = lits[0] ^ 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", r);
lits[0] = other;
lits[1] = r;
*k = lit;
ticks++;
watch_literal (r, lit, w.clause);
j--;
} else if (!u) {
if (w.clause == ignore) {
LOG ("ignoring propagation due to clause to vivify");
continue;
}
ticks++;
assert (v < 0);
vivify_chain_for_units (other, w.clause);
vivify_assign (other, w.clause);
lrat_chain.clear ();
} else {
if (w.clause == ignore) {
LOG ("ignoring conflict due to clause to vivify");
continue;
}
assert (u < 0);
assert (v < 0);
conflict = w.clause;
break;
}
}
}
if (j != i) {
while (i != eow)
*j++ = *i++;
ws.resize (j - ws.begin ());
}
} else
break;
}
int64_t delta = propagated2 - before;
stats.propagations.vivify += delta;
if (conflict)
LOG (conflict, "conflict");
STOP (propagate);
return !conflict;
}
struct vivify_more_noccs {
Internal *internal;
vivify_more_noccs (Internal *i) : internal (i) {}
bool operator() (int a, int b) {
int64_t n = internal->noccs (a);
int64_t m = internal->noccs (b);
if (n > m)
return true; if (n < m)
return false; if (a == -b)
return a > 0; return abs (a) < abs (b); }
};
struct vivify_more_noccs_kissat {
Internal *internal;
vivify_more_noccs_kissat (Internal *i) : internal (i) {}
bool operator() (int a, int b) {
unsigned s = internal->noccs (a);
unsigned t = internal->noccs (b);
return (((t - s) |
((internal->vlit (a) - internal->vlit (b)) & ~(s - t))) >>
31);
}
};
struct vivify_flush_smaller {
bool operator() (Clause *a, Clause *b) const {
const auto eoa = a->end (), eob = b->end ();
auto i = a->begin (), j = b->begin ();
for (; i != eoa && j != eob; i++, j++)
if (*i != *j)
return *i < *j;
const bool smaller = j == eob && i != eoa;
return smaller;
}
};
void Internal::flush_vivification_schedule (std::vector<Clause *> &schedule,
int64_t &ticks) {
ticks += 1 + 3 * cache_lines (schedule.size (), sizeof (Clause *));
stable_sort (schedule.begin (), schedule.end (), vivify_flush_smaller ());
const auto end = schedule.end ();
auto j = schedule.begin (), i = j;
Clause *prev = 0;
int64_t subsumed = 0;
for (; i != end; i++) {
ticks++;
Clause *c = *j++ = *i;
if (!prev || c->size < prev->size) {
prev = c;
continue;
}
const auto eop = prev->end ();
auto k = prev->begin ();
for (auto l = c->begin (); k != eop; k++, l++)
if (*k != *l)
break;
if (k == eop) {
LOG (c, "found subsumed");
LOG (prev, "subsuming");
assert (!c->garbage);
assert (!prev->garbage);
assert (c->redundant || !prev->redundant);
mark_garbage (c);
subsumed++;
j--;
} else
prev = c;
}
if (subsumed)
PHASE ("vivify", stats.vivifications,
"flushed %" PRId64 " subsumed scheduled clauses", subsumed);
stats.vivifysubs += subsumed;
stats.vivifyflushed += subsumed;
if (subsumed) {
schedule.resize (j - schedule.begin ());
shrink_vector (schedule);
} else
assert (j == end);
}
bool Internal::consider_to_vivify_clause (Clause *c) {
if (c->garbage)
return false;
if (opts.vivifyonce >= 1 && c->redundant && c->vivified)
return false;
if (opts.vivifyonce >= 2 && !c->redundant && c->vivified)
return false;
if (!c->redundant)
return true;
assert (c->redundant);
return true;
}
struct vivify_better_watch {
Internal *internal;
vivify_better_watch (Internal *i) : internal (i) {}
bool operator() (int a, int b) {
const signed char av = internal->val (a), bv = internal->val (b);
if (av >= 0 && bv < 0)
return true;
if (av < 0 && bv >= 0)
return false;
return internal->var (a).trail > internal->var (b).trail;
}
};
void Internal::vivify_strengthen (Clause *c, int64_t &ticks) {
assert (!clause.empty ());
if (clause.size () == 1) {
backtrack_without_updating_phases ();
const int unit = clause[0];
LOG (c, "vivification shrunken to unit %d", unit);
assert (!val (unit));
assign_unit (unit);
stats.vivifyunits++;
bool ok = vivify_propagate (ticks);
if (!ok)
learn_empty_clause ();
} else {
sort (clause.begin (), clause.end (), vivify_better_watch (this));
int new_level = level;
const int lit0 = clause[0];
signed char val0 = val (lit0);
if (val0 < 0) {
const int level0 = var (lit0).level;
LOG ("1st watch %d negative at level %d", lit0, level0);
new_level = level0 - 1;
}
const int lit1 = clause[1];
const signed char val1 = val (lit1);
if (val1 < 0 && !(val0 > 0 && var (lit0).level <= var (lit1).level)) {
const int level1 = var (lit1).level;
LOG ("2nd watch %d negative at level %d", lit1, level1);
new_level = level1 - 1;
}
assert (new_level >= 0);
if (new_level < level)
backtrack (new_level);
assert (val (lit0) >= 0);
assert (val (lit1) >= 0 || (val (lit0) > 0 && val (lit1) < 0 &&
var (lit0).level <= var (lit1).level));
Clause *d = new_clause_as (c);
LOG (c, "before vivification");
LOG (d, "after vivification");
(void) d;
}
clause.clear ();
mark_garbage (c);
lrat_chain.clear ();
++stats.vivifystrs;
}
void Internal::vivify_sort_watched (Clause *c) {
sort (c->begin (), c->end (), vivify_better_watch (this));
int new_level = level;
const int lit0 = c->literals[0];
signed char val0 = val (lit0);
if (val0 < 0) {
const int level0 = var (lit0).level;
LOG ("1st watch %d negative at level %d", lit0, level0);
new_level = level0 - 1;
}
const int lit1 = c->literals[1];
const signed char val1 = val (lit1);
if (val1 < 0 && !(val0 > 0 && var (lit0).level <= var (lit1).level)) {
const int level1 = var (lit1).level;
LOG ("2nd watch %d negative at level %d", lit1, level1);
new_level = level1 - 1;
}
assert (new_level >= 0);
if (new_level < level)
backtrack_without_updating_phases (new_level);
assert (val (lit0) >= 0);
assert (val (lit1) >= 0 || (val (lit0) > 0 && val (lit1) < 0 &&
var (lit0).level <= var (lit1).level));
}
void Internal::vivify_analyze (Clause *start, bool &subsumes,
Clause **subsuming,
const Clause *const candidate, int implied,
bool &redundant) {
const auto &t = &trail; int i = t->size (); Clause *reason = start;
assert (reason);
assert (!trail.empty ());
int uip = trail.back ();
bool mark_implied = (implied);
while (i >= 0) {
if (reason) {
redundant = (redundant || reason->redundant);
subsumes = (start != reason && reason->size <= start->size);
LOG (reason, "resolving on %d with", uip);
for (auto other : *reason) {
const Var v = var (other);
Flags &f = flags (other);
if (!marked2 (other) && v.level) {
LOG ("not subsuming due to lit %d", other);
subsumes = false;
}
if (!val (other)) {
LOG ("skipping unset lit %d", other);
continue;
}
if (other == uip) {
continue;
}
if (!v.level) {
if (f.seen || !lrat || reason == start)
continue;
LOG ("unit reason for %d", other);
int64_t id = unit_id (-other);
LOG ("adding unit reason %" PRId64 " for %s", id, LOGLIT (other));
unit_chain.push_back (id);
f.seen = true;
analyzed.push_back (other);
continue;
}
if (mark_implied && other != implied) {
LOG ("skipping non-implied literal %d on current level", other);
continue;
}
assert (val (other));
if (f.seen)
continue;
LOG ("pushing lit %d", other);
analyzed.push_back (other);
f.seen = true;
}
if (reason != start && reason->redundant) {
const int new_glue = recompute_glue (reason);
promote_clause (reason, new_glue);
}
if (subsumes) {
assert (reason);
LOG (reason, "clause found subsuming");
LOG (candidate, "clause found subsumed");
*subsuming = reason;
return;
}
} else {
LOG ("vivify analyzed decision %d", uip);
clause.push_back (-uip);
}
mark_implied = false;
uip = 0;
while (!uip && i > 0) {
assert (i > 0);
const int lit = (*t)[--i];
if (!var (lit).level)
continue;
if (flags (lit).seen)
uip = lit;
}
if (!uip)
break;
LOG ("uip is %d", uip);
Var &w = var (uip);
reason = w.reason;
if (lrat && reason)
lrat_chain.push_back (reason->id);
}
(void) candidate;
}
void Internal::vivify_deduce (Clause *candidate, Clause *conflict,
int implied, Clause **subsuming,
bool &redundant) {
assert (lrat_chain.empty ());
bool subsumes;
Clause *reason;
assert (clause.empty ());
if (implied) {
reason = candidate;
mark2 (candidate);
const int not_implied = -implied;
assert (var (not_implied).level);
Flags &f = flags (not_implied);
f.seen = true;
LOG ("pushing implied lit %d", not_implied);
analyzed.push_back (not_implied);
clause.push_back (implied);
} else {
reason = (conflict ? conflict : candidate);
assert (reason);
assert (!reason->garbage || reason->size == 2);
mark2 (candidate);
subsumes = (candidate != reason);
redundant = reason->redundant;
LOG (reason, "resolving with");
if (lrat)
lrat_chain.push_back (reason->id);
for (auto lit : *reason) {
const Var &v = var (lit);
Flags &f = flags (lit);
assert (val (lit) < 0);
if (!v.level) {
if (!lrat)
continue;
LOG ("adding unit %d", lit);
if (!f.seen) {
int64_t id = unit_id (-lit);
LOG ("adding unit reason %" PRId64 " for %s", id, LOGLIT (lit));
unit_chain.push_back (id);
}
f.seen = true;
analyzed.push_back (lit);
continue;
}
assert (v.level);
if (!marked2 (lit)) {
LOG ("lit %d is not marked", lit);
subsumes = false;
}
LOG ("analyzing lit %d", lit);
LOG ("pushing lit %d", lit);
analyzed.push_back (lit);
f.seen = true;
}
if (reason != candidate && reason->redundant) {
const int new_glue = recompute_glue (reason);
promote_clause (reason, new_glue);
}
if (subsumes) {
assert (candidate != reason);
#ifndef NDEBUG
int nonfalse_reason = 0;
for (auto lit : *reason)
if (!fixed (lit))
++nonfalse_reason;
int nonfalse_candidate = 0;
for (auto lit : *candidate)
if (!fixed (lit))
++nonfalse_candidate;
assert (nonfalse_reason <= nonfalse_candidate);
#endif
LOG (candidate, "vivify subsumed 0");
LOG (reason, "vivify subsuming 0");
*subsuming = reason;
unmark (candidate);
if (lrat)
lrat_chain.clear ();
return;
}
}
vivify_analyze (reason, subsumes, subsuming, candidate, implied,
redundant);
unmark (candidate);
if (subsumes) {
assert (*subsuming);
LOG (candidate, "vivify subsumed");
LOG (*subsuming, "vivify subsuming");
if (lrat)
lrat_chain.clear ();
}
}
bool Internal::vivify_shrinkable (const std::vector<int> &sorted,
Clause *conflict) {
unsigned count_implied = 0;
for (auto lit : sorted) {
const signed char value = val (lit);
if (!value) {
LOG ("vivification unassigned %d", lit);
return true;
}
if (value > 0) {
LOG ("vivification implied satisfied %d", lit);
if (conflict)
return true;
if (count_implied++) {
LOG ("at least one implied literal with conflict thus shrinking");
return true;
}
} else {
assert (value < 0);
const Var &v = var (lit);
const Flags &f = flags (lit);
if (!v.level)
continue;
if (!f.seen) {
LOG ("vivification non-analyzed %d", lit);
return true;
}
if (v.reason) {
LOG ("vivification implied falsified %d", lit);
return true;
}
}
}
return false;
}
inline void Internal::vivify_increment_stats (const Vivifier &vivifier) {
switch (vivifier.tier) {
case Vivify_Mode::TIER1:
++stats.vivifystred1;
break;
case Vivify_Mode::TIER2:
++stats.vivifystred2;
break;
case Vivify_Mode::TIER3:
++stats.vivifystred3;
break;
case Vivify_Mode::IRREDUNDANT:
++stats.vivifystrirr;
break;
}
}
bool Internal::vivify_instantiate (
const std::vector<int> &sorted, Clause *c,
std::vector<std::tuple<int, Clause *, bool>> &lrat_stack,
int64_t &ticks) {
LOG ("now trying instantiation");
conflict = nullptr;
const int lit = sorted.back ();
LOG ("vivify instantiation");
assert (!var (lit).reason);
assert (var (lit).level);
assert (val (lit));
backtrack_without_updating_phases (level - 1);
assert (val (lit) == 0);
stats.vivifydecs++;
vivify_assume (lit);
bool ok = vivify_propagate (ticks);
if (!ok) {
LOG (c, "instantiate success with literal %d in", lit);
stats.vivifyinst++;
if (lrat) {
clear_analyzed_literals ();
assert (lrat_chain.empty ());
vivify_build_lrat (0, c, lrat_stack);
vivify_build_lrat (0, conflict, lrat_stack);
clear_analyzed_literals ();
}
int remove = lit;
conflict = nullptr;
unwatch_clause (c);
backtrack_without_updating_phases (level - 2);
strengthen_clause (c, remove);
vivify_sort_watched (c);
watch_clause (c);
assert (!conflict);
return true;
} else {
LOG ("vivify instantiation failed");
return false;
}
}
bool Internal::vivify_clause (Vivifier &vivifier, Clause *c) {
assert (c->size > 2); assert (analyzed.empty ());
assert (!ignore);
c->vivify = false; c->vivified = true;
assert (!c->garbage);
auto &lrat_stack = vivifier.lrat_stack;
auto &ticks = vivifier.ticks;
ticks++;
auto &sorted = vivifier.sorted;
sorted.clear ();
for (const auto &lit : *c) {
const int tmp = fixed (lit);
if (tmp > 0) {
LOG (c, "satisfied by propagated unit %d", lit);
mark_garbage (c);
return false;
} else if (!tmp)
sorted.push_back (lit);
}
assert (sorted.size () > 1);
if (sorted.size () == 2) {
LOG ("skipping actual binary");
return false;
}
sort (sorted.begin (), sorted.end (), vivify_more_noccs_kissat (this));
assert (std::is_sorted (sorted.begin (), sorted.end (),
vivify_more_noccs (this)));
LOG (c, "vivification checking");
stats.vivifychecks++;
if (level) {
#ifdef LOGGING
int orig_level = level;
#endif
int forced = 0;
for (const auto &lit : *c) {
const signed char tmp = val (lit);
if (tmp < 0)
continue;
if (tmp > 0 && var (lit).reason == c)
forced = lit;
break;
}
if (forced) {
LOG ("clause is reason forcing %d", forced);
assert (var (forced).level);
backtrack_without_updating_phases (var (forced).level - 1);
}
if (level) {
int l = 1;
for (const auto &lit : sorted) {
assert (!fixed (lit));
const int decision = control[l].decision;
if (-lit == decision) {
LOG ("reusing decision %d at decision level %d", decision, l);
++stats.vivifyreused;
if (++l > level)
break;
} else {
LOG ("literal %d does not match decision %d at decision level %d",
lit, decision, l);
backtrack_without_updating_phases (l - 1);
break;
}
}
}
LOG ("reused %d decision levels from %d", level, orig_level);
}
LOG (sorted, "sorted size %zd probing schedule", sorted.size ());
ignore = c;
int subsume = 0;
for (const auto &lit : sorted) {
if (subsume)
break;
const signed char tmp = val (lit);
if (tmp) {
const Var &v = var (lit);
assert (v.level);
if (!v.reason) {
LOG ("skipping decision %d", lit);
continue;
}
if (tmp < 0) {
assert (v.level);
LOG ("literal %d is already false and can be removed", lit);
continue;
}
assert (tmp > 0);
LOG ("subsumed since literal %d already true", lit);
subsume = lit; break;
}
assert (!tmp);
stats.vivifydecs++;
vivify_assume (-lit);
LOG ("negated decision %d score %" PRId64 "", lit, noccs (lit));
if (!vivify_propagate (ticks)) {
break; }
}
if (subsume) {
int better_subsume_trail = var (subsume).trail;
for (auto lit : sorted) {
if (val (lit) <= 0)
continue;
const Var v = var (lit);
if (v.trail < better_subsume_trail) {
LOG ("improving subsume from %d at %d to %d at %d", subsume,
better_subsume_trail, lit, v.trail);
better_subsume_trail = v.trail;
subsume = lit;
}
}
}
Clause *subsuming = nullptr;
bool redundant = false;
const int level_after_assumptions = level;
assert (level_after_assumptions);
vivify_deduce (c, conflict, subsume, &subsuming, redundant);
bool res;
if (lrat) {
for (auto id : unit_chain)
lrat_chain.push_back (id);
unit_chain.clear ();
reverse (lrat_chain.begin (), lrat_chain.end ());
}
if (subsuming) {
assert (c != subsuming);
vivify_subsume_clause (subsuming, c);
res = false;
} else if (vivify_shrinkable (sorted, conflict)) {
vivify_increment_stats (vivifier);
LOG ("vivify succeeded, learning new clause");
clear_analyzed_literals ();
LOG (lrat_chain, "lrat");
LOG (clause, "learning clause");
conflict = nullptr; vivify_strengthen (c, ticks);
res = true;
} else if (subsume && c->redundant) {
LOG (c, "vivification implied");
mark_garbage (c);
++stats.vivifyimplied;
res = true;
} else if ((conflict || subsume) && !c->redundant && !redundant) {
if (opts.vivifydemote) {
LOG ("demote clause from irredundant to redundant");
demote_clause (c);
const int new_glue = recompute_glue (c);
promote_clause (c, new_glue);
res = false;
} else {
mark_garbage (c);
++stats.vivifyimplied;
res = true;
}
} else if (subsume) {
LOG (c, "no vivification instantiation with implied literal %d",
subsume);
assert (!c->redundant);
assert (redundant);
res = false;
++stats.vivifyimplied;
} else {
assert (level > 2);
assert ((size_t) level == sorted.size ());
LOG (c, "vivification failed on");
lrat_chain.clear ();
assert (!subsume);
if (!subsume && opts.vivifyinst) {
res = vivify_instantiate (sorted, c, lrat_stack, ticks);
assert (!conflict);
} else {
LOG ("cannot apply instantiation");
res = false;
}
}
if (conflict && level == level_after_assumptions) {
LOG ("forcing backtracking at least one level after conflict");
backtrack_without_updating_phases (level - 1);
}
clause.clear ();
clear_analyzed_literals (); lrat_chain.clear ();
conflict = nullptr;
ignore = nullptr;
if (res) {
switch (vivifier.tier) {
case Vivify_Mode::IRREDUNDANT:
++stats.vivifiedirred;
break;
case Vivify_Mode::TIER1:
++stats.vivifiedtier1;
break;
case Vivify_Mode::TIER2:
++stats.vivifiedtier2;
break;
case Vivify_Mode::TIER3:
++stats.vivifiedtier3;
break;
}
}
return res;
}
void Internal::vivify_build_lrat (
int lit, Clause *reason,
std::vector<std::tuple<int, Clause *, bool>> &stack) {
assert (stack.empty ());
stack.push_back ({lit, reason, false});
while (!stack.empty ()) {
int lit;
Clause *reason;
bool finished;
std::tie (lit, reason, finished) = stack.back ();
LOG ("VIVIFY LRAT justifying %d", lit);
stack.pop_back ();
if (lit && flags (lit).seen) {
LOG ("skipping already justified");
continue;
}
if (finished) {
lrat_chain.push_back (reason->id);
if (lit && reason) {
Flags &f = flags (lit);
f.seen = true;
analyzed.push_back (lit); assert (flags (lit).seen);
}
continue;
} else
stack.push_back ({lit, reason, true});
for (const auto &other : *reason) {
if (other == lit)
continue;
Var &v = var (other);
Flags &f = flags (other);
if (f.seen)
continue;
if (!v.level) {
const int64_t id = unit_id (-other);
lrat_chain.push_back (id);
f.seen = true;
analyzed.push_back (other);
continue;
}
if (v.reason) { LOG ("VIVIFY LRAT pushing %d", other);
stack.push_back ({other, v.reason, false});
}
}
}
stack.clear ();
}
inline void Internal::vivify_chain_for_units (int lit, Clause *reason) {
if (!lrat)
return;
if (level)
return; assert (lrat_chain.empty ());
for (auto &reason_lit : *reason) {
if (lit == reason_lit)
continue;
assert (val (reason_lit));
const int signed_reason_lit = val (reason_lit) * reason_lit;
int64_t id = unit_id (signed_reason_lit);
lrat_chain.push_back (id);
}
lrat_chain.push_back (reason->id);
}
vivify_ref create_ref (Internal *internal, Clause *c) {
LOG (c, "creating vivify_refs of clause");
vivify_ref ref;
ref.clause = c;
ref.size = c->size;
for (int i = 0; i < COUNTREF_COUNTS; ++i)
ref.count[i] = 0;
ref.vivify = c->vivify;
int lits[COUNTREF_COUNTS] = {0};
for (int i = 0; i != std::min (COUNTREF_COUNTS, c->size); ++i) {
int best = 0;
unsigned best_count = 0;
for (auto lit : *c) {
LOG ("to find best number of occurrences for literal %d, looking at "
"literal %d",
i, lit);
for (int j = 0; j != i; ++j) {
LOG ("comparing %d with literal %d", lit, lits[j]);
if (lits[j] == lit)
goto CONTINUE_WITH_NEXT_LITERAL;
}
{
const int64_t lit_count = internal->noccs (lit);
assert (lit_count);
LOG ("checking literal %s with %" PRId64 " occurrences",
LOGLIT (lit), lit_count);
if (lit_count <= best_count)
continue;
best_count = lit_count;
best = lit;
}
CONTINUE_WITH_NEXT_LITERAL:;
}
assert (best);
assert (best_count);
assert (best_count < UINT32_MAX);
ref.count[i] =
(((uint64_t) best_count) << 32) + (uint64_t) internal->vlit (best);
LOG ("final count at position %d is %s - %u: %" PRIu64, i,
LOGLIT (best), best_count, ref.count[i]);
lits[i] = best;
}
return ref;
}
inline void
Internal::vivify_prioritize_leftovers (char tag, size_t prioritized,
std::vector<Clause *> &schedule) {
if (prioritized) {
PHASE ("vivify", stats.vivifications,
"[phase %c] leftovers of %zu clause", tag, prioritized);
} else {
PHASE ("vivify", stats.vivifications,
"[phase %c] prioritizing all clause", tag);
for (auto c : schedule)
c->vivify = true;
}
#ifdef QUIET
(void) tag;
#endif
const size_t max = opts.vivifyschedmax;
if (schedule.size () > max) {
if (prioritized) {
std::stable_partition (begin (schedule), end (schedule),
[] (Clause *c) { return c->vivify; });
}
schedule.resize (max);
}
shrink_vector (schedule);
}
void Internal::vivify_initialize (Vivifier &vivifier, int64_t &ticks) {
const int tier1 = vivifier.tier1_limit;
const int tier2 = vivifier.tier2_limit;
init_noccs ();
assert (watching ());
#if 0#endif
size_t prioritized_irred = 0, prioritized_tier1 = 0,
prioritized_tier2 = 0, prioritized_tier3 = 0;
for (const auto &c : clauses) {
++ticks;
if (c->size == 2)
continue; if (!consider_to_vivify_clause (c))
continue;
const int shift = 12 - c->size;
const int64_t score = shift < 1 ? 1 : (1l << shift); for (const auto lit : *c) {
noccs (lit) += score;
}
LOG (c, "putting clause in candidates");
if (!c->redundant)
vivifier.schedule_irred.push_back (c),
prioritized_irred += (c->vivify);
else if (c->glue <= tier1)
vivifier.schedule_tier1.push_back (c),
prioritized_tier1 += (c->vivify);
else if (c->glue <= tier2)
vivifier.schedule_tier2.push_back (c),
prioritized_tier2 += (c->vivify);
else
vivifier.schedule_tier3.push_back (c),
prioritized_tier3 += (c->vivify);
++ticks;
}
vivify_prioritize_leftovers ('x', prioritized_irred,
vivifier.schedule_irred);
vivify_prioritize_leftovers ('u', prioritized_tier1,
vivifier.schedule_tier1);
vivify_prioritize_leftovers ('v', prioritized_tier2,
vivifier.schedule_tier2);
vivify_prioritize_leftovers ('w', prioritized_tier3,
vivifier.schedule_tier3);
if (opts.vivifyflush) {
clear_watches ();
for (auto &sched : vivifier.schedules) {
ticks += 1 + cache_lines (sched.size (), sizeof (Clause *));
for (const auto &c : sched) {
sort (c->begin (), c->end (), vivify_more_noccs (this));
}
flush_vivification_schedule (sched, ticks);
}
connect_watches (); }
vivify_propagate (ticks);
PHASE ("vivify", stats.vivifications,
"[phase %c] leftovers out of %zu clauses", 'u',
vivifier.schedule_tier1.size ());
}
inline std::vector<vivify_ref> ¤t_refs_schedule (Vivifier &vivifier) {
return vivifier.refs_schedule;
}
inline std::vector<Clause *> ¤t_schedule (Vivifier &vivifier) {
switch (vivifier.tier) {
case Vivify_Mode::TIER1:
return vivifier.schedule_tier1;
break;
case Vivify_Mode::TIER2:
return vivifier.schedule_tier2;
break;
case Vivify_Mode::TIER3:
return vivifier.schedule_tier3;
break;
case Vivify_Mode::IRREDUNDANT:
return vivifier.schedule_irred;
break;
}
__builtin_unreachable ();
}
struct vivify_refcount_rank {
int offset;
vivify_refcount_rank (int j) : offset (j) {
assert (offset < COUNTREF_COUNTS);
}
typedef uint64_t Type;
Type operator() (const vivify_ref &a) const { return a.count[offset]; }
};
struct vivify_refcount_smaller {
int offset;
vivify_refcount_smaller (int j) : offset (j) {
assert (offset < COUNTREF_COUNTS);
}
bool operator() (const vivify_ref &a, const vivify_ref &b) const {
const auto s = vivify_refcount_rank (offset) (a);
const auto t = vivify_refcount_rank (offset) (b);
return s < t;
}
};
struct vivify_inversesize_rank {
vivify_inversesize_rank () {}
typedef uint64_t Type;
Type operator() (const vivify_ref &a) const { return ~a.size; }
};
struct vivify_inversesize_smaller {
vivify_inversesize_smaller () {}
bool operator() (const vivify_ref &a, const vivify_ref &b) const {
const auto s = vivify_inversesize_rank () (a);
const auto t = vivify_inversesize_rank () (b);
return s < t;
}
};
void Internal::vivify_round (Vivifier &vivifier, int64_t ticks_limit) {
if (unsat)
return;
if (terminated_asynchronously ())
return;
auto &refs_schedule = current_refs_schedule (vivifier);
auto &schedule = current_schedule (vivifier);
PHASE ("vivify", stats.vivifications,
"starting %c vivification round ticks limit %" PRId64
" with %zu clauses",
vivifier.tag, ticks_limit, schedule.size ());
assert (watching ());
int64_t ticks = 1 + schedule.size ();
if (vivifier.tier != Vivify_Mode::IRREDUNDANT ||
irredundant () / 10 < redundant ()) {
const auto end_schedule = end (schedule);
refs_schedule.resize (schedule.size ());
std::transform (begin (schedule), end_schedule, begin (refs_schedule),
[&] (Clause *c) { return create_ref (this, c); });
MSORT (opts.radixsortlim, refs_schedule.begin (), refs_schedule.end (),
vivify_inversesize_rank (), vivify_inversesize_smaller ());
for (int i = 0; i < COUNTREF_COUNTS; ++i) {
const int offset = COUNTREF_COUNTS - 1 - i;
MSORT (opts.radixsortlim, refs_schedule.begin (),
refs_schedule.end (), vivify_refcount_rank (offset),
vivify_refcount_smaller (offset));
}
std::stable_partition (begin (refs_schedule), end (refs_schedule),
[] (vivify_ref c) { return !c.vivify; });
std::transform (begin (refs_schedule), end (refs_schedule),
begin (schedule),
[] (vivify_ref c) { return c.clause; });
refs_schedule.clear ();
} else {
std::stable_partition (begin (schedule), end (schedule),
[] (Clause *c) { return !c->vivify; });
}
int64_t checked = stats.vivifychecks;
int64_t subsumed = stats.vivifysubs;
int64_t strengthened = stats.vivifystrs;
int64_t units = stats.vivifyunits;
int64_t scheduled = schedule.size ();
stats.vivifysched += scheduled;
PHASE ("vivify", stats.vivifications,
"scheduled %" PRId64 " clauses to be vivified %.0f%%", scheduled,
percent (scheduled, stats.current.irredundant));
const int64_t limit = ticks_limit - stats.ticks.vivify;
assert (limit >= 0);
propagated2 = propagated = 0;
if (!unsat && !propagate ()) {
LOG ("propagation after connecting watches in inconsistency");
learn_empty_clause ();
}
vivifier.ticks = ticks;
int retry = 0;
while (!unsat && !terminated_asynchronously () && !schedule.empty () &&
vivifier.ticks < limit) {
Clause *c = schedule.back (); schedule.pop_back ();
if (vivify_clause (vivifier, c) && !c->garbage && c->size > 2 &&
retry < opts.vivifyretry) {
++retry;
schedule.push_back (c);
} else
retry = 0;
}
if (level)
backtrack_without_updating_phases ();
if (!unsat) {
int64_t still_need_to_be_vivified = schedule.size ();
#if 0#else
#endif
if (still_need_to_be_vivified)
PHASE ("vivify", stats.vivifications,
"still need to vivify %" PRId64 " clauses %.02f%% of %" PRId64
" scheduled",
still_need_to_be_vivified,
percent (still_need_to_be_vivified, scheduled), scheduled);
else {
PHASE ("vivify", stats.vivifications,
"no previously not yet vivified clause left");
}
erase_vector (schedule); }
if (!unsat) {
propagated2 = propagated = 0;
if (!propagate ()) {
LOG ("propagating vivified units leads to conflict");
learn_empty_clause ();
}
}
checked = stats.vivifychecks - checked;
subsumed = stats.vivifysubs - subsumed;
strengthened = stats.vivifystrs - strengthened;
units = stats.vivifyunits - units;
PHASE ("vivify", stats.vivifications,
"checked %" PRId64 " clauses %.02f%% of %" PRId64
" scheduled using %" PRIu64 " ticks",
checked, percent (checked, scheduled), scheduled, vivifier.ticks);
if (units)
PHASE ("vivify", stats.vivifications,
"found %" PRId64 " units %.02f%% of %" PRId64 " checked", units,
percent (units, checked), checked);
if (subsumed)
PHASE ("vivify", stats.vivifications,
"subsumed %" PRId64 " clauses %.02f%% of %" PRId64 " checked",
subsumed, percent (subsumed, checked), checked);
if (strengthened)
PHASE ("vivify", stats.vivifications,
"strengthened %" PRId64 " clauses %.02f%% of %" PRId64
" checked",
strengthened, percent (strengthened, checked), checked);
stats.subsumed += subsumed;
stats.strengthened += strengthened;
stats.ticks.vivify += vivifier.ticks;
bool unsuccessful = !(subsumed + strengthened + units);
report (vivifier.tag, unsuccessful);
}
void set_vivifier_mode (Vivifier &vivifier, Vivify_Mode tier) {
vivifier.tier = tier;
switch (tier) {
case Vivify_Mode::TIER1:
vivifier.tag = 'u';
break;
case Vivify_Mode::TIER2:
vivifier.tag = 'v';
break;
case Vivify_Mode::TIER3:
vivifier.tag = 'w';
break;
case Vivify_Mode::IRREDUNDANT:
vivifier.tag = 'x';
break;
}
}
void Internal::compute_tier_limits (Vivifier &vivifier) {
if (!opts.vivifycalctier) {
vivifier.tier1_limit = 2;
vivifier.tier2_limit = 6;
return;
}
vivifier.tier1_limit = tier1[false];
vivifier.tier2_limit = tier2[false];
}
bool Internal::vivify () {
if (unsat)
return false;
if (terminated_asynchronously ())
return false;
if (!opts.vivify)
return false;
if (!stats.current.irredundant)
return false;
if (level)
backtrack ();
assert (opts.vivify);
assert (!level);
SET_EFFORT_LIMIT (totallimit, vivify, true);
private_steps = true;
START_SIMPLIFIER (vivify, VIVIFY);
stats.vivifications++;
double tier1effort = !opts.vivifytier1 ? 0 : (double) opts.vivifytier1eff;
double tier2effort = !opts.vivifytier2 ? 0 : (double) opts.vivifytier2eff;
double tier3effort = !opts.vivifytier3 ? 0 : (double) opts.vivifytier3eff;
double irreffort =
delaying_vivify_irredundant.bumpreasons.delay () || !opts.vivifyirred
? 0
: (double) opts.vivifyirredeff;
double sumeffort = tier1effort + tier2effort + tier3effort + irreffort;
if (!stats.current.redundant)
tier1effort = tier2effort = tier3effort = 0;
if (!sumeffort)
sumeffort = irreffort = 1;
int64_t total = totallimit - stats.ticks.vivify;
PHASE ("vivify", stats.vivifications,
"vivification limit of %" PRId64 " ticks", total);
Vivifier vivifier (Vivify_Mode::TIER1);
compute_tier_limits (vivifier);
if (vivifier.tier1_limit == vivifier.tier2_limit) {
tier1effort += tier2effort;
tier2effort = 0;
LOG ("vivification tier1 matches tier2 "
"thus using tier2 budget for tier1");
}
int64_t init_ticks = 0;
vivify_initialize (vivifier, init_ticks);
stats.ticks.vivify += init_ticks;
int64_t limit = stats.ticks.vivify;
const double shared_effort = (double) init_ticks / 4.0;
if (opts.vivifytier1) {
set_vivifier_mode (vivifier, Vivify_Mode::TIER1);
if (limit < stats.ticks.vivify)
limit = stats.ticks.vivify;
const double effort = (total * tier1effort) / sumeffort;
assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >=
limit);
limit += effort;
if (limit - shared_effort > stats.ticks.vivify) {
limit -= shared_effort;
assert (limit >= 0);
vivify_round (vivifier, limit);
} else {
LOG ("building the schedule already used our entire ticks budget for "
"tier1");
}
}
if (!unsat && tier2effort) {
erase_vector (vivifier.schedule_tier1);
if (limit < stats.ticks.vivify)
limit = stats.ticks.vivify;
const double effort = (total * tier2effort) / sumeffort;
assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >=
limit);
limit += effort;
if (limit - shared_effort > stats.ticks.vivify) {
limit -= shared_effort;
assert (limit >= 0);
set_vivifier_mode (vivifier, Vivify_Mode::TIER2);
vivify_round (vivifier, limit);
} else {
LOG ("building the schedule already used our entire ticks budget for "
"tier2");
}
}
if (!unsat && tier3effort) {
erase_vector (vivifier.schedule_tier2);
if (limit < stats.ticks.vivify)
limit = stats.ticks.vivify;
const double effort = (total * tier3effort) / sumeffort;
assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >=
limit);
limit += effort;
if (limit - shared_effort > stats.ticks.vivify) {
limit -= shared_effort;
assert (limit >= 0);
set_vivifier_mode (vivifier, Vivify_Mode::TIER3);
vivify_round (vivifier, limit);
} else {
LOG ("building the schedule already used our entire ticks budget for "
"tier3");
}
}
if (!unsat && irreffort) {
erase_vector (vivifier.schedule_tier3);
if (limit < stats.ticks.vivify)
limit = stats.ticks.vivify;
const double effort = (total * irreffort) / sumeffort;
assert (std::numeric_limits<int64_t>::max () - (int64_t) effort >=
limit);
limit += effort;
if (limit - shared_effort > stats.ticks.vivify) {
limit -= shared_effort;
assert (limit >= 0);
set_vivifier_mode (vivifier, Vivify_Mode::IRREDUNDANT);
const int old = stats.vivifystrirr;
const int old_tried = stats.vivifychecks;
vivify_round (vivifier, limit);
if (stats.vivifychecks - old_tried == 0 ||
(float) (stats.vivifystrirr - old) /
(float) (stats.vivifychecks - old_tried) <
0.01) {
delaying_vivify_irredundant.bumpreasons.bump_delay ();
} else {
delaying_vivify_irredundant.bumpreasons.reduce_delay ();
}
} else {
delaying_vivify_irredundant.bumpreasons.bump_delay ();
LOG ("building the schedule already used our entire ticks budget for "
"irredundant");
}
}
reset_noccs ();
STOP_SIMPLIFIER (vivify, VIVIFY);
private_steps = false;
assert (!ignore);
return true;
}
}