575 lines
14 KiB
C
Raw Normal View History

2022-08-30 15:42:35 +08:00
#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);
}