Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
z3-z3-4.12.6.src.sat.sat_anf_simplifier.cpp Maven / Gradle / Ivy
/*++
Copyright (c) 2020 Microsoft Corporation
Module Name:
sat_anf_simplifier.cpp
Abstract:
Simplification based on ANF format.
Author:
Nikolaj Bjorner 2020-01-02
--*/
#include "util/union_find.h"
#include "sat/sat_anf_simplifier.h"
#include "sat/sat_solver.h"
#include "sat/sat_elim_eqs.h"
#include "sat/sat_xor_finder.h"
#include "sat/sat_aig_finder.h"
#include "math/grobner/pdd_solver.h"
namespace sat {
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_eqs
<< " :mb " << mem_stat()
<< m_watch << ")\n");
}
};
void anf_simplifier::operator()() {
dd::pdd_manager m(20, dd::pdd_manager::semantics::mod2_e);
u_dependency_manager dm;
pdd_solver solver(s.rlimit(), dm, m);
report _report(*this);
configure_solver(solver);
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");
}
/**
\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) {
union_find_default_ctx ctx;
union_find<> uf(ctx);
for (unsigned i = 2*s.num_vars(); i--> 0; ) uf.mk_var();
auto add_eq = [&](literal l1, literal l2) {
uf.merge(l1.index(), l2.index());
uf.merge((~l1).index(), (~l2).index());
};
unsigned old_num_eqs = m_stats.m_num_eqs;
for (auto* e : solver.equations()) {
auto const& p = e->poly();
if (p.is_one()) {
s.set_conflict();
break;
}
else if (p.is_unary()) {
// unit
SASSERT(!p.is_val() && p.lo().is_val() && p.hi().is_val());
literal lit(p.var(), p.lo().is_zero());
s.assign_unit(lit);
++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().is_one());
add_eq(x, y);
++m_stats.m_num_eqs;
TRACE("anf_simplifier", tout << "equivalence " << p << " : " << x << " == " << y << "\n";);
}
}
if (old_num_eqs < m_stats.m_num_eqs) {
elim_eqs elim(s);
elim(uf);
}
}
/**
\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;
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()];
++m_stats.m_num_phase_flips;
}
}
}
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 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& bins) {
clause_vector oclauses;
svector obins;
unsigned j = 0;
for (clause* cp : clauses) {
clause const& c = *cp;
if (is_too_large(c))
continue;
else if (is_pre_satisfied(c)) {
oclauses.push_back(cp);
}
else {
clauses[j++] = cp;
}
}
clauses.shrink(j);
j = 0;
for (auto const& b : bins) {
if (is_pre_satisfied(b)) {
obins.push_back(b);
}
else {
bins[j++] = b;
}
}
bins.shrink(j);
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 {
obins[j++] = b;
}
}
obins.shrink(j);
if (bins.size() + clauses.size() >= m_config.m_max_clauses) {
break;
}
j = 0;
for (clause* cp : oclauses) {
if (has_relevant_var(*cp)) {
added = true;
clauses.push_back(cp);
}
else {
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) {
set_relevant(b.first);
set_relevant(b.second);
}
void anf_simplifier::set_relevant(clause const& c) {
for (literal l : c) set_relevant(l);
}
bool anf_simplifier::is_pre_satisfied(clause const& c) {
for (literal l : c) if (phase_is_true(l)) return true;
return false;
}
bool anf_simplifier::is_pre_satisfied(solver::bin_clause const& b) {
return phase_is_true(b.first) || phase_is_true(b.second);
}
bool anf_simplifier::phase_is_true(literal l) {
bool ph = (s.m_best_phase_size > 0) ? s.m_best_phase[l.var()] : s.m_phase[l.var()];
return l.sign() ? !ph : ph;
}
bool anf_simplifier::has_relevant_var(clause const& c) {
for (literal l : c) if (is_relevant(l)) return true;
return false;
}
bool anf_simplifier::has_relevant_var(solver::bin_clause const& b) {
return is_relevant(b.first) || is_relevant(b.second);
}
/**
\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 f =
[&,this](literal_vector const& x) {
add_xor(x, ps);
m_stats.m_num_xors++;
};
xor_finder xf(s);
xf.set(f);
xf(clauses);
}
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& bins, pdd_solver& ps) {
if (!m_config.m_compile_aig) {
return;
}
hashtable> seen_bin;
std::function on_aig =
[&,this](literal head, literal_vector const& tail) {
add_aig(head, tail, ps);
for (literal l : tail) {
seen_bin.insert(normalize(solver::bin_clause(~l, head)));
}
m_stats.m_num_aigs++;
};
std::function on_if =
[&,this](literal head, literal c, literal th, literal el) {
add_if(head, c, th, el, ps);
m_stats.m_num_ifs++;
};
aig_finder af(s);
af.set(on_aig);
af.set(on_if);
af(clauses);
std::function not_seen =
[&](solver::bin_clause b) { return !seen_bin.contains(normalize(b)); };
bins.filter_update(not_seen);
}
/**
assign levels to variables.
use variable id 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) {
unsigned nv = s.num_vars();
unsigned_vector l2v(nv), var2id(nv), id2var(nv);
svector> vl(nv);
for (unsigned i = 0; i < nv; ++i) var2id[i] = i;
shuffle(var2id.size(), var2id.data(), s.rand());
for (unsigned i = 0; i < nv; ++i) id2var[var2id[i]] = i;
for (unsigned i = 0; i < nv; ++i) vl[i] = std::make_pair(i, var2id[i]);
std::sort(vl.begin(), vl.end());
for (unsigned i = 0; i < nv; ++i) l2v[i] = id2var[vl[i].second];
ps.get_manager().reset(l2v);
// set configuration parameters.
dd::solver::config cfg;
cfg.m_expr_size_limit = 1000;
cfg.m_max_steps = 1000;
cfg.m_random_seed = s.rand()();
cfg.m_enable_exlin = m_config.m_enable_exlin;
unsigned max_num_nodes = 1 << 18;
ps.get_manager().set_max_num_nodes(max_num_nodes);
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();
dd::pdd p = (lit2pdd(b.first) | lit2pdd(b.second)) ^ true;
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) {
if (c.size() > m_config.m_max_clause_size) return;
auto& m = ps.get_manager();
dd::pdd p = m.zero();
for (literal l : c) p |= lit2pdd(l);
p = p ^ true;
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.one();
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 << " poly : " << p << "\n";);
}
void anf_simplifier::add_if(literal head, literal c, literal th, literal el, pdd_solver& ps) {
auto& m = ps.get_manager();
dd::pdd cond = lit2pdd(c);
dd::pdd p = lit2pdd(head) ^ (cond & lit2pdd(th)) ^ (~cond & lit2pdd(el));
ps.add(p);
TRACE("anf_simplifier", tout << "ite: " << head << " == " << c << "?" << th << ":" << el << " poly : " << p << "\n";);
}
void anf_simplifier::save_statistics(pdd_solver& solver) {
solver.collect_statistics(m_st);
m_st.update("sat-anf.units", m_stats.m_num_units);
m_st.update("sat-anf.eqs", m_stats.m_num_eqs);
m_st.update("sat-anf.ands", m_stats.m_num_aigs);
m_st.update("sat-anf.ites", m_stats.m_num_ifs);
m_st.update("sat-anf.xors", m_stats.m_num_xors);
m_st.update("sat-anf.phase_flips", m_stats.m_num_phase_flips);
}
}