274 lines
7.4 KiB
C
274 lines
7.4 KiB
C
#include "analyze.h"
|
|
#include "backtrack.h"
|
|
#include "bump.h"
|
|
#include "deduce.h"
|
|
#include "inline.h"
|
|
#include "learn.h"
|
|
#include "minimize.h"
|
|
|
|
#include <inttypes.h>
|
|
|
|
#define INVALID_LEVEL UINT_MAX
|
|
|
|
static bool
|
|
one_literal_on_conflict_level(kissat *solver,
|
|
clause *conflict,
|
|
unsigned *conflict_level_ptr)
|
|
{
|
|
assert(conflict);
|
|
assert(conflict->size > 1);
|
|
|
|
unsigned conflict_level = INVALID_LEVEL;
|
|
unsigned literals_on_conflict_level = 0;
|
|
unsigned forced_lit = INVALID_LIT;
|
|
|
|
assigned *all_assigned = solver->assigned;
|
|
|
|
unsigned *lits = conflict->lits;
|
|
const unsigned conflict_size = conflict->size;
|
|
const unsigned *end_of_lits = lits + conflict_size;
|
|
|
|
for (const unsigned *p = lits; p != end_of_lits; p++)
|
|
{
|
|
const unsigned lit = *p;
|
|
assert(VALUE(lit) < 0);
|
|
const unsigned idx = IDX(lit);
|
|
const unsigned level = all_assigned[idx].level;
|
|
if (conflict_level == level)
|
|
{
|
|
if (++literals_on_conflict_level > 1 && level == solver->level)
|
|
break;
|
|
}
|
|
else if (conflict_level == INVALID_LEVEL || conflict_level < level)
|
|
{
|
|
forced_lit = lit;
|
|
conflict_level = level;
|
|
literals_on_conflict_level = 1;
|
|
}
|
|
}
|
|
assert(conflict_level != INVALID_LEVEL);
|
|
assert(literals_on_conflict_level);
|
|
|
|
LOG("found %u literals on conflict level %u",
|
|
literals_on_conflict_level, conflict_level);
|
|
*conflict_level_ptr = conflict_level;
|
|
|
|
if (!conflict_level)
|
|
{
|
|
solver->inconsistent = true;
|
|
LOG("learned empty clause from conflict at conflict level zero");
|
|
CHECK_AND_ADD_EMPTY();
|
|
ADD_EMPTY_TO_PROOF();
|
|
return false;
|
|
}
|
|
|
|
if (conflict_level < solver->level)
|
|
{
|
|
LOG("forced backtracking due to conflict level %u < level %u",
|
|
conflict_level, solver->level);
|
|
kissat_backtrack(solver, conflict_level);
|
|
}
|
|
|
|
if (conflict_size > 2)
|
|
{
|
|
for (unsigned i = 0; i < 2; i++)
|
|
{
|
|
const unsigned lit = lits[i];
|
|
const unsigned lit_idx = IDX(lit);
|
|
unsigned highest_position = i;
|
|
unsigned highest_literal = lit;
|
|
unsigned highest_level = all_assigned[lit_idx].level;
|
|
for (unsigned j = i + 1; j < conflict_size; j++)
|
|
{
|
|
const unsigned other = lits[j];
|
|
const unsigned other_idx = IDX(other);
|
|
const unsigned level = all_assigned[other_idx].level;
|
|
if (highest_level >= level)
|
|
continue;
|
|
highest_literal = other;
|
|
highest_position = j;
|
|
highest_level = level;
|
|
if (highest_level == conflict_level)
|
|
break;
|
|
}
|
|
if (highest_position == i)
|
|
continue;
|
|
reference ref = INVALID_REF;
|
|
if (highest_position > 1)
|
|
{
|
|
ref = kissat_reference_clause(solver, conflict);
|
|
kissat_unwatch_blocking(solver, lit, ref);
|
|
}
|
|
lits[highest_position] = lit;
|
|
lits[i] = highest_literal;
|
|
if (highest_position > 1)
|
|
kissat_watch_blocking(solver, lits[i], lits[!i], ref);
|
|
}
|
|
}
|
|
|
|
if (literals_on_conflict_level > 1)
|
|
return false;
|
|
|
|
assert(literals_on_conflict_level == 1);
|
|
assert(forced_lit != INVALID_LIT);
|
|
|
|
LOG("reusing conflict as driving clause of %s", LOGLIT(forced_lit));
|
|
kissat_backtrack(solver, solver->level - 1);
|
|
if (conflict_size == 2)
|
|
{
|
|
assert(conflict == &solver->conflict);
|
|
const unsigned other = lits[0] ^ lits[1] ^ forced_lit;
|
|
kissat_assign_binary(solver, conflict->redundant, forced_lit, other);
|
|
}
|
|
else
|
|
{
|
|
const reference ref = kissat_reference_clause(solver, conflict);
|
|
kissat_assign_reference(solver, forced_lit, ref, conflict);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
mark_literal_as_analyzed(kissat *solver, assigned *all_assigned,
|
|
unsigned lit, const char *type)
|
|
{
|
|
const unsigned idx = IDX(lit);
|
|
assigned *a = all_assigned + idx;
|
|
if (a->analyzed == ANALYZED)
|
|
return;
|
|
a->analyzed = ANALYZED;
|
|
LOG("marking %s literal %s as analyzed", type, LOGLIT(lit));
|
|
PUSH_STACK(solver->analyzed, idx);
|
|
(void)type;
|
|
}
|
|
|
|
static inline void
|
|
analyze_reason_side_literals(kissat *solver)
|
|
{
|
|
assert(!solver->probing);
|
|
if (!GET_OPTION(bumpreasons))
|
|
return;
|
|
assigned *all_assigned = solver->assigned;
|
|
for (all_stack(unsigned, lit, solver->clause.lits))
|
|
all_assigned[IDX(lit)].analyzed = ANALYZED;
|
|
word *arena = BEGIN_STACK(solver->arena);
|
|
for (all_stack(unsigned, lit, solver->clause.lits))
|
|
{
|
|
const unsigned idx = IDX(lit);
|
|
assigned *a = all_assigned + idx;
|
|
assert(a->level > 0);
|
|
assert(a->reason != UNIT);
|
|
if (a->reason == DECISION)
|
|
continue;
|
|
if (a->binary)
|
|
mark_literal_as_analyzed(solver, all_assigned, a->reason,
|
|
"reason side");
|
|
else
|
|
{
|
|
assert(a->reason < SIZE_STACK(solver->arena));
|
|
clause *c = (clause *)(arena + a->reason);
|
|
solver->dps_ticks++;
|
|
for (all_literals_in_clause(lit, c))
|
|
if (IDX(lit) != idx)
|
|
mark_literal_as_analyzed(solver, all_assigned, lit,
|
|
"reason side");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
reset_levels(kissat *solver)
|
|
{
|
|
LOG("reset %zu marked levels", SIZE_STACK(solver->levels));
|
|
frame *frames = BEGIN_STACK(solver->frames);
|
|
for (all_stack(unsigned, level, solver->levels))
|
|
{
|
|
assert(level < SIZE_STACK(solver->frames));
|
|
frame *f = frames + level;
|
|
assert(f->used > 0);
|
|
f->used = 0;
|
|
}
|
|
CLEAR_STACK(solver->levels);
|
|
}
|
|
|
|
static void
|
|
reset_markings(kissat *solver)
|
|
{
|
|
LOG("unmarking %zu analyzed variables", SIZE_STACK(solver->analyzed));
|
|
assigned *all_assigned = solver->assigned;
|
|
for (all_stack(unsigned, idx, solver->analyzed))
|
|
all_assigned[idx].analyzed = 0;
|
|
}
|
|
|
|
static void
|
|
reset_analyze(kissat *solver)
|
|
{
|
|
LOG("reset %zu analyzed variables", SIZE_STACK(solver->analyzed));
|
|
CLEAR_STACK(solver->analyzed);
|
|
|
|
LOG("reset %zu learned literals", SIZE_STACK(solver->clause.lits));
|
|
CLEAR_STACK(solver->clause.lits);
|
|
}
|
|
|
|
static void
|
|
update_trail_average(kissat *solver)
|
|
{
|
|
assert(!solver->probing);
|
|
const unsigned size = SIZE_STACK(solver->trail);
|
|
const unsigned assigned = size - solver->unflushed;
|
|
const unsigned active = solver->active;
|
|
const double filled = kissat_percent(assigned, active);
|
|
LOG("trail filled %.0f%% (size %u, unflushed %u, active %u)",
|
|
filled, size, solver->unflushed, active);
|
|
UPDATE(trail, filled);
|
|
}
|
|
|
|
int kissat_analyze(kissat *solver, clause *conflict)
|
|
{
|
|
assert(!solver->inconsistent);
|
|
START(analyze);
|
|
if (!solver->probing)
|
|
{
|
|
update_trail_average(solver);
|
|
UPDATE(level, solver->level);
|
|
}
|
|
int res;
|
|
do
|
|
{
|
|
LOGCLS(conflict, "analyzing conflict %" PRIu64, CONFLICTS);
|
|
unsigned conflict_level;
|
|
if (one_literal_on_conflict_level(solver, conflict, &conflict_level))
|
|
res = 1;
|
|
else if (!conflict_level)
|
|
res = -1;
|
|
else if ((conflict = kissat_deduce_first_uip_clause(solver, conflict)))
|
|
{
|
|
reset_markings(solver);
|
|
reset_analyze(solver);
|
|
reset_levels(solver);
|
|
res = 0;
|
|
}
|
|
else
|
|
{
|
|
kissat_minimize_clause(solver);
|
|
if (!solver->probing)
|
|
analyze_reason_side_literals(solver);
|
|
reset_markings(solver);
|
|
kissat_learn_clause(solver);
|
|
solver->nconflict++;
|
|
// MAB
|
|
if (!solver->probing && (!solver->stable || solver->heuristic == 0))
|
|
kissat_bump_variables(solver);
|
|
if (!solver->probing && solver->stable && (solver->heuristic == 1))
|
|
kissat_update_conflicted_chb(solver);
|
|
|
|
reset_analyze(solver);
|
|
reset_levels(solver);
|
|
res = 1;
|
|
}
|
|
} while (!res);
|
|
STOP(analyze);
|
|
return res > 0 ? 0 : 20;
|
|
}
|