mirror of
https://github.com/Z3Prover/z3
synced 2025-04-08 10:25:18 +00:00
add anf and aig simplifier modules, cut-set enumeration, aig_finder, hoist out xor_finder from ba_solver
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
12e727e49a
commit
d27a949ae9
|
@ -310,6 +310,7 @@ namespace dd {
|
|||
unsigned var() const { return m.var(root); }
|
||||
rational const& val() const { SASSERT(is_val()); return m.val(root); }
|
||||
bool is_val() const { return m.is_val(root); }
|
||||
bool is_one() const { return m.is_one(root); }
|
||||
bool is_zero() const { return m.is_zero(root); }
|
||||
bool is_linear() const { return m.is_linear(root); }
|
||||
bool is_unary() const { return !is_val() && lo().is_zero() && hi().is_val(); }
|
||||
|
|
|
@ -2,6 +2,8 @@ z3_add_component(sat
|
|||
SOURCES
|
||||
ba_solver.cpp
|
||||
dimacs.cpp
|
||||
sat_aig_finder.cpp
|
||||
sat_aig_simplifier.cpp
|
||||
sat_anf_simplifier.cpp
|
||||
sat_asymm_branch.cpp
|
||||
sat_big.cpp
|
||||
|
@ -11,6 +13,7 @@ z3_add_component(sat
|
|||
sat_clause_use_list.cpp
|
||||
sat_cleaner.cpp
|
||||
sat_config.cpp
|
||||
sat_cutset.cpp
|
||||
sat_ddfw.cpp
|
||||
sat_drat.cpp
|
||||
sat_elim_eqs.cpp
|
||||
|
@ -29,7 +32,7 @@ z3_add_component(sat
|
|||
sat_solver.cpp
|
||||
sat_unit_walk.cpp
|
||||
sat_watched.cpp
|
||||
sat_xor_util.cpp
|
||||
sat_xor_finder.cpp
|
||||
COMPONENT_DEPENDENCIES
|
||||
util
|
||||
dd
|
||||
|
|
|
@ -22,7 +22,7 @@ Revision History:
|
|||
#include "sat/sat_types.h"
|
||||
#include "util/mpz.h"
|
||||
#include "sat/sat_simplifier_params.hpp"
|
||||
#include "sat/sat_xor_util.h"
|
||||
#include "sat/sat_xor_finder.h"
|
||||
|
||||
|
||||
namespace sat {
|
||||
|
@ -2911,9 +2911,9 @@ namespace sat {
|
|||
void ba_solver::pre_simplify() {
|
||||
VERIFY(s().at_base_lvl());
|
||||
m_constraint_removed = false;
|
||||
xor_util xu(s());
|
||||
for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) pre_simplify(xu, *m_constraints[i]);
|
||||
for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) pre_simplify(xu, *m_learned[i]);
|
||||
xor_finder xf(s());
|
||||
for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) pre_simplify(xf, *m_constraints[i]);
|
||||
for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) pre_simplify(xf, *m_learned[i]);
|
||||
bool change = m_constraint_removed;
|
||||
cleanup_constraints();
|
||||
if (change) {
|
||||
|
@ -2924,8 +2924,8 @@ namespace sat {
|
|||
}
|
||||
}
|
||||
|
||||
void ba_solver::pre_simplify(xor_util& xu, constraint& c) {
|
||||
if (c.is_xr() && c.size() <= xu.max_xor_size()) {
|
||||
void ba_solver::pre_simplify(xor_finder& xf, constraint& c) {
|
||||
if (c.is_xr() && c.size() <= xf.max_xor_size()) {
|
||||
unsigned sz = c.size();
|
||||
literal_vector lits;
|
||||
bool parity = false;
|
||||
|
@ -2936,7 +2936,7 @@ namespace sat {
|
|||
|
||||
// IF_VERBOSE(0, verbose_stream() << "blast: " << c << "\n");
|
||||
for (unsigned i = 0; i < (1ul << sz); ++i) {
|
||||
if (xu.parity(sz, i) == parity) {
|
||||
if (xf.parity(sz, i) == parity) {
|
||||
lits.reset();
|
||||
for (unsigned j = 0; j < sz; ++j) {
|
||||
lits.push_back(literal(x[j].var(), (0 != (i & (1 << j)))));
|
||||
|
@ -3799,11 +3799,11 @@ namespace sat {
|
|||
}
|
||||
|
||||
void ba_solver::extract_xor() {
|
||||
xor_util xu(s());
|
||||
std::function<void (literal_vector const&, bool)> f = [this](literal_vector const& l, bool b) { add_xr(l,b); };
|
||||
xu.set(f);
|
||||
xu.extract_xors();
|
||||
for (clause* cp : xu.removed_clauses()) {
|
||||
xor_finder xf(s());
|
||||
std::function<void (literal_vector const&)> f = [this](literal_vector const& l) { add_xr(l, false); };
|
||||
xf.set(f);
|
||||
xf.extract_xors(s().m_clauses);
|
||||
for (clause* cp : xf.removed_clauses()) {
|
||||
cp->set_removed(true);
|
||||
m_clause_removed = true;
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ Revision History:
|
|||
|
||||
namespace sat {
|
||||
|
||||
class xor_util;
|
||||
class xor_finder;
|
||||
|
||||
class ba_solver : public extension {
|
||||
|
||||
|
@ -347,7 +347,7 @@ namespace sat {
|
|||
lbool add_assign(constraint& c, literal l);
|
||||
bool incremental_mode() const;
|
||||
void simplify(constraint& c);
|
||||
void pre_simplify(xor_util& xu, constraint& c);
|
||||
void pre_simplify(xor_finder& xu, constraint& c);
|
||||
void nullify_tracking_literal(constraint& c);
|
||||
void set_conflict(constraint& c, literal lit);
|
||||
void assign(constraint& c, literal lit);
|
||||
|
|
153
src/sat/sat_aig_finder.cpp
Normal file
153
src/sat/sat_aig_finder.cpp
Normal file
|
@ -0,0 +1,153 @@
|
|||
/*++
|
||||
Copyright (c) 2020 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
sat_aig_finder.cpp
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner 2020-01-02
|
||||
|
||||
Notes:
|
||||
|
||||
AIG finder
|
||||
|
||||
--*/
|
||||
|
||||
#pragma once;
|
||||
|
||||
#include "sat/sat_aig_finder.h"
|
||||
|
||||
namespace sat {
|
||||
|
||||
void aig_finder::operator()(clause_vector const& clauses) {
|
||||
m_big.init(s, true);
|
||||
|
||||
for (clause* cp : clauses) {
|
||||
clause& c = *cp;
|
||||
if (c.size() <= 2) continue;
|
||||
if (find_aig(c)) continue;
|
||||
if (find_if(c)) continue;
|
||||
}
|
||||
}
|
||||
|
||||
bool aig_finder::implies(literal a, literal b) {
|
||||
if (m_big.connected(a, b))
|
||||
return true;
|
||||
for (auto const& w : s.get_wlist(a)) {
|
||||
if (w.is_binary_clause() && b == w.get_literal())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// a = ~b & ~c
|
||||
// if (~a | ~b) (~a | ~c), (b | c | a)
|
||||
|
||||
bool aig_finder::find_aig(clause& c) {
|
||||
bool is_aig = false;
|
||||
for (literal head : c) {
|
||||
is_aig = true;
|
||||
for (literal tail : c) {
|
||||
if (head == tail)
|
||||
continue;
|
||||
if (!implies(head, ~tail)) {
|
||||
is_aig = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (is_aig) {
|
||||
m_ands.reset();
|
||||
for (literal tail : c)
|
||||
if (tail != head)
|
||||
m_ands.push_back(~tail);
|
||||
m_aig_def(head, m_ands, c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return is_aig;
|
||||
}
|
||||
|
||||
//
|
||||
// x = if y then z else u
|
||||
// if x, y -> z
|
||||
// x, ~y -> u
|
||||
// y, z -> x
|
||||
// ~y, u -> x
|
||||
//
|
||||
|
||||
bool aig_finder::find_if(clause& c) {
|
||||
return false;
|
||||
#if 0
|
||||
if (c.size() != 3) return false;
|
||||
|
||||
literal x = c[0], y = c[1], z = c[2];
|
||||
if (find_if(~x, ~y, z, c)) return true;
|
||||
if (find_if(~x, ~z, y, c)) return true;
|
||||
if (find_if(~y, ~x, z, c)) return true;
|
||||
if (find_if(~y, ~z, x, c)) return true;
|
||||
if (find_if(~z, ~x, y, c)) return true;
|
||||
if (find_if(~z, ~y, x, c)) return true;
|
||||
return false;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
||||
// x, y -> z
|
||||
// x, ~y -> u
|
||||
// y, z -> x
|
||||
// ~y, u -> x
|
||||
|
||||
// x + yz + (1 + y)u = 0
|
||||
|
||||
bool aig_finder::check_if(literal x, literal y, literal z, clause& c) {
|
||||
clause* c2 = find_clause(~y, ~z, x);
|
||||
if (!c2) {
|
||||
return false;
|
||||
}
|
||||
for (clause* c3 : ternay_clauses_with(~x, y)) {
|
||||
literal u = third_literal(~x, y, *c3);
|
||||
clause* c4 = find_clause(y, ~u, x);
|
||||
if (c4) {
|
||||
m_if_def(x, y, z, u, c, *c2, *c3, *c4);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
literal aig_finder::third_literal(literal a, literal b, clause const& c) {
|
||||
for (literal lit : c)
|
||||
if (lit != a && lit != b)
|
||||
return lit;
|
||||
return null_literal;
|
||||
}
|
||||
|
||||
clause* aig_finder::find_clause(literal a, literal b, literal c) {
|
||||
for (auto const& w : s.get_wlist(~a)) {
|
||||
if (w.is_ternary() &&
|
||||
(b == w.get_literal1() && c == w.get_literal2()) ||
|
||||
(c == w.get_literal1() && b == w.get_literal2())) {
|
||||
for (clause* cp : s.clauses()) {
|
||||
clause& cl = *cp;
|
||||
|
||||
#define pair_eq(a, b, x, y) ((a == x && b == y) || (a == y && b == x))
|
||||
#define tern_eq(a, b, c, cl) \
|
||||
cl.size() == 3 && \
|
||||
((cl[0] == a && pair_eq(b, c, c1[1], c1[2])) || \
|
||||
(cl[0] == b && pair_eq(a, c, cl[1], cl[2])) || \
|
||||
(cl[0] == c && pair_eq(a, b, cl[1], cl[2]))))
|
||||
|
||||
if (tern_eq(a, b, c, *cp)) return cp;
|
||||
}
|
||||
}
|
||||
if (w.is_clause() && tern_eq(a, b, c, s.get_clause(w)))
|
||||
return &s.get_clause(w);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
52
src/sat/sat_aig_finder.h
Normal file
52
src/sat/sat_aig_finder.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*++
|
||||
Copyright (c) 2020 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
sat_aig_finder.h
|
||||
|
||||
Abstract:
|
||||
|
||||
extract AIG definitions from clauses.
|
||||
An example AIG clause is:
|
||||
head \/ l1 \/ l2
|
||||
such that
|
||||
~l1 /\ ~l2 => head
|
||||
head => ~l1, head => ~l2
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner 2020-01-02
|
||||
|
||||
Notes:
|
||||
|
||||
AIG finder
|
||||
--*/
|
||||
|
||||
#pragma once;
|
||||
|
||||
#include "util/params.h"
|
||||
#include "util/statistics.h"
|
||||
#include "sat/sat_clause.h"
|
||||
#include "sat/sat_types.h"
|
||||
#include "sat/sat_solver.h"
|
||||
#include "sat/sat_big.h"
|
||||
|
||||
namespace sat {
|
||||
|
||||
class aig_finder {
|
||||
solver& s;
|
||||
big m_big;
|
||||
literal_vector m_ands;
|
||||
std::function<void (literal head, literal_vector const& ands, clause& orig)> m_aig_def;
|
||||
bool implies(literal a, literal b);
|
||||
bool find_aig(clause& c);
|
||||
bool find_if(clause& c);
|
||||
|
||||
public:
|
||||
aig_finder(solver& s) : s(s), m_big(s.rand()) {}
|
||||
~aig_finder() {}
|
||||
void set(std::function<void (literal head, literal_vector const& ands, clause& orig)>& f) { m_aig_def = f; }
|
||||
void operator()(clause_vector const& clauses);
|
||||
};
|
||||
}
|
|
@ -17,128 +17,195 @@
|
|||
|
||||
#include "sat/sat_anf_simplifier.h"
|
||||
#include "sat/sat_solver.h"
|
||||
#include "sat/sat_xor_util.h"
|
||||
#include "sat/sat_xor_finder.h"
|
||||
#include "sat/sat_aig_finder.h"
|
||||
#include "math/grobner/pdd_solver.h"
|
||||
|
||||
namespace sat {
|
||||
|
||||
|
||||
class pdd_solver : public dd::solver {
|
||||
public:
|
||||
pdd_solver(reslimit& lim, dd::pdd_manager& m): dd::solver(lim, m) {}
|
||||
struct anf_simplifier::report {
|
||||
anf_simplifier& s;
|
||||
stopwatch m_watch;
|
||||
report(anf_simplifier& s): s(s) { m_watch.start(); }
|
||||
~report() {
|
||||
m_watch.stop();
|
||||
IF_VERBOSE(2,
|
||||
verbose_stream() << " (sat.anf.simplifier "
|
||||
<< " :num-units " << s.m_stats.m_num_units
|
||||
<< " :num-eqs " << s.m_stats.m_num_eq
|
||||
<< mem_stat() << m_watch << ")\n");
|
||||
}
|
||||
};
|
||||
|
||||
void anf_simplifier::operator()() {
|
||||
|
||||
vector<literal_vector> xors;
|
||||
clause_vector clauses;
|
||||
svector<solver::bin_clause> bins;
|
||||
m_relevant.reset();
|
||||
m_relevant.resize(s.num_vars(), false);
|
||||
for (clause* cp : s.m_clauses) cp->unmark_used();
|
||||
collect_xors(xors);
|
||||
collect_clauses(clauses, bins);
|
||||
|
||||
dd::pdd_manager m(20, dd::pdd_manager::semantics::mod2_e);
|
||||
report _report(*this);
|
||||
pdd_solver solver(s.rlimit(), m);
|
||||
configure_solver(solver);
|
||||
|
||||
try {
|
||||
for (literal_vector const& x : xors) {
|
||||
add_xor(x, solver);
|
||||
}
|
||||
for (clause* cp : clauses) {
|
||||
add_clause(*cp, solver);
|
||||
}
|
||||
for (auto const& b : bins) {
|
||||
add_bin(b, solver);
|
||||
}
|
||||
}
|
||||
catch (dd::pdd_manager::mem_out) {
|
||||
IF_VERBOSE(2, verbose_stream() << "(sat.anf memout)\n");
|
||||
return;
|
||||
}
|
||||
|
||||
TRACE("anf_simplifier", solver.display(tout););
|
||||
|
||||
clauses2anf(solver);
|
||||
TRACE("anf_simplifier", solver.display(tout); s.display(tout););
|
||||
solver.simplify();
|
||||
|
||||
TRACE("anf_simplifier", solver.display(tout););
|
||||
anf2clauses(solver);
|
||||
anf2phase(solver);
|
||||
save_statistics(solver);
|
||||
IF_VERBOSE(10, m_st.display(verbose_stream() << "(sat.anf.simplifier\n"); verbose_stream() << ")\n");
|
||||
}
|
||||
|
||||
unsigned num_units = 0, num_eq = 0;
|
||||
/**
|
||||
\brief extract learned units and equivalences from processed anf.
|
||||
|
||||
TBD: could learn binary clauses
|
||||
TBD: could try simplify equations using BIG subsumption similar to asymm_branch
|
||||
*/
|
||||
void anf_simplifier::anf2clauses(pdd_solver& solver) {
|
||||
|
||||
for (auto* e : solver.equations()) {
|
||||
auto const& p = e->poly();
|
||||
if (p.is_zero()) {
|
||||
continue;
|
||||
}
|
||||
else if (p.is_val()) {
|
||||
if (p.is_one()) {
|
||||
s.set_conflict();
|
||||
break;
|
||||
}
|
||||
else if (p.is_unary()) {
|
||||
// unit
|
||||
literal lit(p.var(), p.lo().val().is_zero());
|
||||
SASSERT(!p.is_val() && p.lo().is_val() && p.hi().is_val());
|
||||
literal lit(p.var(), p.lo().is_zero());
|
||||
s.assign_unit(lit);
|
||||
++num_units;
|
||||
++m_stats.m_num_units;
|
||||
TRACE("anf_simplifier", tout << "unit " << p << " : " << lit << "\n";);
|
||||
}
|
||||
else if (p.is_binary()) {
|
||||
// equivalence
|
||||
// x + y + c = 0
|
||||
SASSERT(!p.is_val() && p.hi().is_one() && !p.lo().is_val() && p.lo().hi().is_one() && p.lo().lo().is_val());
|
||||
literal x(p.var(), false);
|
||||
literal y(p.lo().var(), p.lo().lo().val().is_zero());
|
||||
literal y(p.lo().var(), p.lo().lo().is_zero());
|
||||
s.mk_clause(x, y, true);
|
||||
s.mk_clause(~x, ~y, true);
|
||||
++num_eq;
|
||||
++m_stats.m_num_eq;
|
||||
TRACE("anf_simplifier", tout << "equivalence " << p << " : " << x << " == " << y << "\n";);
|
||||
}
|
||||
// TBD: could learn binary clauses
|
||||
// TBD: could try simplify equations using BIG subsumption similar to asymm_branch
|
||||
}
|
||||
}
|
||||
|
||||
IF_VERBOSE(10, solver.display_statistics(verbose_stream() << "(sat.anf\n" )
|
||||
<< "\n"
|
||||
<< " :num-unit " << num_units
|
||||
<< " :num-eq " << num_eq
|
||||
<< " :num-xor " << xors.size()
|
||||
<< " :num-cls " << clauses.size()
|
||||
<< " :num-bin " << bins.size()
|
||||
<< ")\n");
|
||||
/**
|
||||
\brief update best phase using solved equations
|
||||
polynomials that are not satisfied evaluate to true.
|
||||
In a satisfying assignment, all polynomials should evaluate to false.
|
||||
assume that solutions are provided in reverse order.
|
||||
|
||||
As a simplifying assumption it relies on the property
|
||||
that if an equation is of the form v + p, where v does not occur in p,
|
||||
then all equations that come after it do not contain p.
|
||||
In this way we can flip the assignment to v without
|
||||
invalidating the evaluation cache.
|
||||
*/
|
||||
void anf_simplifier::anf2phase(pdd_solver& solver) {
|
||||
if (!m_config.m_anf2phase)
|
||||
return;
|
||||
m_eval_ts = 0;
|
||||
reset_eval();
|
||||
auto const& eqs = solver.equations();
|
||||
for (unsigned i = eqs.size(); i-- > 0; ) {
|
||||
dd::pdd const& p = eqs[i]->poly();
|
||||
if (!p.is_val() && p.hi().is_one() && s.m_best_phase[p.var()] != eval(p.lo())) {
|
||||
s.m_best_phase[p.var()] = !s.m_best_phase[p.var()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool anf_simplifier::eval(dd::pdd const& p) {
|
||||
if (p.is_one()) return true;
|
||||
if (p.is_zero()) return false;
|
||||
unsigned index = p.index();
|
||||
if (index < m_eval_cache.size()) {
|
||||
if (m_eval_cache[index] == m_eval_ts) return false;
|
||||
if (m_eval_cache[index] == m_eval_ts + 1) return true;
|
||||
}
|
||||
SASSERT(!p.is_val());
|
||||
bool hi = eval(p.hi());
|
||||
bool lo = eval(p.lo());
|
||||
bool v = (hi && s.m_best_phase[p.var()]) ^ lo;
|
||||
m_eval_cache.reserve(index + 1, 0);
|
||||
m_eval_cache[index] = m_eval_ts + v;
|
||||
return v;
|
||||
}
|
||||
|
||||
void anf_simplifier::reset_eval() {
|
||||
if (m_eval_ts + 2 < m_eval_ts) {
|
||||
m_eval_cache.reset();
|
||||
m_eval_ts = 0;
|
||||
}
|
||||
m_eval_ts += 2;
|
||||
}
|
||||
|
||||
void anf_simplifier::clauses2anf(pdd_solver& solver) {
|
||||
svector<solver::bin_clause> bins;
|
||||
m_relevant.reset();
|
||||
m_relevant.resize(s.num_vars(), false);
|
||||
clause_vector clauses(s.clauses());
|
||||
s.collect_bin_clauses(bins, false, false);
|
||||
collect_clauses(clauses, bins);
|
||||
try {
|
||||
compile_xors(clauses, solver);
|
||||
compile_aigs(clauses, bins, solver);
|
||||
|
||||
for (auto const& b : bins) {
|
||||
add_bin(b, solver);
|
||||
}
|
||||
for (clause* cp : clauses) {
|
||||
add_clause(*cp, solver);
|
||||
}
|
||||
}
|
||||
catch (dd::pdd_manager::mem_out) {
|
||||
IF_VERBOSE(1, verbose_stream() << "(sat.anf memout)\n");
|
||||
}
|
||||
}
|
||||
|
||||
void anf_simplifier::collect_clauses(clause_vector & clauses, svector<solver::bin_clause>& bins) {
|
||||
clause_vector oclauses;
|
||||
for (clause* cp : s.clauses()) {
|
||||
svector<solver::bin_clause> obins;
|
||||
|
||||
unsigned j = 0;
|
||||
for (clause* cp : clauses) {
|
||||
clause const& c = *cp;
|
||||
if (c.was_used() || is_too_large(c))
|
||||
if (is_too_large(c))
|
||||
continue;
|
||||
else if (is_pre_satisfied(c)) {
|
||||
oclauses.push_back(cp);
|
||||
}
|
||||
else {
|
||||
clauses.push_back(cp);
|
||||
clauses[j++] = cp;
|
||||
}
|
||||
}
|
||||
svector<solver::bin_clause> obins;
|
||||
s.collect_bin_clauses(obins, false, false);
|
||||
unsigned j = 0;
|
||||
for (auto const& b : obins) {
|
||||
clauses.shrink(j);
|
||||
|
||||
j = 0;
|
||||
for (auto const& b : bins) {
|
||||
if (is_pre_satisfied(b)) {
|
||||
obins[j++] = b;
|
||||
obins.push_back(b);
|
||||
}
|
||||
else {
|
||||
bins.push_back(b);
|
||||
bins[j++] = b;
|
||||
}
|
||||
}
|
||||
obins.shrink(j);
|
||||
bins.shrink(j);
|
||||
|
||||
while (bins.size() + clauses.size() < m_config.m_max_clauses) {
|
||||
unsigned rounds = 0, max_rounds = 3;
|
||||
bool added = true;
|
||||
while (bins.size() + clauses.size() < m_config.m_max_clauses &&
|
||||
(!obins.empty() || !oclauses.empty()) &&
|
||||
added &&
|
||||
rounds < max_rounds) {
|
||||
|
||||
added = false;
|
||||
for (auto const& b : bins) set_relevant(b);
|
||||
for (clause* cp : clauses) set_relevant(*cp);
|
||||
|
||||
j = 0;
|
||||
for (auto const& b : obins) {
|
||||
if (has_relevant_var(b)) {
|
||||
added = true;
|
||||
bins.push_back(b);
|
||||
}
|
||||
else {
|
||||
|
@ -153,16 +220,24 @@ namespace sat {
|
|||
|
||||
j = 0;
|
||||
for (clause* cp : oclauses) {
|
||||
clause& c = *cp;
|
||||
if (has_relevant_var(c)) {
|
||||
if (has_relevant_var(*cp)) {
|
||||
added = true;
|
||||
clauses.push_back(cp);
|
||||
}
|
||||
else {
|
||||
oclauses.push_back(cp);
|
||||
oclauses[j++] = cp;
|
||||
}
|
||||
}
|
||||
oclauses.shrink(j);
|
||||
}
|
||||
|
||||
TRACE("anf_simplifier",
|
||||
tout << "kept:\n";
|
||||
for (clause* cp : clauses) tout << *cp << "\n";
|
||||
for (auto b : bins) tout << b.first << " " << b.second << "\n";
|
||||
tout << "removed:\n";
|
||||
for (clause* cp : oclauses) tout << *cp << "\n";
|
||||
for (auto b : obins) tout << b.first << " " << b.second << "\n";);
|
||||
}
|
||||
|
||||
void anf_simplifier::set_relevant(solver::bin_clause const& b) {
|
||||
|
@ -197,23 +272,77 @@ namespace sat {
|
|||
return is_relevant(b.first) || is_relevant(b.second);
|
||||
}
|
||||
|
||||
void anf_simplifier::collect_xors(vector<literal_vector>& xors) {
|
||||
std::function<void(literal_vector const&, bool)> f =
|
||||
[&](literal_vector const& l, bool) { xors.push_back(l); };
|
||||
|
||||
xor_util xu(s);
|
||||
xu.set(f);
|
||||
xu.extract_xors();
|
||||
for (clause* cp : s.m_clauses) cp->unmark_used();
|
||||
for (clause* cp : s.m_learned) cp->unmark_used();
|
||||
for (clause* cp : xu.removed_clauses()) cp->mark_used();
|
||||
/**
|
||||
\brief extract xors from all s.clauses()
|
||||
(could be just filtered clauses, or clauses with relevant variables).
|
||||
Add the extracted xors to pdd_solver.
|
||||
Remove clauses from list that correspond to extracted xors
|
||||
*/
|
||||
void anf_simplifier::compile_xors(clause_vector& clauses, pdd_solver& ps) {
|
||||
if (!m_config.m_compile_xor) {
|
||||
return;
|
||||
}
|
||||
std::function<void(literal_vector const&)> f =
|
||||
[&,this](literal_vector const& x) {
|
||||
add_xor(x, ps);
|
||||
m_stats.m_num_xors++;
|
||||
};
|
||||
xor_finder xf(s);
|
||||
xf.set(f);
|
||||
xf.extract_xors(clauses);
|
||||
for (clause* cp : clauses) cp->unmark_used();
|
||||
for (clause* cp : xf.removed_clauses()) cp->mark_used();
|
||||
std::function<bool(clause*)> not_used = [](clause* cp) { return !cp->was_used(); };
|
||||
clauses.filter_update(not_used);
|
||||
}
|
||||
|
||||
static solver::bin_clause normalize(solver::bin_clause const& b) {
|
||||
if (b.first.index() > b.second.index()) {
|
||||
return solver::bin_clause(b.second, b.first);
|
||||
}
|
||||
else {
|
||||
return b;
|
||||
}
|
||||
}
|
||||
/**
|
||||
\brief extract AIGs from clauses.
|
||||
Add the extracted AIGs to pdd_solver.
|
||||
Remove clauses from list that correspond to extracted AIGs
|
||||
Remove binary clauses that correspond to extracted AIGs.
|
||||
*/
|
||||
void anf_simplifier::compile_aigs(clause_vector& clauses, svector<solver::bin_clause>& bins, pdd_solver& ps) {
|
||||
if (!m_config.m_compile_aig) {
|
||||
return;
|
||||
}
|
||||
for (clause* cp : clauses) cp->unmark_used();
|
||||
hashtable<solver::bin_clause, solver::bin_clause_hash, default_eq<solver::bin_clause>> seen_bin;
|
||||
|
||||
std::function<void(literal head, literal_vector const& tail, clause& c)> f =
|
||||
[&,this](literal head, literal_vector const& tail, clause& c) {
|
||||
c.mark_used();
|
||||
add_aig(head, tail, ps);
|
||||
for (literal l : tail) {
|
||||
seen_bin.insert(normalize(solver::bin_clause(~l, head)));
|
||||
}
|
||||
m_stats.m_num_aigs++;
|
||||
};
|
||||
aig_finder af(s);
|
||||
af.set(f);
|
||||
af(clauses);
|
||||
|
||||
std::function<bool(clause*)> not_used = [](clause* cp) { return !cp->was_used(); };
|
||||
std::function<bool(solver::bin_clause b)> not_seen = [&](solver::bin_clause b) { return !seen_bin.contains(normalize(b)); };
|
||||
clauses.filter_update(not_used);
|
||||
bins.filter_update(not_seen);
|
||||
}
|
||||
|
||||
/**
|
||||
assign levels to variables.
|
||||
use s.def_level as a primary source for the level of a variable.
|
||||
secondarily, sort variables randomly (each variable is assigned
|
||||
a random, unique, id).
|
||||
*/
|
||||
void anf_simplifier::configure_solver(pdd_solver& ps) {
|
||||
// assign levels to variables.
|
||||
// use s.def_level as a primary source for the level of a variable.
|
||||
// secondarily, sort variables randomly (each variable is assigned
|
||||
// a random, unique, id).
|
||||
unsigned nv = s.num_vars();
|
||||
unsigned_vector l2v(nv), var2id(nv), id2var(nv);
|
||||
svector<std::pair<unsigned, unsigned>> vl(nv);
|
||||
|
@ -239,36 +368,48 @@ namespace sat {
|
|||
ps.set(cfg);
|
||||
}
|
||||
|
||||
#define lit2pdd(_l_) _l_.sign() ? ~m.mk_var(_l_.var()) : m.mk_var(_l_.var())
|
||||
|
||||
void anf_simplifier::add_bin(solver::bin_clause const& b, pdd_solver& ps) {
|
||||
auto& m = ps.get_manager();
|
||||
auto v = m.mk_var(b.first.var());
|
||||
auto w = m.mk_var(b.second.var());
|
||||
if (b.first.sign()) v = ~v;
|
||||
if (b.second.sign()) w = ~w;
|
||||
auto v = lit2pdd(b.first);
|
||||
auto w = lit2pdd(b.second);
|
||||
dd::pdd p = v | w;
|
||||
ps.add(p);
|
||||
TRACE("anf_simplifier", tout << "bin: " << b.first << " " << b.second << " : " << p << "\n";);
|
||||
}
|
||||
|
||||
void anf_simplifier::add_clause(clause const& c, pdd_solver& ps) {
|
||||
auto& m = ps.get_manager();
|
||||
dd::pdd p = m.zero();
|
||||
for (literal l : c) {
|
||||
auto v = m.mk_var(l.var());
|
||||
if (l.sign()) v = ~v;
|
||||
p |= v;
|
||||
}
|
||||
for (literal l : c) p |= lit2pdd(l);
|
||||
ps.add(p);
|
||||
TRACE("anf_simplifier", tout << "clause: " << c << " : " << p << "\n";);
|
||||
}
|
||||
|
||||
void anf_simplifier::add_xor(literal_vector const& x, pdd_solver& ps) {
|
||||
auto& m = ps.get_manager();
|
||||
dd::pdd p = m.zero();
|
||||
for (literal l : x) {
|
||||
auto v = m.mk_var(l.var());
|
||||
if (l.sign()) v = ~v;
|
||||
p ^= v;
|
||||
}
|
||||
for (literal l : x) p ^= lit2pdd(l);
|
||||
ps.add(p);
|
||||
TRACE("anf_simplifier", tout << "xor: " << x << " : " << p << "\n";);
|
||||
}
|
||||
|
||||
void anf_simplifier::add_aig(literal head, literal_vector const& ands, pdd_solver& ps) {
|
||||
auto& m = ps.get_manager();
|
||||
dd::pdd q = m.one();
|
||||
for (literal l : ands) q &= lit2pdd(l);
|
||||
dd::pdd p = lit2pdd(head) ^ q;
|
||||
ps.add(p);
|
||||
TRACE("anf_simplifier", tout << "aig: " << head << " == " << ands << " : " << p << "\n";);
|
||||
}
|
||||
|
||||
void anf_simplifier::save_statistics(pdd_solver& solver) {
|
||||
solver.collect_statistics(m_st);
|
||||
m_st.update("anf.num-units", m_stats.m_num_units);
|
||||
m_st.update("anf.num-eqs", m_stats.m_num_eq);
|
||||
m_st.update("anf.num-aigs", m_stats.m_num_aigs);
|
||||
m_st.update("anf.num-xors", m_stats.m_num_xors);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -25,31 +25,70 @@
|
|||
#include "sat/sat_types.h"
|
||||
#include "sat/sat_solver.h"
|
||||
|
||||
namespace dd {
|
||||
class pdd;
|
||||
class solver;
|
||||
};
|
||||
|
||||
namespace sat {
|
||||
|
||||
class pdd_solver;
|
||||
typedef dd::solver pdd_solver;
|
||||
|
||||
class anf_simplifier {
|
||||
public:
|
||||
struct config {
|
||||
unsigned m_max_clause_size;
|
||||
unsigned m_max_clauses;
|
||||
bool m_compile_xor;
|
||||
bool m_compile_aig;
|
||||
bool m_anf2phase;
|
||||
config():
|
||||
m_max_clause_size(10),
|
||||
m_max_clauses(10000)
|
||||
m_max_clauses(10000),
|
||||
m_compile_xor(true),
|
||||
m_compile_aig(true),
|
||||
m_anf2phase(false)
|
||||
{}
|
||||
};
|
||||
|
||||
private:
|
||||
solver& s;
|
||||
config m_config;
|
||||
svector<bool> m_relevant;
|
||||
struct report;
|
||||
|
||||
struct stats {
|
||||
unsigned m_num_units, m_num_eq;
|
||||
unsigned m_num_aigs, m_num_xors;
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
solver& s;
|
||||
config m_config;
|
||||
svector<bool> m_relevant;
|
||||
stats m_stats;
|
||||
statistics m_st;
|
||||
unsigned_vector m_eval_cache;
|
||||
unsigned m_eval_ts;
|
||||
svector<bool> m_used_for_evaluation;
|
||||
|
||||
void clauses2anf(pdd_solver& solver);
|
||||
void anf2clauses(pdd_solver& solver);
|
||||
void anf2phase(pdd_solver& solver);
|
||||
|
||||
void collect_clauses(clause_vector & clauses, svector<solver::bin_clause>& bins);
|
||||
|
||||
void compile_xors(clause_vector& clauses, pdd_solver& ps);
|
||||
void compile_aigs(clause_vector& clauses, svector<solver::bin_clause>& bins, pdd_solver& ps);
|
||||
|
||||
void collect_xors(vector<literal_vector>& xors);
|
||||
void configure_solver(pdd_solver& ps);
|
||||
void add_clause(clause const& c, pdd_solver& ps);
|
||||
void add_bin(solver::bin_clause const& b, pdd_solver& ps);
|
||||
void add_xor(literal_vector const& x, pdd_solver& ps);
|
||||
void add_aig(literal head, literal_vector const& ands, pdd_solver& ps);
|
||||
void save_statistics(pdd_solver& ps);
|
||||
|
||||
bool eval(dd::pdd const& p);
|
||||
void reset_eval();
|
||||
|
||||
bool is_pre_satisfied(clause const& c);
|
||||
bool is_pre_satisfied(solver::bin_clause const& b);
|
||||
|
@ -65,11 +104,13 @@ namespace sat {
|
|||
void set_relevant(literal l) { set_relevant(l.var()); }
|
||||
void set_relevant(bool_var v) { m_relevant[v] = true; }
|
||||
|
||||
|
||||
public:
|
||||
anf_simplifier(solver& s) : s(s) {}
|
||||
~anf_simplifier() {}
|
||||
|
||||
void operator()();
|
||||
void set(config const& cfg) { m_config = cfg; }
|
||||
void collect_statistics(statistics& st) const { st.copy(m_st); }
|
||||
};
|
||||
}
|
||||
|
|
|
@ -101,6 +101,7 @@ namespace sat {
|
|||
m_unit_walk_threads = p.unit_walk_threads();
|
||||
m_binspr = p.binspr();
|
||||
m_anf_simplify = p.anf();
|
||||
m_aig_simplify = p.aig();
|
||||
m_lookahead_simplify = p.lookahead_simplify();
|
||||
m_lookahead_double = p.lookahead_double();
|
||||
m_lookahead_simplify_bca = p.lookahead_simplify_bca();
|
||||
|
|
|
@ -120,6 +120,7 @@ namespace sat {
|
|||
unsigned m_unit_walk_threads;
|
||||
bool m_unit_walk;
|
||||
bool m_binspr;
|
||||
bool m_aig_simplify;
|
||||
bool m_anf_simplify;
|
||||
bool m_lookahead_simplify;
|
||||
bool m_lookahead_simplify_bca;
|
||||
|
|
|
@ -71,6 +71,7 @@ def_module_params('sat',
|
|||
('unit_walk_threads', UINT, 0, 'number of unit-walk search threads to find satisfiable solution'),
|
||||
('binspr', BOOL, False, 'enable SPR inferences of binary propagation redundant clauses. This inprocessing step eliminates models'),
|
||||
('anf', BOOL, False, 'enable ANF based simplification in-processing'),
|
||||
('aig', BOOL, False, 'enable AIG based simplification in-processing'),
|
||||
('lookahead.cube.cutoff', SYMBOL, 'depth', 'cutoff type used to create lookahead cubes: depth, freevars, psat, adaptive_freevars, adaptive_psat'),
|
||||
# - depth: the maximal cutoff is fixed to the value of lookahead.cube.depth.
|
||||
# So if the value is 10, at most 1024 cubes will be generated of length 10.
|
||||
|
|
|
@ -31,6 +31,7 @@ Revision History:
|
|||
#include "sat/sat_ddfw.h"
|
||||
#include "sat/sat_prob.h"
|
||||
#include "sat/sat_anf_simplifier.h"
|
||||
#include "sat/sat_aig_simplifier.h"
|
||||
#if defined(_MSC_VER) && !defined(_M_ARM) && !defined(_M_ARM64)
|
||||
# include <xmmintrin.h>
|
||||
#endif
|
||||
|
@ -1909,6 +1910,13 @@ namespace sat {
|
|||
if (m_config.m_anf_simplify) {
|
||||
anf_simplifier anf(*this);
|
||||
anf();
|
||||
anf.collect_statistics(m_aux_stats);
|
||||
}
|
||||
|
||||
if (m_config.m_aig_simplify) {
|
||||
aig_simplifier aig(*this);
|
||||
aig();
|
||||
aig.collect_statistics(m_aux_stats);
|
||||
}
|
||||
|
||||
reinit_assumptions();
|
||||
|
|
|
@ -216,7 +216,8 @@ namespace sat {
|
|||
friend struct mk_stat;
|
||||
friend class elim_vars;
|
||||
friend class scoped_detach;
|
||||
friend class xor_util;
|
||||
friend class xor_finder;
|
||||
friend class aig_finder;
|
||||
public:
|
||||
solver(params_ref const & p, reslimit& l);
|
||||
~solver() override;
|
||||
|
@ -402,6 +403,7 @@ namespace sat {
|
|||
bool set_root(literal l, literal r);
|
||||
void flush_roots();
|
||||
typedef std::pair<literal, literal> bin_clause;
|
||||
struct bin_clause_hash { unsigned operator()(bin_clause const& b) const { return b.first.hash() + 2*b.second.hash(); } };
|
||||
protected:
|
||||
watch_list & get_wlist(literal l) { return m_watches[l.index()]; }
|
||||
watch_list const & get_wlist(literal l) const { return m_watches[l.index()]; }
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
|
||||
Module Name:
|
||||
|
||||
sat_xor_util.cpp
|
||||
sat_xor_finder.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
xor utilities
|
||||
xor finderities
|
||||
|
||||
Author:
|
||||
|
||||
|
@ -19,12 +19,12 @@
|
|||
--*/
|
||||
#pragma once;
|
||||
|
||||
#include "sat/sat_xor_util.h"
|
||||
#include "sat/sat_xor_finder.h"
|
||||
#include "sat/sat_solver.h"
|
||||
|
||||
namespace sat {
|
||||
|
||||
void xor_util::extract_xors() {
|
||||
void xor_finder::extract_xors(clause_vector& clauses) {
|
||||
m_removed_clauses.reset();
|
||||
if (!s.get_config().m_xor_solver) {
|
||||
return;
|
||||
|
@ -37,11 +37,11 @@ namespace sat {
|
|||
SASSERT(sizeof(m_combination)*8 <= (1ull << static_cast<uint64_t>(max_size)));
|
||||
init_clause_filter();
|
||||
m_var_position.resize(s.num_vars());
|
||||
for (clause* cp : s.m_clauses) {
|
||||
for (clause* cp : clauses) {
|
||||
cp->unmark_used();
|
||||
}
|
||||
for (; max_size > 2; --max_size) {
|
||||
for (clause* cp : s.m_clauses) {
|
||||
for (clause* cp : clauses) {
|
||||
clause& c = *cp;
|
||||
if (c.size() == max_size && !c.was_removed() && !c.is_learned() && !c.was_used()) {
|
||||
extract_xor(c);
|
||||
|
@ -51,7 +51,7 @@ namespace sat {
|
|||
m_clause_filters.clear();
|
||||
}
|
||||
|
||||
void xor_util::extract_xor(clause& c) {
|
||||
void xor_finder::extract_xor(clause& c) {
|
||||
SASSERT(c.size() > 2);
|
||||
unsigned filter = get_clause_filter(c);
|
||||
s.init_visited();
|
||||
|
@ -99,20 +99,19 @@ namespace sat {
|
|||
}
|
||||
}
|
||||
|
||||
void xor_util::add_xor(bool parity, clause& c) {
|
||||
void xor_finder::add_xor(bool parity, clause& c) {
|
||||
DEBUG_CODE(for (clause* cp : m_clauses_to_remove) VERIFY(cp->was_used()););
|
||||
m_removed_clauses.append(m_clauses_to_remove);
|
||||
bool learned = false;
|
||||
literal_vector lits;
|
||||
for (literal l : c) {
|
||||
lits.push_back(literal(l.var(), false));
|
||||
s.set_external(l.var());
|
||||
}
|
||||
if (parity) lits[0].neg();
|
||||
m_add_xr(lits, learned);
|
||||
m_add_xr(lits);
|
||||
}
|
||||
|
||||
bool xor_util::extract_xor(bool parity, clause& c, literal l1, literal l2) {
|
||||
bool xor_finder::extract_xor(bool parity, clause& c, literal l1, literal l2) {
|
||||
SASSERT(s.is_visited(l1.var()));
|
||||
SASSERT(s.is_visited(l2.var()));
|
||||
m_missing.reset();
|
||||
|
@ -131,7 +130,7 @@ namespace sat {
|
|||
return update_combinations(c, parity, mask);
|
||||
}
|
||||
|
||||
bool xor_util::extract_xor(bool parity, clause& c, clause& c2) {
|
||||
bool xor_finder::extract_xor(bool parity, clause& c, clause& c2) {
|
||||
bool parity2 = false;
|
||||
for (literal l : c2) {
|
||||
if (!s.is_visited(l.var())) return false;
|
||||
|
@ -168,7 +167,7 @@ namespace sat {
|
|||
return update_combinations(c, parity, mask);
|
||||
}
|
||||
|
||||
bool xor_util::update_combinations(clause& c, bool parity, unsigned mask) {
|
||||
bool xor_finder::update_combinations(clause& c, bool parity, unsigned mask) {
|
||||
unsigned num_missing = m_missing.size();
|
||||
for (unsigned k = 0; k < (1ul << num_missing); ++k) {
|
||||
unsigned mask2 = mask;
|
||||
|
@ -189,7 +188,7 @@ namespace sat {
|
|||
return true;
|
||||
}
|
||||
|
||||
void xor_util::init_parity() {
|
||||
void xor_finder::init_parity() {
|
||||
for (unsigned i = m_parity.size(); i <= m_max_xor_size; ++i) {
|
||||
bool_vector bv;
|
||||
for (unsigned j = 0; j < (1ul << i); ++j) {
|
||||
|
@ -203,14 +202,14 @@ namespace sat {
|
|||
}
|
||||
}
|
||||
|
||||
void xor_util::init_clause_filter() {
|
||||
void xor_finder::init_clause_filter() {
|
||||
m_clause_filters.reset();
|
||||
m_clause_filters.resize(s.num_vars());
|
||||
init_clause_filter(s.m_clauses);
|
||||
init_clause_filter(s.m_learned);
|
||||
}
|
||||
|
||||
void xor_util::init_clause_filter(clause_vector& clauses) {
|
||||
void xor_finder::init_clause_filter(clause_vector& clauses) {
|
||||
for (clause* cp : clauses) {
|
||||
clause& c = *cp;
|
||||
if (c.size() <= m_max_xor_size && s.all_distinct(c)) {
|
||||
|
@ -222,7 +221,7 @@ namespace sat {
|
|||
}
|
||||
}
|
||||
|
||||
unsigned xor_util::get_clause_filter(clause& c) {
|
||||
unsigned xor_finder::get_clause_filter(clause& c) {
|
||||
unsigned filter = 0;
|
||||
for (literal l : c) {
|
||||
filter |= 1 << ((l.var() % 32));
|
|
@ -3,11 +3,11 @@
|
|||
|
||||
Module Name:
|
||||
|
||||
sat_xor.h
|
||||
sat_xor_finder.h
|
||||
|
||||
Abstract:
|
||||
|
||||
xor utilities
|
||||
xor finderities
|
||||
|
||||
Author:
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
|||
|
||||
namespace sat {
|
||||
|
||||
class xor_util {
|
||||
class xor_finder {
|
||||
solver& s;
|
||||
struct clause_filter {
|
||||
unsigned m_filter;
|
||||
|
@ -47,7 +47,7 @@ namespace sat {
|
|||
literal_vector m_clause; // reference clause with literals sorted according to main clause
|
||||
unsigned_vector m_missing; // set of indices not occurring in clause.
|
||||
clause_vector m_removed_clauses;
|
||||
std::function<void (literal_vector const& lits, bool learned)> m_add_xr;
|
||||
std::function<void (literal_vector const& lits)> m_add_xr;
|
||||
|
||||
inline void set_combination(unsigned mask) { m_combination |= (1 << mask); }
|
||||
inline bool get_combination(unsigned mask) const { return (m_combination & (1 << mask)) != 0; }
|
||||
|
@ -62,15 +62,15 @@ namespace sat {
|
|||
unsigned get_clause_filter(clause& c);
|
||||
|
||||
public:
|
||||
xor_util(solver& s) : s(s), m_max_xor_size(5) { init_parity(); }
|
||||
~xor_util() {}
|
||||
xor_finder(solver& s) : s(s), m_max_xor_size(5) { init_parity(); }
|
||||
~xor_finder() {}
|
||||
|
||||
void set(std::function<void (literal_vector const& lits, bool learned)>& f) { m_add_xr = f; }
|
||||
void set(std::function<void (literal_vector const& lits)>& f) { m_add_xr = f; }
|
||||
|
||||
bool parity(unsigned i, unsigned j) const { return m_parity[i][j]; }
|
||||
unsigned max_xor_size() const { return m_max_xor_size; }
|
||||
|
||||
void extract_xors();
|
||||
void extract_xors(clause_vector& clauses);
|
||||
clause_vector& removed_clauses() { return m_removed_clauses; }
|
||||
};
|
||||
}
|
Loading…
Reference in a new issue