#include "allocate.h" #include "collect.h" #include "inline.h" #include "print.h" #include "reduce.h" #include "rank.h" #include "report.h" #include "trail.h" #include #include bool kissat_reducing (kissat * solver) { if (!GET_OPTION (reduce)) return false; if (!solver->statistics.clauses_redundant) return false; if (CONFLICTS < solver->limits.reduce.conflicts) return false; return true; } typedef struct reducible reducible; struct reducible { uint64_t rank; unsigned ref; }; #define RANK_REDUCIBLE(RED) \ (RED).rank // *INDENT-OFF* typedef STACK (reducible) reducibles; // *INDENT-ON* static bool collect_reducibles (kissat * solver, reducibles * reds, reference start_ref) { assert (start_ref != INVALID_REF); assert (start_ref <= SIZE_STACK (solver->arena)); const word *arena = BEGIN_STACK (solver->arena); clause *start = (clause *) (arena + start_ref); const clause *end = (clause *) END_STACK (solver->arena); assert (start < end); while (start != end && (!start->redundant || start->keep)) start = kissat_next_clause (start); if (start == end) { solver->first_reducible = INVALID_REF; LOG ("no reducible clause candidate left"); return false; } const reference redundant = (word *) start - arena; #ifdef LOGGING if (redundant < solver->first_reducible) LOG ("updating redundant clauses start from %zu to %zu", (size_t) solver->first_reducible, (size_t) redundant); else LOG ("no update to redundant clauses start %zu", (size_t) solver->first_reducible); #endif solver->first_reducible = redundant; const unsigned tier2 = GET_OPTION (tier2); #ifndef QUIET size_t flushed_hyper_ternary_clauses = 0; size_t used_hyper_ternary_clauses = 0; #endif for (clause * c = start; c != end; c = kissat_next_clause (c)) { if (!c->redundant) continue; if (c->garbage) continue; if (c->reason) continue; if (c->hyper) { assert (c->size == 3); if (c->used) { #ifndef QUIET used_hyper_ternary_clauses++; #endif c->used = false; } else { #ifndef QUIET flushed_hyper_ternary_clauses++; #endif kissat_mark_clause_as_garbage (solver, c); } continue; } if (c->keep) continue; if (c->used) { c->used--; if (c->glue <= tier2) continue; } assert (!c->garbage); assert (kissat_clause_in_arena (solver, c)); reducible red; const uint64_t negative_size = (1u << LD_MAX_VAR) - c->size; const uint64_t negative_glue = (1u << LD_MAX_GLUE) - c->glue; red.rank = negative_size | (negative_glue << LD_MAX_VAR); red.ref = (word *) c - arena; PUSH_STACK (*reds, red); } #ifndef QUIET // *INDENT-OFF* size_t total_hyper_ternary_clauses = flushed_hyper_ternary_clauses + used_hyper_ternary_clauses; if (flushed_hyper_ternary_clauses) kissat_phase (solver, "reduced", GET (reductions), "reduced %zu unused hyper ternary clauses %.0f%% out of %zu", flushed_hyper_ternary_clauses, kissat_percent (flushed_hyper_ternary_clauses, total_hyper_ternary_clauses), total_hyper_ternary_clauses); // *INDENT-ON* #endif if (EMPTY_STACK (*reds)) { LOG ("did not find any reducible redundant clause"); return false; } return true; } #define USEFULNESS RANK_REDUCIBLE #define RADIX_SORT_REDUCE_LENGTH 16 static void sort_reducibles (kissat * solver, reducibles * reds) { RADIX_STACK (RADIX_SORT_REDUCE_LENGTH, reducible, uint64_t, *reds, USEFULNESS); } static void mark_less_useful_clauses_as_garbage (kissat * solver, reducibles * reds) { const size_t size = SIZE_STACK (*reds); size_t target = size * (GET_OPTION (reducefraction) / 100.0); #ifndef QUIET statistics *statistics = &solver->statistics; const size_t clauses = statistics->clauses_irredundant + statistics->clauses_redundant; kissat_phase (solver, "reduce", GET (reductions), "reducing %zu (%.0f%%) out of %zu (%.0f%%) " "reducible clauses", target, kissat_percent (target, size), size, kissat_percent (size, clauses)); #endif unsigned reduced = 0; word *arena = BEGIN_STACK (solver->arena); const reducible *begin = BEGIN_STACK (*reds); const reducible *end = END_STACK (*reds); for (const reducible * p = begin; p != end && target--; p++) { clause *c = (clause *) (arena + p->ref); assert (kissat_clause_in_arena (solver, c)); assert (!c->garbage); assert (!c->keep); assert (!c->reason); assert (c->redundant); LOGCLS (c, "reducing"); kissat_mark_clause_as_garbage (solver, c); reduced++; } ADD (clauses_reduced, reduced); } static bool compacting (kissat * solver) { if (!GET_OPTION (compact)) return false; unsigned inactive = solver->vars - solver->active; unsigned limit = GET_OPTION (compactlim) / 1e2 * solver->vars; bool compact = (inactive > limit); LOG ("%u inactive variables %.0f%% <= limit %u %.0f%%", inactive, kissat_percent (inactive, solver->vars), limit, kissat_percent (limit, solver->vars)); return compact; } static void force_restart_before_reduction (kissat * solver) { if (!GET_OPTION (reducerestart)) return; if (!solver->stable && (GET_OPTION (reducerestart) < 2)) return; printf("\nbegin force res\n"); kissat_restart_and_flush_trail (solver); } int kissat_reduce (kissat * solver) { START (reduce); INC (reductions); LOG ("beginreduce"); kissat_phase (solver, "reduce", GET (reductions), "reduce limit %" PRIu64 " hit after %" PRIu64 " conflicts", solver->limits.reduce.conflicts, CONFLICTS); force_restart_before_reduction (solver); bool compact = compacting (solver); reference start = compact ? 0 : solver->first_reducible; if (start != INVALID_REF) { #ifndef QUIET size_t arena_size = SIZE_STACK (solver->arena); size_t words_to_sweep = arena_size - start; size_t bytes_to_sweep = sizeof (word) * words_to_sweep; kissat_phase (solver, "reduce", GET (reductions), "reducing clauses after offset %zu in arena", start); kissat_phase (solver, "reduce", GET (reductions), "sweeping %zu words %s %.0f%%", words_to_sweep, FORMAT_BYTES (bytes_to_sweep), kissat_percent (words_to_sweep, arena_size)); #endif if (kissat_flush_and_mark_reason_clauses (solver, start)) { reducibles reds; INIT_STACK (reds); if (collect_reducibles (solver, &reds, start)) { sort_reducibles (solver, &reds); mark_less_useful_clauses_as_garbage (solver, &reds); RELEASE_STACK (reds); kissat_sparse_collect (solver, compact, start); } else if (compact) kissat_sparse_collect (solver, compact, start); else kissat_unmark_reason_clauses (solver, start); } else assert (solver->inconsistent); } else kissat_phase (solver, "reduce", GET (reductions), "nothing to reduce"); UPDATE_CONFLICT_LIMIT (reduce, reductions, NDIVLOGN, false); REPORT (0, '-'); STOP (reduce); return solver->inconsistent ? 20 : 0; }