#include "internal.hpp"
namespace CaDiCaL {
void Internal::mark (Clause *c) {
for (const auto &lit : *c)
mark (lit);
}
void Internal::mark2 (Clause *c) {
for (const auto &lit : *c)
mark2 (lit);
}
void Internal::unmark (Clause *c) {
for (const auto &lit : *c)
unmark (lit);
}
void Internal::mark_clause () {
for (const auto &lit : clause)
mark (lit);
}
void Internal::unmark_clause () {
for (const auto &lit : clause)
unmark (lit);
}
void Internal::mark_removed (Clause *c, int except) {
LOG (c, "marking removed");
assert (!c->redundant);
for (const auto &lit : *c)
if (lit != except)
mark_removed (lit);
}
inline void Internal::mark_added (int lit, int size, bool redundant) {
mark_subsume (lit);
if (size == 3)
mark_ternary (lit);
if (!redundant)
mark_block (lit);
if ((!redundant || size == 2))
mark_factor (lit);
}
void Internal::mark_added (Clause *c) {
LOG (c, "marking added");
assert (likely_to_be_kept_clause (c));
for (const auto &lit : *c)
mark_added (lit, c->size, c->redundant);
}
Clause *Internal::new_clause (bool red, int glue) {
assert (clause.size () <= (size_t) INT_MAX);
const int size = (int) clause.size ();
assert (size >= 2);
if (glue > size)
glue = size;
size_t bytes = Clause::bytes (size);
Clause *c = (Clause *) new char[bytes];
DeferDeleteArray<char> clause_delete ((char *) c);
c->id = ++clause_id;
c->conditioned = false;
c->covered = false;
c->enqueued = false;
c->frozen = false;
c->garbage = false;
c->gate = false;
c->hyper = false;
c->instantiated = false;
c->moved = false;
c->reason = false;
c->redundant = red;
c->transred = false;
c->subsume = false;
c->swept = false;
c->flushed = false;
c->vivified = false;
c->vivify = false;
c->used = 0;
c->glue = glue;
c->size = size;
c->pos = 2;
for (int i = 0; i < size; i++)
c->literals[i] = clause[i];
assert (c->bytes () == bytes);
stats.current.total++;
stats.added.total++;
if (red) {
stats.current.redundant++;
stats.added.redundant++;
} else {
stats.irrlits += size;
stats.current.irredundant++;
stats.added.irredundant++;
}
clauses.push_back (c);
clause_delete.release ();
LOG (c, "new pointer %p", (void *) c);
if (likely_to_be_kept_clause (c))
mark_added (c);
return c;
}
void Internal::promote_clause (Clause *c, int new_glue) {
assert (c->redundant);
assert (new_glue);
const int tier1limit = tier1[false];
const int tier2limit = max (tier1limit, tier2[false]);
if (!c->redundant)
return;
if (c->hyper)
return;
int old_glue = c->glue;
if (new_glue >= old_glue)
return;
c->used = max_used;
if (old_glue > tier1limit && new_glue <= tier1limit) {
LOG (c, "promoting with new glue %d to tier1", new_glue);
stats.promoted1++;
} else if (old_glue > tier2limit && new_glue <= tier2limit) {
LOG (c, "promoting with new glue %d to tier2", new_glue);
stats.promoted2++;
} else if (old_glue <= tier2limit)
LOG (c, "keeping with new glue %d in tier2", new_glue);
else
LOG (c, "keeping with new glue %d in tier3", new_glue);
stats.improvedglue++;
c->glue = new_glue;
}
void Internal::promote_clause_glue_only (Clause *c, int new_glue) {
assert (c->redundant);
assert (new_glue);
if (c->hyper)
return;
int old_glue = c->glue;
const int tier1limit = tier1[false];
const int tier2limit = max (tier1limit, tier2[false]);
if (new_glue >= old_glue)
return;
if (new_glue <= tier1limit) {
LOG (c, "promoting with new glue %d to tier1", new_glue);
stats.promoted1++;
} else if (old_glue > tier2limit && new_glue <= tier2limit) {
LOG (c, "promoting with new glue %d to tier2", new_glue);
stats.promoted2++;
} else if (old_glue <= tier2limit)
LOG (c, "keeping with new glue %d in tier2", new_glue);
else
LOG (c, "keeping with new glue %d in tier3", new_glue);
stats.improvedglue++;
c->glue = new_glue;
}
size_t Internal::shrink_clause (Clause *c, int new_size) {
if (opts.check && is_external_forgettable (c->id))
mark_garbage_external_forgettable (c->id);
assert (new_size >= 2);
int old_size = c->size;
assert (new_size < old_size);
#ifndef NDEBUG
for (int i = c->size; i < new_size; i++)
c->literals[i] = 0;
#endif
if (c->pos >= new_size)
c->pos = 2;
size_t old_bytes = c->bytes ();
c->size = new_size;
size_t new_bytes = c->bytes ();
size_t res = old_bytes - new_bytes;
if (c->redundant)
promote_clause_glue_only (c, min (c->size - 1, c->glue));
else {
int delta_size = old_size - new_size;
assert (stats.irrlits >= delta_size);
stats.irrlits -= delta_size;
}
if (likely_to_be_kept_clause (c))
mark_added (c);
return res;
}
void Internal::deallocate_clause (Clause *c) {
char *p = (char *) c;
if (arena.contains (p))
return;
LOG (c, "deallocate pointer %p", (void *) c);
delete[] p;
}
void Internal::delete_clause (Clause *c) {
LOG (c, "delete pointer %p", (void *) c);
size_t bytes = c->bytes ();
stats.collected += bytes;
if (c->garbage) {
assert (stats.garbage.bytes >= (int64_t) bytes);
stats.garbage.bytes -= bytes;
assert (stats.garbage.clauses > 0);
stats.garbage.clauses--;
assert (stats.garbage.literals >= c->size);
stats.garbage.literals -= c->size;
if (proof && c->size == 2 && !c->flushed) {
proof->delete_clause (c);
}
}
deallocate_clause (c);
}
void Internal::mark_garbage (Clause *c) {
assert (!c->garbage);
if (proof && (c->size != 2 || !watching ())) {
c->flushed = true;
proof->delete_clause (c);
}
if (opts.check && is_external_forgettable (c->id))
mark_garbage_external_forgettable (c->id);
assert (stats.current.total > 0);
stats.current.total--;
size_t bytes = c->bytes ();
if (c->redundant) {
assert (stats.current.redundant > 0);
stats.current.redundant--;
} else {
assert (stats.current.irredundant > 0);
stats.current.irredundant--;
assert (stats.irrlits >= c->size);
stats.irrlits -= c->size;
mark_removed (c);
}
stats.garbage.bytes += bytes;
stats.garbage.clauses++;
stats.garbage.literals += c->size;
c->garbage = true;
c->used = 0;
LOG (c, "marked garbage pointer %p", (void *) c);
}
void Internal::assign_original_unit (int64_t id, int lit) {
assert (!level || opts.chrono);
assert (!unsat);
const int idx = vidx (lit);
assert (!vals[idx]);
assert (!flags (idx).eliminated ());
Var &v = var (idx);
v.level = 0;
v.trail = (int) trail.size ();
v.reason = 0;
const signed char tmp = sign (lit);
set_val (idx, tmp);
trail.push_back (lit);
num_assigned++;
const unsigned uidx = vlit (lit);
if (lrat || frat)
unit_clauses (uidx) = id;
LOG ("original unit assign %d", lit);
assert (num_assigned == trail.size () || level);
mark_fixed (lit);
if (level)
return;
if (propagate ())
return;
assert (conflict);
LOG ("propagation of original unit results in conflict");
learn_empty_clause ();
}
void Internal::add_new_original_clause (int64_t id) {
if (!from_propagator && level && !opts.ilb) {
backtrack ();
} else if (changed_val) {
assert (val (changed_val));
int new_level = var (changed_val).level - 1;
assert (new_level >= 0);
backtrack (new_level);
}
assert (!changed_val);
LOG (original, "original clause");
assert (clause.empty ());
bool skip = false;
unordered_set<int> learned_levels;
size_t unassigned = 0;
newest_clause = 0;
if (unsat) {
LOG ("skipping clause since formula is already inconsistent");
skip = true;
} else {
assert (clause.empty ());
for (const auto &lit : original) {
int tmp = marked (lit);
if (tmp > 0) {
LOG ("removing duplicated literal %d", lit);
} else if (tmp < 0) {
LOG ("tautological since both %d and %d occur", -lit, lit);
skip = true;
} else {
mark (lit);
tmp = fixed (lit);
if (tmp < 0) {
LOG ("removing falsified literal %d", lit);
if (lrat) {
int elit = externalize (lit);
unsigned eidx = (elit > 0) + 2u * (unsigned) abs (elit);
if (!external->ext_units[eidx]) {
int64_t uid = unit_id (-lit);
lrat_chain.push_back (uid);
}
}
} else if (tmp > 0) {
LOG ("satisfied since literal %d true", lit);
skip = true;
} else {
clause.push_back (lit);
assert (flags (lit).status != Flags::UNUSED);
tmp = val (lit);
if (tmp)
learned_levels.insert (var (lit).level);
else
unassigned++;
}
}
}
for (const auto &lit : original)
unmark (lit);
}
if (skip) {
if (from_propagator) {
stats.ext_prop.elearn_conf++;
if (opts.check && is_external_forgettable (id))
mark_garbage_external_forgettable (id);
}
if (proof) {
proof->delete_external_original_clause (id, false, external->eclause);
}
} else {
int64_t new_id = id;
const size_t size = clause.size ();
if (original.size () > size) {
new_id = ++clause_id;
if (proof) {
if (lrat)
lrat_chain.push_back (id);
proof->add_derived_clause (new_id, false, clause, lrat_chain);
proof->delete_external_original_clause (id, false,
external->eclause);
}
external->check_learned_clause ();
if (from_propagator) {
if (opts.check && is_external_forgettable (id))
mark_garbage_external_forgettable (id);
}
}
external->eclause.clear ();
lrat_chain.clear ();
if (!size) {
if (from_propagator)
stats.ext_prop.elearn_conf++;
assert (!unsat);
if (!original.size ())
VERBOSE (1, "found empty original clause");
else
VERBOSE (1, "found falsified original clause");
unsat = true;
conflict_id = new_id;
marked_failed = true;
conclusion.push_back (new_id);
} else if (size == 1) {
if (force_no_backtrack) {
assert (level);
const int idx = vidx (clause[0]);
assert (val (clause[0]) >= 0);
assert (!flags (idx).eliminated ());
Var &v = var (idx);
assert (val (clause[0]));
v.level = 0;
v.reason = 0;
did_external_prop = true;
const unsigned uidx = vlit (clause[0]);
if (lrat || frat)
unit_clauses (uidx) = new_id;
mark_fixed (clause[0]);
} else {
const int lit = clause[0];
assert (!val (lit) || var (lit).level);
if (val (lit) < 0)
backtrack (var (lit).level - 1);
assert (val (lit) >= 0);
handle_external_clause (0);
assign_original_unit (new_id, lit);
}
} else {
move_literals_to_watch ();
#ifndef NDEBUG
check_watched_literal_invariants ();
#endif
int glue = (int) (learned_levels.size () + unassigned);
assert (glue <= (int) clause.size ());
bool clause_redundancy = from_propagator && ext_clause_forgettable;
Clause *c = new_clause (clause_redundancy, glue);
c->id = new_id;
clause_id--;
watch_clause (c);
clause.clear ();
original.clear ();
handle_external_clause (c);
newest_clause = c;
}
}
clause.clear ();
lrat_chain.clear ();
}
Clause *Internal::new_learned_redundant_clause (int glue) {
assert (clause.size () > 1);
#ifndef NDEBUG
for (size_t i = 2; i < clause.size (); i++)
assert (var (clause[0]).level >= var (clause[i]).level),
assert (var (clause[1]).level >= var (clause[i]).level);
#endif
external->check_learned_clause ();
Clause *res = new_clause (true, glue);
if (proof) {
proof->add_derived_clause (res, lrat_chain);
}
assert (watching ());
watch_clause (res);
return res;
}
Clause *Internal::new_hyper_binary_resolved_clause (bool red, int glue) {
external->check_learned_clause ();
Clause *res = new_clause (red, glue);
if (proof) {
proof->add_derived_clause (res, lrat_chain);
}
assert (watching ());
watch_clause (res);
return res;
}
Clause *Internal::new_hyper_ternary_resolved_clause (bool red) {
external->check_learned_clause ();
size_t size = clause.size ();
Clause *res = new_clause (red, size);
if (proof) {
proof->add_derived_clause (res, lrat_chain);
}
assert (!watching ());
return res;
}
Clause *Internal::new_factor_clause (int witness) {
external->check_learned_clause ();
stats.factor_added++;
stats.literals_factored += clause.size ();
Clause *res = new_clause (false, 0);
if (proof) {
if (witness)
proof->add_derived_rat_clause (res, externalize (witness),
lrat_chain);
else
proof->add_derived_clause (res, lrat_chain);
}
assert (!watching ());
assert (occurring ());
for (const auto &lit : *res) {
occs (lit).push_back (res);
}
return res;
}
Clause *
Internal::new_hyper_ternary_resolved_clause_and_watch (bool red,
bool full_watching) {
external->check_learned_clause ();
size_t size = clause.size ();
Clause *res = new_clause (red, size);
if (proof) {
proof->add_derived_clause (res, lrat_chain);
}
if (full_watching) {
assert (watching ());
watch_clause (res);
}
return res;
}
Clause *Internal::new_clause_as (const Clause *orig) {
external->check_learned_clause ();
const int new_glue = orig->glue;
Clause *res = new_clause (orig->redundant, new_glue);
if (proof) {
proof->add_derived_clause (res, lrat_chain);
}
assert (watching ());
watch_clause (res);
return res;
}
Clause *Internal::new_resolved_irredundant_clause () {
external->check_learned_clause ();
if (proof) {
proof->add_derived_clause (clause_id + 1, false, clause, lrat_chain);
}
Clause *res = new_clause (false);
assert (!watching ());
return res;
}
void Internal::decay_clauses_upon_incremental_clauses () {
if (!opts.incdecay)
return;
if (!stats.searches)
return;
if (stats.conflicts < lim.incremental_decay)
return;
PHASE ("decay", stats.incremental_decay,
"decaying clauses with next decaying at conflict %" PRId64
"(after the next incremental call)",
lim.incremental_decay);
for (auto c : clauses) {
if (c->garbage)
continue;
if (!c->redundant)
continue;
if (c->id >= last.incremental_decay.last_id)
continue;
switch (opts.incdecay) {
case 1: ++c->glue;
break;
case 2: if (c->glue < tier1[false])
c->used = 1;
break;
case 3:
if (c->glue < tier1[false])
c->used = 1;
++c->glue;
break;
default:
break;
}
}
lim.incremental_decay += stats.conflicts + opts.incdecayint;
++stats.incremental_decay;
last.incremental_decay.last_id = clause_id;
}
}