https://github.com/TakehideSoh/SAF
Tip revision: 3b45819df15225431050eeb48c1a31ffdd580af7 authored by Daniel Le Berre on 22 June 2023, 20:02:19 UTC
Typo in README
Typo in README
Tip revision: 3b45819
compact.cpp
#include "internal.hpp"
namespace CaDiCaL {
/*------------------------------------------------------------------------*/
// Compacting removes holes generated by inactive variables (fixed,
// eliminated, substituted or pure) by mapping active variables indices down
// to a contiguous interval of indices.
/*------------------------------------------------------------------------*/
bool Internal::compacting () {
if (level) return false;
if (!opts.compact) return false;
if (stats.conflicts < lim.compact) return false;
int inactive = max_var - active ();
assert (inactive >= 0);
if (!inactive) return false;
if (inactive < opts.compactmin) return false;
return inactive >= (1e-3 * opts.compactlim) * max_var;
}
/*------------------------------------------------------------------------*/
struct Mapper {
Internal * internal;
int new_max_var; // New 'max_var' after compacting.
int * table; // Old variable index to new literal map.
int first_fixed; // First fixed variable index.
int map_first_fixed; // Mapped literal of first fixed variable.
signed char first_fixed_val; // Value of first fixed variable.
size_t new_vsize;
/*----------------------------------------------------------------------*/
// We produce a compacting garbage collector like map of old 'src' to
// new 'dst' variables. Inactive variables are just skipped except for
// fixed ones which will be mapped to the first fixed variable (in the
// appropriate phase). This avoids to handle the case 'fixed value'
// separately as it is done in Lingeling, where fixed variables are
// mapped to the internal variable '1'.
//
Mapper (Internal * i) :
internal (i),
new_max_var (0),
first_fixed (0),
map_first_fixed (0),
first_fixed_val (0)
{
table = new int [ internal->max_var + 1u ];
clear_n (table, internal->max_var + 1u);
assert (!internal->level);
for (auto src : internal->vars) {
const Flags & f = internal->flags (src);
if (f.active ()) table[src] = ++new_max_var;
else if (f.fixed () && !first_fixed)
table[first_fixed = src] = map_first_fixed = ++new_max_var;
}
first_fixed_val = first_fixed ? internal->val (first_fixed) : 0;
new_vsize = new_max_var + 1u;
}
~Mapper () { delete [] table; }
/*----------------------------------------------------------------------*/
// Map old variable indices. A result of zero means not mapped.
//
int map_idx (int src) {
assert (0 < src);
assert (src <= internal->max_var);
const int res = table[src];
assert (res <= new_max_var);
return res;
}
/*----------------------------------------------------------------------*/
// The 'map_idx' above is just a look-up into the 'table'. Here we have
// to care about signedness of 'src', and in addition that fixed variables
// have all to be mapped to the first fixed variable 'first_fixed'.
//
int map_lit (int src) {
int res = map_idx (abs (src));
if (!res) {
const signed char tmp = internal->val (src);
if (tmp) {
assert (first_fixed);
res = map_first_fixed;
if (tmp != first_fixed_val) res = -res;
}
} else if ((src) < 0) res = -res;
assert (abs (res) <= new_max_var);
return res;
}
/*----------------------------------------------------------------------*/
// Map positive variable indices in vector.
//
template<class T>
void map_vector (vector<T> & v) {
for (auto src : internal->vars) {
const int dst = map_idx (src);
if (!dst) continue;
assert (0 < dst);
assert (dst <= src);
v[dst] = v[src];
}
v.resize (new_vsize);
shrink_vector (v);
}
/*----------------------------------------------------------------------*/
// Map positive and negative variable indices in two-sided vector.
//
template<class T>
void map2_vector (vector<T> & v) {
for (auto src : internal->vars) {
const int dst = map_idx (src);
if (!dst) continue;
assert (0 < dst);
assert (dst <= src);
v[2*dst] = v[2*src];
v[2*dst + 1] = v[2*src + 1];
}
v.resize (2*new_vsize);
shrink_vector (v);
}
/*----------------------------------------------------------------------*/
// Map a vector of literals, flush inactive literals, then resize and
// shrink it to fit the new size after flushing.
//
void map_flush_and_shrink_lits (vector<int> & v) {
const auto end = v.end ();
auto j = v.begin (), i = j;
for (; i != end; i++) {
const int src = *i;
int dst = map_idx (abs (src));
assert (abs (dst) <= abs (src));
if (!dst) continue;
if (src < 0) dst = -dst;
*j++ = dst;
}
v.resize (j - v.begin ());
shrink_vector (v);
}
};
/*------------------------------------------------------------------------*/
static signed char * ignore_clang_analyze_memory_leak_warning;
void Internal::compact () {
START (compact);
assert (active () < max_var);
stats.compacts++;
assert (!level);
assert (!unsat);
assert (!conflict);
assert (clause.empty ());
assert (levels.empty ());
assert (analyzed.empty ());
assert (minimized.empty ());
assert (control.size () == 1);
assert (propagated == trail.size ());
garbage_collection ();
Mapper mapper (this);
if (mapper.first_fixed)
LOG ("found first fixed %d",
sign (mapper.first_fixed_val)*mapper.first_fixed);
else LOG ("no variable fixed");
if (!assumptions.empty ()) {
assert (!external->assumptions.empty ());
LOG ("temporarily reset internal assumptions");
reset_assumptions ();
}
const bool is_constraint = !constraint.empty ();
if (is_constraint) {
assert (!external->constraint.empty ());
LOG ("temporarily reset internal constraint");
reset_constraint ();
}
/*======================================================================*/
// In this first part we only map stuff without reallocation / shrinking.
/*======================================================================*/
// Flush the external indices. This has to occur before we map 'vals'.
//
for (auto eidx : external->vars) {
int src = external->e2i[eidx];
if (!src) continue;
int dst = mapper.map_lit (src);
LOG ("compact %" PRId64 " maps external %d to internal %d from internal %d",
stats.compacts, eidx, dst, src);
external->e2i[eidx] = dst;
}
// Map the literals in all clauses.
//
for (const auto & c : clauses) {
assert (!c->garbage);
for (auto & src : *c) {
assert (!val (src));
int dst;
dst = mapper.map_lit (src);
assert (dst || c->garbage);
src = dst;
}
}
// Map the blocking literals in all watches.
//
if (!wtab.empty ())
for (auto lit : lits)
for (auto & w : watches (lit))
w.blit = mapper.map_lit (w.blit);
// We first flush inactive variables and map the links in the queue. This
// has to be done before we map the actual links data structure 'links'.
{
int prev = 0, mapped_prev = 0, next;
for (int idx = queue.first; idx; idx = next) {
next = links[idx].next;
if (idx == mapper.first_fixed) continue;
const int dst = mapper.map_idx (idx);
if (!dst) continue;
assert (active (idx));
if (prev) links[prev].next = dst; else queue.first = dst;
links[idx].prev = mapped_prev;
mapped_prev = dst;
prev = idx;
}
if (prev) links[prev].next = 0; else queue.first = 0;
queue.unassigned = queue.last = mapped_prev;
}
/*======================================================================*/
// In the second part we map, flush and shrink arrays.
/*======================================================================*/
mapper.map_flush_and_shrink_lits (trail);
propagated = trail.size ();
if (mapper.first_fixed) {
assert (trail.size () == 1);
var (mapper.first_fixed).trail = 0; // before mapping 'vtab'
} else assert (trail.empty ());
if (!probes.empty ())
mapper.map_flush_and_shrink_lits (probes);
/*======================================================================*/
// In the third part we map stuff and also reallocate memory.
/*======================================================================*/
// Now we continue in reverse order of allocated bytes, e.g., see
// 'Internal::enlarge' which reallocates in order of allocated bytes.
mapper.map_vector (ftab);
mapper.map_vector (parents);
mapper.map_vector (marks);
mapper.map_vector (phases.saved);
mapper.map_vector (phases.forced);
mapper.map_vector (phases.target);
mapper.map_vector (phases.best);
mapper.map_vector (phases.prev);
mapper.map_vector (phases.min);
// Special code for 'frozentab'.
//
for (auto src : vars) {
const int dst = mapper.map_idx (src);
if (!dst) continue;
if (src == dst) continue;
assert (dst < src);
frozentab[dst] += frozentab[src];
frozentab[src] = 0;
}
frozentab.resize (mapper.new_vsize);
shrink_vector (frozentab);
/*----------------------------------------------------------------------*/
if (!external->assumptions.empty ()) {
for (const auto & elit : external->assumptions) {
assert (elit);
assert (elit != INT_MIN);
int eidx = abs (elit);
assert (eidx <= external->max_var);
int ilit = external->e2i[eidx];
assert (ilit); // Because we froze all!!!
if (elit < 0) ilit = -ilit;
assume (ilit);
}
PHASE ("compact", stats.compacts,
"reassumed %zd external assumptions",
external->assumptions.size ());
}
// Special case for 'val' as for 'val' we trade branch less code for
// memory and always allocated an [-maxvar,...,maxvar] array.
{
signed char * new_vals = new signed char [ 2*mapper.new_vsize ];
ignore_clang_analyze_memory_leak_warning = new_vals;
new_vals += mapper.new_vsize;
for (auto src : vars)
new_vals[-mapper.map_idx (src)] = vals[-src];
for (auto src : vars)
new_vals[mapper.map_idx (src)] = vals[src];
new_vals[0] = 0;
vals -= vsize;
delete [] vals;
vals = new_vals;
}
// 'constrain' uses 'val', so this code has to be after remapping that
if (is_constraint) {
assert (!level);
assert (!external->constraint.back ());
for (auto elit : external->constraint){
assert (elit != INT_MIN);
int eidx = abs (elit);
assert (eidx <= external->max_var);
int ilit = external->e2i[eidx];
assert (!ilit == !elit);
if (elit < 0)
ilit = -ilit;
LOG ("re adding lit extrenal %d internal %d to constraint", elit, ilit);
constrain (ilit);
}
PHASE ("compact", stats.compacts, "added %zd external literals to constraint",
external->constraint.size () - 1);
}
mapper.map_vector (i2e);
mapper.map2_vector (ptab);
mapper.map_vector (btab);
mapper.map_vector (gtab);
mapper.map_vector (links);
mapper.map_vector (vtab);
if (!ntab.empty ()) mapper.map2_vector (ntab);
if (!wtab.empty ()) mapper.map2_vector (wtab);
if (!otab.empty ()) mapper.map2_vector (otab);
if (!big.empty ()) mapper.map2_vector (big);
/*======================================================================*/
// In the fourth part we map the binary heap for scores.
/*======================================================================*/
// The simplest way to map a binary heap is to get all elements from the
// heap and reinsert them. This could be slightly improved in terms of
// speed if we add a 'flush (int * map)' function to 'Heap', but that is
// pretty complicated and would require that the 'Heap' knows that mapped
// elements with 'zero' destination should be flushed.
vector<int> saved;
assert (saved.empty ());
if (!scores.empty ()) {
while (!scores.empty ()) {
const int src = scores.front ();
scores.pop_front ();
const int dst = mapper.map_idx (src);
if (!dst) continue;
if (src == mapper.first_fixed) continue;
saved.push_back (dst);
}
scores.erase ();
}
mapper.map_vector (stab);
if (!saved.empty ()) {
for (const auto idx : saved)
scores.push_back (idx);
scores.shrink ();
}
/*----------------------------------------------------------------------*/
PHASE ("compact", stats.compacts,
"reducing internal variables from %d to %d",
max_var, mapper.new_max_var);
/*----------------------------------------------------------------------*/
// Need to adjust the target and best assigned counters too.
size_t new_target_assigned = 0, new_best_assigned = 0;
for (auto idx : Range (mapper.new_max_var)) {
if (phases.target[idx]) new_target_assigned++;
if (phases.best[idx]) new_best_assigned++;
}
LOG ("reset target assigned from %zd to %zd",
target_assigned, new_target_assigned);
LOG ("reset best assigned from %zd to %zd",
best_assigned, new_best_assigned);
target_assigned = new_target_assigned;
best_assigned = new_best_assigned;
no_conflict_until = 0;
INIT_EMA (averages.current.trail.fast, opts.ematrailfast);
INIT_EMA (averages.current.trail.slow, opts.ematrailslow);
/*----------------------------------------------------------------------*/
max_var = mapper.new_max_var;
vsize = mapper.new_vsize;
stats.unused = 0;
stats.inactive = stats.now.fixed = mapper.first_fixed ? 1 : 0;
stats.now.substituted = stats.now.eliminated = stats.now.pure = 0;
check_var_stats ();
int64_t delta = opts.compactint * (stats.compacts + 1);
lim.compact = stats.conflicts + delta;
PHASE ("compact", stats.compacts,
"new compact limit %" PRId64 " after %" PRId64 " conflicts",
lim.compact, delta);
STOP (compact);
}
}
