575 lines
14 KiB
C
575 lines
14 KiB
C
|
#include "allocate.h"
|
||
|
#include "dense.h"
|
||
|
#include "eliminate.h"
|
||
|
#include "inline.h"
|
||
|
#include "print.h"
|
||
|
#include "ternary.h"
|
||
|
#include "report.h"
|
||
|
#include "sort.h"
|
||
|
#include "terminate.h"
|
||
|
|
||
|
static void
|
||
|
connect_ternary_clauses (kissat * solver, value * ternary)
|
||
|
{
|
||
|
assert (!solver->level);
|
||
|
assert (!solver->watching);
|
||
|
size_t connected = 0;
|
||
|
value *values = solver->values;
|
||
|
for (all_clauses (c))
|
||
|
{
|
||
|
if (c->garbage)
|
||
|
continue;
|
||
|
if (c->size != 3)
|
||
|
continue;
|
||
|
const unsigned *lits = c->lits;
|
||
|
if (values[lits[0]])
|
||
|
continue;
|
||
|
if (values[lits[1]])
|
||
|
continue;
|
||
|
if (values[lits[2]])
|
||
|
continue;
|
||
|
ternary[lits[0]] = ternary[lits[1]] = ternary[lits[2]] = 1;
|
||
|
kissat_connect_clause (solver, c);
|
||
|
connected++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static unsigned
|
||
|
schedule_ternary (kissat * solver, value * ternary)
|
||
|
{
|
||
|
heap *schedule = &solver->schedule;
|
||
|
flags *flags = solver->flags;
|
||
|
|
||
|
assert (!schedule->size);
|
||
|
unsigned scheduled = 0;
|
||
|
|
||
|
for (all_variables (idx))
|
||
|
{
|
||
|
struct flags *f = flags + idx;
|
||
|
if (!f->active)
|
||
|
continue;
|
||
|
const unsigned lit = LIT (idx);
|
||
|
if (!ternary[lit])
|
||
|
continue;
|
||
|
const unsigned not_lit = NOT (lit);
|
||
|
if (!ternary[not_lit])
|
||
|
continue;
|
||
|
if (!schedule->size)
|
||
|
kissat_resize_heap (solver, schedule, solver->vars);
|
||
|
kissat_update_variable_score (solver, schedule, idx);
|
||
|
kissat_push_heap (solver, schedule, idx);
|
||
|
scheduled++;
|
||
|
}
|
||
|
|
||
|
if (!scheduled)
|
||
|
return 0;
|
||
|
|
||
|
kissat_phase (solver, "ternary", GET (hyper_ternary_phases),
|
||
|
"scheduled %u variables %.0f%%",
|
||
|
scheduled, kissat_percent (scheduled, solver->active));
|
||
|
|
||
|
return scheduled;
|
||
|
}
|
||
|
|
||
|
typedef struct tag tag;
|
||
|
|
||
|
struct tag
|
||
|
{
|
||
|
unsigned first:30;
|
||
|
bool redundant:1;
|
||
|
bool binary:1;
|
||
|
};
|
||
|
|
||
|
// *INDENT-OFF*
|
||
|
typedef STACK (tag) tags;
|
||
|
// *INDENT-ON*
|
||
|
|
||
|
static void
|
||
|
ternary_resolution (kissat * solver, tags * tags, references * garbage,
|
||
|
unsigned lit, clause * c, clause * d)
|
||
|
{
|
||
|
assert (c->size == 3);
|
||
|
assert (d->size == 3);
|
||
|
|
||
|
LOGCLS (c, "ternary 1st antecedent");
|
||
|
LOGCLS (d, "ternary 2nd antecedent");
|
||
|
|
||
|
INC (hyper_ternary_resolutions);
|
||
|
|
||
|
unsigned lits[4];
|
||
|
unsigned size = 0;
|
||
|
for (unsigned i = 0; i < 3; i++)
|
||
|
{
|
||
|
const unsigned other = c->lits[i];
|
||
|
if (other == lit)
|
||
|
continue;
|
||
|
lits[size++] = other;
|
||
|
}
|
||
|
assert (size == 2);
|
||
|
|
||
|
const unsigned not_lit = NOT (lit);
|
||
|
bool tautological = false;
|
||
|
for (unsigned i = 0; i < 3; i++)
|
||
|
{
|
||
|
const unsigned other = d->lits[i];
|
||
|
if (other == not_lit)
|
||
|
continue;
|
||
|
const unsigned not_other = NOT (other);
|
||
|
bool found = false;
|
||
|
for (unsigned j = 0; j < size; j++)
|
||
|
{
|
||
|
const unsigned prev = lits[j];
|
||
|
if (other == prev)
|
||
|
{
|
||
|
found = true;
|
||
|
break;
|
||
|
}
|
||
|
if (not_other == prev)
|
||
|
{
|
||
|
tautological = true;
|
||
|
#ifndef LOGGING
|
||
|
break;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
#ifndef LOGGING
|
||
|
if (tautological)
|
||
|
break;
|
||
|
#endif
|
||
|
if (found)
|
||
|
continue;
|
||
|
assert (size < 4);
|
||
|
lits[size++] = other;
|
||
|
}
|
||
|
|
||
|
if (tautological)
|
||
|
{
|
||
|
LOGLITS (size, lits, "tautological resolvent");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (size == 4)
|
||
|
{
|
||
|
LOGLITS (4, lits, "size 3 exceeding resolvent");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
assert (size == 2 || size == 3);
|
||
|
LOGLITS (size, lits, "ternary resolvent");
|
||
|
|
||
|
for (unsigned i = size - 1; i; i--)
|
||
|
PUSH_STACK (solver->delayed, lits[i]);
|
||
|
|
||
|
bool redundant;
|
||
|
if (size == 3)
|
||
|
redundant = true;
|
||
|
else
|
||
|
{
|
||
|
redundant = c->redundant && d->redundant;
|
||
|
const reference c_ref = kissat_reference_clause (solver, c);
|
||
|
const reference d_ref = kissat_reference_clause (solver, d);
|
||
|
PUSH_STACK (*garbage, c_ref);
|
||
|
PUSH_STACK (*garbage, d_ref);
|
||
|
assert (!c->garbage), assert (!d->garbage);
|
||
|
c->garbage = d->garbage = true;
|
||
|
}
|
||
|
|
||
|
tag tag;
|
||
|
tag.first = lits[0];
|
||
|
tag.redundant = redundant;
|
||
|
tag.binary = (size == 2);
|
||
|
|
||
|
PUSH_STACK (*tags, tag);
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
find_binary (kissat * solver, bool irredundant,
|
||
|
unsigned first, unsigned second)
|
||
|
{
|
||
|
if (WATCHES (first).size > WATCHES (second).size)
|
||
|
SWAP (unsigned, first, second);
|
||
|
watches *watches = &WATCHES (first);
|
||
|
const unsigned steps = kissat_cache_lines (watches->size, sizeof (watch));
|
||
|
ADD (hyper_ternary_steps, steps + 1);
|
||
|
for (all_binary_large_watches (watch, *watches))
|
||
|
{
|
||
|
if (!watch.type.binary)
|
||
|
continue;
|
||
|
if (irredundant && watch.binary.redundant)
|
||
|
continue;
|
||
|
const unsigned other = watch.binary.lit;
|
||
|
if (other == second)
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
find_ternary (kissat * solver, word * arena,
|
||
|
unsigned first, unsigned second, unsigned third)
|
||
|
{
|
||
|
if (WATCHES (second).size > WATCHES (third).size)
|
||
|
SWAP (unsigned, second, third);
|
||
|
if (WATCHES (first).size > WATCHES (second).size)
|
||
|
SWAP (unsigned, first, second);
|
||
|
watches *watches = &WATCHES (first);
|
||
|
const unsigned steps = kissat_cache_lines (watches->size, sizeof (watch));
|
||
|
ADD (hyper_ternary_steps, steps + 1);
|
||
|
for (all_binary_large_watches (watch, *watches))
|
||
|
{
|
||
|
if (watch.type.binary)
|
||
|
{
|
||
|
const unsigned other = watch.binary.lit;
|
||
|
if (other == second)
|
||
|
return true;
|
||
|
if (other == third)
|
||
|
return true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
const reference ref = watch.large.ref;
|
||
|
clause *d = (clause *) (arena + ref);
|
||
|
assert (kissat_clause_in_arena (solver, d));
|
||
|
assert (d->size == 3);
|
||
|
INC (hyper_ternary_steps);
|
||
|
|
||
|
const unsigned a = d->lits[0];
|
||
|
const unsigned b = d->lits[1];
|
||
|
const unsigned c = d->lits[2];
|
||
|
if (a == first)
|
||
|
{
|
||
|
if (b == second && c == third)
|
||
|
return true;
|
||
|
if (c == second && b == third)
|
||
|
return true;
|
||
|
}
|
||
|
else if (b == first)
|
||
|
{
|
||
|
if (a == second && c == third)
|
||
|
return true;
|
||
|
if (c == second && a == third)
|
||
|
return true;
|
||
|
}
|
||
|
else if (c == first)
|
||
|
{
|
||
|
if (a == second && b == third)
|
||
|
return true;
|
||
|
if (b == second && a == third)
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return find_binary (solver, false, second, third);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
update_ternary_schedule_literal (kissat * solver, heap * schedule,
|
||
|
bool reschedule, unsigned lit)
|
||
|
{
|
||
|
const unsigned idx = IDX (lit);
|
||
|
kissat_update_variable_score (solver, schedule, idx);
|
||
|
if (reschedule && !kissat_heap_contains (schedule, idx))
|
||
|
kissat_push_heap (solver, schedule, idx);
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
update_ternary_schedule_stack (kissat * solver,
|
||
|
bool reschedule, unsigneds * stack)
|
||
|
{
|
||
|
heap *schedule = &solver->schedule;
|
||
|
for (all_stack (unsigned, lit, *stack))
|
||
|
update_ternary_schedule_literal (solver, schedule, reschedule, lit);
|
||
|
}
|
||
|
|
||
|
static word *
|
||
|
add_ternary_resolvents (kissat * solver, tags * tags, uint64_t * resolved_ptr)
|
||
|
{
|
||
|
word *arena = BEGIN_STACK (solver->arena);
|
||
|
while (!EMPTY_STACK (*tags))
|
||
|
{
|
||
|
tag tag = POP_STACK (*tags);
|
||
|
const bool binary = tag.binary;
|
||
|
const bool redundant = tag.redundant;
|
||
|
const unsigned first = tag.first;
|
||
|
const unsigned second = POP_STACK (solver->delayed);
|
||
|
const unsigned third =
|
||
|
binary ? INVALID_LIT : POP_STACK (solver->delayed);
|
||
|
if (binary)
|
||
|
{
|
||
|
if (find_binary (solver, !redundant, first, second))
|
||
|
continue;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
assert (third != INVALID_LIT);
|
||
|
if (find_ternary (solver, arena, first, second, third))
|
||
|
continue;
|
||
|
}
|
||
|
assert (EMPTY_STACK (solver->clause.lits));
|
||
|
PUSH_STACK (solver->clause.lits, first);
|
||
|
PUSH_STACK (solver->clause.lits, second);
|
||
|
if (!binary)
|
||
|
PUSH_STACK (solver->clause.lits, third);
|
||
|
if (!redundant)
|
||
|
{
|
||
|
assert (binary);
|
||
|
(void) kissat_new_irredundant_clause (solver);
|
||
|
}
|
||
|
else if (binary)
|
||
|
{
|
||
|
(void) kissat_new_binary_clause (solver, true, first, second);
|
||
|
assert (arena == BEGIN_STACK (solver->arena));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
reference ref = kissat_new_redundant_clause (solver, 2);
|
||
|
clause *c = kissat_dereference_clause (solver, ref);
|
||
|
assert (c->redundant);
|
||
|
assert (c->size == 3);
|
||
|
INC (hyper_ternaries);
|
||
|
assert (!c->hyper);
|
||
|
c->hyper = true;
|
||
|
LOGCLS (c, "actually");
|
||
|
}
|
||
|
arena = BEGIN_STACK (solver->arena);
|
||
|
|
||
|
if (binary)
|
||
|
INC (hyper_ternary2_resolved);
|
||
|
else
|
||
|
INC (hyper_ternary3_resolved);
|
||
|
INC (hyper_ternary_resolved);
|
||
|
*resolved_ptr += 1;
|
||
|
|
||
|
update_ternary_schedule_stack (solver, !binary, &solver->clause.lits);
|
||
|
CLEAR_STACK (solver->clause.lits);
|
||
|
}
|
||
|
assert (EMPTY_STACK (solver->delayed));
|
||
|
|
||
|
return arena;
|
||
|
}
|
||
|
|
||
|
static void
|
||
|
remove_ternary_subsumed_clauses (kissat * solver, references * garbage)
|
||
|
{
|
||
|
size_t marked = 0;
|
||
|
for (all_stack (reference, ref, *garbage))
|
||
|
{
|
||
|
clause *c = kissat_dereference_clause (solver, ref);
|
||
|
assert (c->garbage);
|
||
|
c->garbage = false;
|
||
|
kissat_mark_clause_as_garbage (solver, c);
|
||
|
marked++;
|
||
|
}
|
||
|
LOG ("marked %zu clauses as garbage", marked);
|
||
|
CLEAR_STACK (*garbage);
|
||
|
(void) marked;
|
||
|
}
|
||
|
|
||
|
static word *
|
||
|
resolve_ternary_clauses (kissat * solver, word * arena, uint64_t steps_limit,
|
||
|
tags * tags, references * garbage, unsigned lit,
|
||
|
uint64_t * resolved_ptr)
|
||
|
{
|
||
|
assert (!solver->level);
|
||
|
LOG ("resolving ternary clauses on literal %s", LOGLIT (lit));
|
||
|
const unsigned not_lit = NOT (lit);
|
||
|
|
||
|
watches *pos_watches = &WATCHES (lit);
|
||
|
watches *neg_watches = &WATCHES (not_lit);
|
||
|
|
||
|
assert (EMPTY_STACK (*garbage));
|
||
|
assert (EMPTY_STACK (solver->delayed));
|
||
|
|
||
|
for (all_binary_large_watches (pos_watch, *pos_watches))
|
||
|
{
|
||
|
if (TERMINATED (13))
|
||
|
break;
|
||
|
if (solver->statistics.hyper_ternary_steps > steps_limit)
|
||
|
break;
|
||
|
if (pos_watch.type.binary)
|
||
|
continue;
|
||
|
const reference pos_ref = pos_watch.large.ref;
|
||
|
clause *c = (clause *) (arena + pos_ref);
|
||
|
assert (kissat_clause_in_arena (solver, c));
|
||
|
assert (c->size == 3);
|
||
|
INC (hyper_ternary_steps);
|
||
|
if (c->garbage)
|
||
|
continue;
|
||
|
for (all_binary_large_watches (neg_watch, *neg_watches))
|
||
|
{
|
||
|
if (neg_watch.type.binary)
|
||
|
continue;
|
||
|
const reference neg_ref = neg_watch.large.ref;
|
||
|
clause *d = (clause *) (arena + neg_ref);
|
||
|
assert (kissat_clause_in_arena (solver, d));
|
||
|
assert (d->size == 3);
|
||
|
assert (c != d);
|
||
|
INC (hyper_ternary_steps);
|
||
|
if (d->garbage)
|
||
|
continue;
|
||
|
ternary_resolution (solver, tags, garbage, lit, c, d);
|
||
|
if (c->garbage)
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
word *res = add_ternary_resolvents (solver, tags, resolved_ptr);
|
||
|
remove_ternary_subsumed_clauses (solver, garbage);
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
static bool
|
||
|
really_ternary (kissat * solver)
|
||
|
{
|
||
|
if (!GET_OPTION (really))
|
||
|
return true;
|
||
|
|
||
|
const uint64_t limit = 2 * CLAUSES;
|
||
|
statistics *statistics = &solver->statistics;
|
||
|
|
||
|
if (limit >= statistics->search_ticks + GET_OPTION (ternarymineff))
|
||
|
return false;
|
||
|
|
||
|
for (all_clauses (c))
|
||
|
if (!c->garbage && c->size == 3)
|
||
|
return true;
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static uint64_t
|
||
|
ternary_round (kissat * solver,
|
||
|
const uint64_t resolved_limit, const uint64_t steps_limit)
|
||
|
{
|
||
|
tags tags;
|
||
|
INIT_STACK (tags);
|
||
|
|
||
|
references garbage;
|
||
|
INIT_STACK (garbage);
|
||
|
|
||
|
word *arena = BEGIN_STACK (solver->arena);
|
||
|
heap *schedule = &solver->schedule;
|
||
|
|
||
|
#ifndef QUIET
|
||
|
size_t scheduled = kissat_size_heap (schedule);
|
||
|
#endif
|
||
|
uint64_t resolved = 0;
|
||
|
|
||
|
while (!kissat_empty_heap (schedule))
|
||
|
{
|
||
|
if (TERMINATED (14))
|
||
|
break;
|
||
|
|
||
|
if (solver->statistics.hyper_ternary_steps > steps_limit)
|
||
|
{
|
||
|
kissat_phase (solver, "ternary", GET (hyper_ternary_phases),
|
||
|
"steps limit %" PRIu64 " reached", steps_limit);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (resolved > resolved_limit)
|
||
|
{
|
||
|
kissat_phase (solver, "ternary", GET (hyper_ternary_phases),
|
||
|
"resolved limit %" PRIu64 " reached", resolved_limit);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
const unsigned idx = kissat_max_heap (schedule);
|
||
|
kissat_pop_heap (solver, schedule, idx);
|
||
|
assert (ACTIVE (idx));
|
||
|
|
||
|
const unsigned lit = LIT (idx);
|
||
|
arena = resolve_ternary_clauses (solver, arena, steps_limit,
|
||
|
&tags, &garbage, lit, &resolved);
|
||
|
}
|
||
|
|
||
|
#ifndef QUIET
|
||
|
size_t remain = kissat_size_heap (schedule);
|
||
|
if (remain)
|
||
|
kissat_phase (solver, "ternary", GET (hyper_ternary_phases),
|
||
|
"remaining %u variables %.0f%% (incomplete ternary round)",
|
||
|
remain, kissat_percent (remain, scheduled));
|
||
|
else
|
||
|
kissat_phase (solver, "ternary", GET (hyper_ternary_phases),
|
||
|
"all %u variables resolved (complete ternary round)",
|
||
|
scheduled);
|
||
|
#endif
|
||
|
|
||
|
RELEASE_STACK (tags);
|
||
|
RELEASE_STACK (garbage);
|
||
|
|
||
|
return resolved;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
kissat_ternary (kissat * solver)
|
||
|
{
|
||
|
if (solver->inconsistent)
|
||
|
return;
|
||
|
|
||
|
assert (!solver->level);
|
||
|
assert (solver->watching);
|
||
|
assert (solver->probing);
|
||
|
|
||
|
if (TERMINATED (15))
|
||
|
return;
|
||
|
if (!GET_OPTION (ternary))
|
||
|
return;
|
||
|
|
||
|
if (!really_ternary (solver))
|
||
|
return;
|
||
|
|
||
|
RETURN_IF_DELAYED (ternary);
|
||
|
|
||
|
START (ternary);
|
||
|
INC (hyper_ternary_phases);
|
||
|
|
||
|
#ifndef NMETRICS
|
||
|
uint64_t resolved2 = solver->statistics.hyper_ternary2_resolved;
|
||
|
uint64_t resolved3 = solver->statistics.hyper_ternary3_resolved;
|
||
|
#endif
|
||
|
|
||
|
kissat_enter_dense_mode (solver, 0, 0);
|
||
|
|
||
|
value *ternary = kissat_calloc (solver, LITS, sizeof *ternary);
|
||
|
connect_ternary_clauses (solver, ternary);
|
||
|
|
||
|
const double resolved_limit_fraction = GET_OPTION (ternarymaxadd) * 0.01;
|
||
|
const uint64_t resolved_limit = CLAUSES * resolved_limit_fraction;
|
||
|
|
||
|
const unsigned scheduled = schedule_ternary (solver, ternary);
|
||
|
uint64_t resolved = 0;
|
||
|
|
||
|
if (scheduled)
|
||
|
{
|
||
|
SET_EFFICIENCY_BOUND (steps_limit, ternary,
|
||
|
hyper_ternary_steps, search_ticks,
|
||
|
2 * CLAUSES + kissat_nlogn (scheduled));
|
||
|
|
||
|
resolved = ternary_round (solver, resolved_limit, steps_limit);
|
||
|
kissat_release_heap (solver, &solver->schedule);
|
||
|
}
|
||
|
|
||
|
kissat_dealloc (solver, ternary, LITS, sizeof *ternary);
|
||
|
kissat_resume_sparse_mode (solver, false, 0, 0);
|
||
|
|
||
|
#ifndef NMETRICS
|
||
|
resolved2 = solver->statistics.hyper_ternary2_resolved - resolved2;
|
||
|
resolved3 = solver->statistics.hyper_ternary3_resolved - resolved3;
|
||
|
kissat_phase (solver, "ternary", GET (hyper_ternary_phases),
|
||
|
"resolved %" PRIu64 " clauses (%" PRIu64
|
||
|
" ternary %.0f%% and %" PRIu64 " binary %.0f%%)",
|
||
|
resolved,
|
||
|
resolved3, kissat_percent (resolved3, resolved),
|
||
|
resolved2, kissat_percent (resolved2, resolved));
|
||
|
#else
|
||
|
kissat_phase (solver, "ternary", GET (hyper_ternary_phases),
|
||
|
"resolved %" PRIu64 " clauses", resolved);
|
||
|
#endif
|
||
|
UPDATE_DELAY (resolved, ternary);
|
||
|
REPORT (!resolved, '3');
|
||
|
kissat_check_statistics (solver);
|
||
|
STOP (ternary);
|
||
|
}
|