3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-23 09:05:31 +00:00

checkpoint

Signed-off-by: Leonardo de Moura <leonardo@microsoft.com>
This commit is contained in:
Leonardo de Moura 2012-10-24 11:11:07 -07:00
parent 463297d264
commit 69ce24a6ce
17 changed files with 3 additions and 3 deletions

View file

@ -1,791 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
assertion_set2sat.cpp
Abstract:
"Compile" an assertion set into the SAT engine.
Atoms are "abstracted" into boolean variables.
The mapping between boolean variables and atoms
can be used to convert back the state of the
SAT engine into an assertion set.
The idea is to support scenarios such as:
1) simplify, blast, convert into SAT, and solve
2) convert into SAT, apply SAT for a while, learn new units, and translate back into an assertion set.
3) convert into SAT, apply SAT preprocessor (failed literal propagation, resolution, etc) and translate back into an assertion set.
4) Convert boolean structure into SAT, convert atoms into another engine, combine engines using lazy combination, solve.
Author:
Leonardo (leonardo) 2011-05-22
Notes:
--*/
#include"assertion_set2sat.h"
#include"ast_smt2_pp.h"
#include"ref_util.h"
#include"cooperate.h"
#include"filter_model_converter.h"
#include"model_evaluator.h"
#include"for_each_expr.h"
#include"model_v2_pp.h"
#include"assertion_set_util.h"
struct assertion_set2sat::imp {
struct frame {
app * m_t;
unsigned m_root:1;
unsigned m_sign:1;
unsigned m_idx;
frame(app * t, bool r, bool s, unsigned idx):
m_t(t), m_root(r), m_sign(s), m_idx(idx) {}
};
ast_manager & m;
svector<frame> m_frame_stack;
svector<sat::literal> m_result_stack;
obj_map<app, sat::literal> m_cache;
obj_hashtable<expr> m_interface_vars;
sat::solver & m_solver;
atom2bool_var & m_map;
sat::bool_var m_true;
bool m_ite_extra;
unsigned long long m_max_memory;
volatile bool m_cancel;
imp(ast_manager & _m, params_ref const & p, sat::solver & s, atom2bool_var & map):
m(_m),
m_solver(s),
m_map(map) {
updt_params(p);
m_cancel = false;
m_true = sat::null_bool_var;
}
void updt_params(params_ref const & p) {
m_ite_extra = p.get_bool(":ite-extra", true);
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
}
void throw_op_not_handled() {
throw assertion_set2sat_exception("operator not supported, apply simplifier before invoking translator");
}
void mk_clause(sat::literal l) {
TRACE("assertion_set2sat", tout << "mk_clause: " << l << "\n";);
m_solver.mk_clause(1, &l);
}
void mk_clause(sat::literal l1, sat::literal l2) {
TRACE("assertion_set2sat", tout << "mk_clause: " << l1 << " " << l2 << "\n";);
m_solver.mk_clause(l1, l2);
}
void mk_clause(sat::literal l1, sat::literal l2, sat::literal l3) {
TRACE("assertion_set2sat", tout << "mk_clause: " << l1 << " " << l2 << " " << l3 << "\n";);
m_solver.mk_clause(l1, l2, l3);
}
void mk_clause(unsigned num, sat::literal * lits) {
TRACE("assertion_set2sat", tout << "mk_clause: "; for (unsigned i = 0; i < num; i++) tout << lits[i] << " "; tout << "\n";);
m_solver.mk_clause(num, lits);
}
sat::bool_var mk_true() {
// create fake variable to represent true;
if (m_true == sat::null_bool_var) {
m_true = m_solver.mk_var();
mk_clause(sat::literal(m_true, false)); // v is true
}
return m_true;
}
void convert_atom(expr * t, bool root, bool sign) {
SASSERT(m.is_bool(t));
sat::literal l;
sat::bool_var v = m_map.to_bool_var(t);
if (v == sat::null_bool_var) {
if (m.is_true(t)) {
l = sat::literal(mk_true(), sign);
}
else if (m.is_false(t)) {
l = sat::literal(mk_true(), !sign);
}
else {
bool ext = !is_uninterp_const(t) || m_interface_vars.contains(t);
sat::bool_var v = m_solver.mk_var(ext);
m_map.insert(t, v);
l = sat::literal(v, sign);
TRACE("assertion_set2sat", tout << "new_var: " << v << "\n" << mk_ismt2_pp(t, m) << "\n";);
}
}
else {
SASSERT(v != sat::null_bool_var);
l = sat::literal(v, sign);
}
SASSERT(l != sat::null_literal);
if (root)
mk_clause(l);
else
m_result_stack.push_back(l);
}
bool process_cached(app * t, bool root, bool sign) {
sat::literal l;
if (m_cache.find(t, l)) {
if (sign)
l.neg();
if (root)
mk_clause(l);
else
m_result_stack.push_back(l);
return true;
}
return false;
}
bool visit(expr * t, bool root, bool sign) {
if (!is_app(t)) {
convert_atom(t, root, sign);
return true;
}
if (process_cached(to_app(t), root, sign))
return true;
if (to_app(t)->get_family_id() != m.get_basic_family_id()) {
convert_atom(t, root, sign);
return true;
}
switch (to_app(t)->get_decl_kind()) {
case OP_NOT:
case OP_OR:
case OP_IFF:
m_frame_stack.push_back(frame(to_app(t), root, sign, 0));
return false;
case OP_ITE:
case OP_EQ:
if (m.is_bool(to_app(t)->get_arg(1))) {
m_frame_stack.push_back(frame(to_app(t), root, sign, 0));
return false;
}
convert_atom(t, root, sign);
return true;
case OP_AND:
case OP_XOR:
case OP_IMPLIES:
case OP_DISTINCT:
TRACE("assertion_set2sat_not_handled", tout << mk_ismt2_pp(t, m) << "\n";);
throw_op_not_handled();
default:
convert_atom(t, root, sign);
return true;
}
}
void convert_or(app * t, bool root, bool sign) {
TRACE("assertion_set2sat", tout << "convert_or:\n" << mk_ismt2_pp(t, m) << "\n";);
unsigned num = t->get_num_args();
if (root) {
SASSERT(num == m_result_stack.size());
if (sign) {
// this case should not really happen.
for (unsigned i = 0; i < num; i++) {
sat::literal l = m_result_stack[i];
l.neg();
mk_clause(l);
}
}
else {
mk_clause(m_result_stack.size(), m_result_stack.c_ptr());
m_result_stack.reset();
}
}
else {
SASSERT(num <= m_result_stack.size());
sat::bool_var k = m_solver.mk_var();
sat::literal l(k, false);
m_cache.insert(t, l);
sat::literal * lits = m_result_stack.end() - num;
for (unsigned i = 0; i < num; i++) {
mk_clause(~lits[i], l);
}
m_result_stack.push_back(~l);
lits = m_result_stack.end() - num - 1;
// remark: mk_clause may perform destructive updated to lits.
// I have to execute it after the binary mk_clause above.
mk_clause(num+1, lits);
unsigned old_sz = m_result_stack.size() - num - 1;
m_result_stack.shrink(old_sz);
if (sign)
l.neg();
m_result_stack.push_back(l);
}
}
void convert_ite(app * n, bool root, bool sign) {
unsigned sz = m_result_stack.size();
SASSERT(sz >= 3);
sat::literal c = m_result_stack[sz-3];
sat::literal t = m_result_stack[sz-2];
sat::literal e = m_result_stack[sz-1];
if (root) {
SASSERT(sz == 3);
if (sign) {
mk_clause(~c, ~t);
mk_clause(c, ~e);
}
else {
mk_clause(~c, t);
mk_clause(c, e);
}
m_result_stack.reset();
}
else {
sat::bool_var k = m_solver.mk_var();
sat::literal l(k, false);
m_cache.insert(n, l);
mk_clause(~l, ~c, t);
mk_clause(~l, c, e);
mk_clause(l, ~c, ~t);
mk_clause(l, c, ~e);
if (m_ite_extra) {
mk_clause(~t, ~e, l);
mk_clause(t, e, ~l);
}
m_result_stack.shrink(sz-3);
if (sign)
l.neg();
m_result_stack.push_back(l);
}
}
void convert_iff(app * t, bool root, bool sign) {
TRACE("assertion_set2sat", tout << "convert_iff " << root << " " << sign << "\n" << mk_ismt2_pp(t, m) << "\n";);
unsigned sz = m_result_stack.size();
SASSERT(sz >= 2);
sat::literal l1 = m_result_stack[sz-1];
sat::literal l2 = m_result_stack[sz-2];
if (root) {
SASSERT(sz == 2);
if (sign) {
mk_clause(l1, l2);
mk_clause(~l1, ~l2);
}
else {
mk_clause(l1, ~l2);
mk_clause(~l1, l2);
}
m_result_stack.reset();
}
else {
sat::bool_var k = m_solver.mk_var();
sat::literal l(k, false);
m_cache.insert(t, l);
mk_clause(~l, l1, ~l2);
mk_clause(~l, ~l1, l2);
mk_clause(l, l1, l2);
mk_clause(l, ~l1, ~l2);
m_result_stack.shrink(sz-2);
if (sign)
l.neg();
m_result_stack.push_back(l);
}
}
void convert(app * t, bool root, bool sign) {
SASSERT(t->get_family_id() == m.get_basic_family_id());
switch (to_app(t)->get_decl_kind()) {
case OP_OR:
convert_or(t, root, sign);
break;
case OP_ITE:
convert_ite(t, root, sign);
break;
case OP_IFF:
case OP_EQ:
convert_iff(t, root, sign);
break;
default:
UNREACHABLE();
}
}
void process(expr * n) {
TRACE("assertion_set2sat", tout << "converting: " << mk_ismt2_pp(n, m) << "\n";);
if (visit(n, true, false)) {
SASSERT(m_result_stack.empty());
return;
}
while (!m_frame_stack.empty()) {
loop:
cooperate("assertion_set2sat");
if (m_cancel)
throw assertion_set2sat_exception(STE_CANCELED_MSG);
if (memory::get_allocation_size() > m_max_memory)
throw assertion_set2sat_exception(STE_MAX_MEMORY_MSG);
frame & fr = m_frame_stack.back();
app * t = fr.m_t;
bool root = fr.m_root;
bool sign = fr.m_sign;
TRACE("assertion_set2sat_bug", tout << "result stack\n";
tout << mk_ismt2_pp(t, m) << " root: " << root << " sign: " << sign << "\n";
for (unsigned i = 0; i < m_result_stack.size(); i++) tout << m_result_stack[i] << " ";
tout << "\n";);
if (fr.m_idx == 0 && process_cached(t, root, sign)) {
m_frame_stack.pop_back();
continue;
}
if (m.is_not(t)) {
m_frame_stack.pop_back();
visit(t->get_arg(0), root, !sign);
continue;
}
unsigned num = t->get_num_args();
while (fr.m_idx < num) {
expr * arg = t->get_arg(fr.m_idx);
fr.m_idx++;
if (!visit(arg, false, false))
goto loop;
}
TRACE("assertion_set2sat_bug", tout << "converting\n";
tout << mk_ismt2_pp(t, m) << " root: " << root << " sign: " << sign << "\n";
for (unsigned i = 0; i < m_result_stack.size(); i++) tout << m_result_stack[i] << " ";
tout << "\n";);
convert(t, root, sign);
m_frame_stack.pop_back();
}
SASSERT(m_result_stack.empty());
}
void operator()(assertion_set const & s) {
m_interface_vars.reset();
collect_boolean_interface(s, m_interface_vars);
unsigned size = s.size();
for (unsigned idx = 0; idx < size; idx++) {
expr * f = s.form(idx);
process(f);
}
}
void operator()(unsigned sz, expr * const * fs) {
m_interface_vars.reset();
collect_boolean_interface(m, sz, fs, m_interface_vars);
for (unsigned i = 0; i < sz; i++)
process(fs[i]);
}
void set_cancel(bool f) { m_cancel = f; }
};
struct unsupported_bool_proc {
struct found {};
ast_manager & m;
unsupported_bool_proc(ast_manager & _m):m(_m) {}
void operator()(var *) {}
void operator()(quantifier *) {}
void operator()(app * n) {
if (n->get_family_id() == m.get_basic_family_id()) {
switch (n->get_decl_kind()) {
case OP_AND:
case OP_XOR:
case OP_IMPLIES:
case OP_DISTINCT:
throw found();
default:
break;
}
}
}
};
/**
\brief Return true if s contains an unsupported Boolean operator.
assertion_set_rewriter (with the following configuration) can be used to
eliminate unsupported operators.
:elim-and true
:blast-distinct true
*/
bool assertion_set2sat::has_unsupported_bool(assertion_set const & s) {
return test<unsupported_bool_proc>(s);
}
assertion_set2sat::assertion_set2sat():m_imp(0) {
}
void assertion_set2sat::collect_param_descrs(param_descrs & r) {
insert_max_memory(r);
r.insert(":ite-extra", CPK_BOOL, "(default: true) add redundant clauses (that improve unit propagation) when encoding if-then-else formulas");
}
struct assertion_set2sat::scoped_set_imp {
assertion_set2sat * m_owner;
scoped_set_imp(assertion_set2sat * o, assertion_set2sat::imp * i):m_owner(o) {
#pragma omp critical (assertion_set2sat)
{
m_owner->m_imp = i;
}
}
~scoped_set_imp() {
#pragma omp critical (assertion_set2sat)
{
m_owner->m_imp = 0;
}
}
};
void assertion_set2sat::operator()(assertion_set const & s, params_ref const & p, sat::solver & t, atom2bool_var & m) {
imp proc(s.m(), p, t, m);
scoped_set_imp set(this, &proc);
proc(s);
}
void assertion_set2sat::operator()(ast_manager & mng, unsigned num, expr * const * fs, params_ref const & p, sat::solver & t, atom2bool_var & m) {
imp proc(mng, p, t, m);
scoped_set_imp set(this, &proc);
proc(num, fs);
}
void assertion_set2sat::set_cancel(bool f) {
#pragma omp critical (assertion_set2sat)
{
if (m_imp)
m_imp->set_cancel(f);
}
}
class sat_model_converter : public model_converter {
sat::model_converter m_mc;
// TODO: the following mapping is storing a lot of useless information, and may be a performance bottleneck.
// We need to save only the expressions associated with variables that occur in m_mc.
// This information may be stored as a vector of pairs.
// The mapping is only created during the model conversion.
expr_ref_vector m_var2expr;
ref<filter_model_converter> m_fmc; // filter for eliminating fresh variables introduced in the assertion-set --> sat conversion
sat_model_converter(ast_manager & m):
m_var2expr(m) {
}
public:
sat_model_converter(ast_manager & m, sat::solver const & s):m_var2expr(m) {
m_mc.copy(s.get_model_converter());
m_fmc = alloc(filter_model_converter, m);
}
ast_manager & m() { return m_var2expr.get_manager(); }
void insert(expr * atom, bool aux) {
m_var2expr.push_back(atom);
if (aux) {
SASSERT(is_uninterp_const(atom));
SASSERT(m().is_bool(atom));
m_fmc->insert(to_app(atom)->get_decl());
}
}
virtual void operator()(model_ref & md) {
TRACE("sat_mc", tout << "before sat_mc\n"; model_v2_pp(tout, *md); display(tout););
// REMARK: potential problem
// model_evaluator can't evaluate quantifiers. Then,
// an eliminated variable that depends on a quantified expression can't be recovered.
// A similar problem also affects any model_converter that uses elim_var_model_converter.
//
// Possible solution:
// model_converters reject any variable elimination that depends on a quantified expression.
model_evaluator ev(*md);
ev.set_model_completion(false);
// create a SAT model using md
sat::model sat_md;
unsigned sz = m_var2expr.size();
expr_ref val(m());
for (sat::bool_var v = 0; v < sz; v++) {
expr * atom = m_var2expr.get(v);
ev(atom, val);
if (m().is_true(val))
sat_md.push_back(l_true);
else if (m().is_false(val))
sat_md.push_back(l_false);
else
sat_md.push_back(l_undef);
}
// apply SAT model converter
m_mc(sat_md);
// register value of non-auxiliary boolean variables back into md
sz = m_var2expr.size();
for (sat::bool_var v = 0; v < sz; v++) {
expr * atom = m_var2expr.get(v);
if (is_uninterp_const(atom)) {
func_decl * d = to_app(atom)->get_decl();
lbool new_val = sat_md[v];
if (new_val == l_true)
md->register_decl(d, m().mk_true());
else if (new_val == l_false)
md->register_decl(d, m().mk_false());
}
}
// apply filter model converter
(*m_fmc)(md);
TRACE("sat_mc", tout << "after sat_mc\n"; model_v2_pp(tout, *md););
}
virtual model_converter * translate(ast_translation & translator) {
sat_model_converter * res = alloc(sat_model_converter, translator.to());
res->m_fmc = static_cast<filter_model_converter*>(m_fmc->translate(translator));
unsigned sz = m_var2expr.size();
for (unsigned i = 0; i < sz; i++)
res->m_var2expr.push_back(translator(m_var2expr.get(i)));
return res;
}
void display(std::ostream & out) {
out << "(sat-model-converter\n";
m_mc.display(out);
sat::bool_var_set vars;
m_mc.collect_vars(vars);
out << "(atoms";
unsigned sz = m_var2expr.size();
for (unsigned i = 0; i < sz; i++) {
if (vars.contains(i)) {
out << "\n (" << i << "\n " << mk_ismt2_pp(m_var2expr.get(i), m(), 2) << ")";
}
}
out << ")\n";
m_fmc->display(out);
out << ")\n";
}
};
struct sat2assertion_set::imp {
ast_manager & m;
expr_ref_vector m_lit2expr;
unsigned long long m_max_memory;
bool m_learned;
bool m_produce_models;
volatile bool m_cancel;
imp(ast_manager & _m, params_ref const & p):m(_m), m_lit2expr(m), m_cancel(false) {
updt_params(p);
}
void updt_params(params_ref const & p) {
m_produce_models = p.get_bool(":produce-models", false);
m_learned = p.get_bool(":learned", false);
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
}
void checkpoint() {
if (m_cancel)
throw sat2assertion_set_exception(STE_CANCELED_MSG);
if (memory::get_allocation_size() > m_max_memory)
throw sat2assertion_set_exception(STE_MAX_MEMORY_MSG);
}
void init_lit2expr(sat::solver const & s, atom2bool_var const & map, model_converter_ref & mc) {
ref<sat_model_converter> _mc;
if (m_produce_models) {
_mc = alloc(sat_model_converter, m, s);
}
unsigned num_vars = s.num_vars();
m_lit2expr.resize(num_vars * 2);
map.mk_inv(m_lit2expr);
sort * b = m.mk_bool_sort();
for (sat::bool_var v = 0; v < num_vars; v++) {
checkpoint();
sat::literal l(v, false);
if (m_lit2expr.get(l.index()) == 0) {
SASSERT(m_lit2expr.get((~l).index()) == 0);
app * aux = m.mk_fresh_const(0, b);
if (_mc)
_mc->insert(aux, true);
m_lit2expr.set(l.index(), aux);
m_lit2expr.set((~l).index(), m.mk_not(aux));
}
else {
if (_mc)
_mc->insert(m_lit2expr.get(l.index()), false);
SASSERT(m_lit2expr.get((~l).index()) != 0);
}
}
mc = _mc.get();
}
expr * lit2expr(sat::literal l) {
return m_lit2expr.get(l.index());
}
void assert_clauses(sat::clause * const * begin, sat::clause * const * end, assertion_set & r) {
ptr_buffer<expr> lits;
for (sat::clause * const * it = begin; it != end; it++) {
checkpoint();
lits.reset();
sat::clause const & c = *(*it);
unsigned sz = c.size();
for (unsigned i = 0; i < sz; i++) {
lits.push_back(lit2expr(c[i]));
}
r.assert_expr(m.mk_or(lits.size(), lits.c_ptr()));
}
}
void operator()(sat::solver const & s, atom2bool_var const & map, assertion_set & r, model_converter_ref & mc) {
if (s.inconsistent()) {
r.assert_expr(m.mk_false());
return;
}
init_lit2expr(s, map, mc);
// collect units
unsigned num_vars = s.num_vars();
for (sat::bool_var v = 0; v < num_vars; v++) {
checkpoint();
switch (s.value(v)) {
case l_true:
r.assert_expr(lit2expr(sat::literal(v, false)));
break;
case l_false:
r.assert_expr(lit2expr(sat::literal(v, true)));
break;
case l_undef:
break;
}
}
// collect binary clauses
svector<sat::solver::bin_clause> bin_clauses;
s.collect_bin_clauses(bin_clauses, m_learned);
svector<sat::solver::bin_clause>::iterator it = bin_clauses.begin();
svector<sat::solver::bin_clause>::iterator end = bin_clauses.end();
for (; it != end; ++it) {
checkpoint();
r.assert_expr(m.mk_or(lit2expr(it->first), lit2expr(it->second)));
}
// collect clauses
assert_clauses(s.begin_clauses(), s.end_clauses(), r);
if (m_learned)
assert_clauses(s.begin_learned(), s.end_learned(), r);
}
void set_cancel(bool f) { m_cancel = f; }
};
sat2assertion_set::sat2assertion_set():m_imp(0) {
}
void sat2assertion_set::collect_param_descrs(param_descrs & r) {
insert_max_memory(r);
r.insert(":learned", CPK_BOOL, "(default: false) collect also learned clauses.");
}
struct sat2assertion_set::scoped_set_imp {
sat2assertion_set * m_owner;
scoped_set_imp(sat2assertion_set * o, sat2assertion_set::imp * i):m_owner(o) {
#pragma omp critical (sat2assertion_set)
{
m_owner->m_imp = i;
}
}
~scoped_set_imp() {
#pragma omp critical (sat2assertion_set)
{
m_owner->m_imp = 0;
}
}
};
void sat2assertion_set::operator()(sat::solver const & t, atom2bool_var const & m, params_ref const & p,
assertion_set & s, model_converter_ref & mc) {
imp proc(s.m(), p);
scoped_set_imp set(this, &proc);
proc(t, m, s, mc);
}
void sat2assertion_set::set_cancel(bool f) {
#pragma omp critical (sat2assertion_set)
{
if (m_imp)
m_imp->set_cancel(f);
}
}
// HACK introduced during code reorg.
// NOTE: the whole file will be deleted.
struct collect_boolean_interface_proc2 {
struct visitor {
obj_hashtable<expr> & m_r;
visitor(obj_hashtable<expr> & r):m_r(r) {}
void operator()(var * n) {}
void operator()(app * n) { if (is_uninterp_const(n)) m_r.insert(n); }
void operator()(quantifier * n) {}
};
ast_manager & m;
expr_fast_mark2 fvisited;
expr_fast_mark1 tvisited;
ptr_vector<expr> todo;
visitor proc;
collect_boolean_interface_proc2(ast_manager & _m, obj_hashtable<expr> & r):
m(_m),
proc(r) {
}
void process(expr * f) {
if (fvisited.is_marked(f))
return;
fvisited.mark(f);
todo.push_back(f);
while (!todo.empty()) {
expr * t = todo.back();
todo.pop_back();
if (is_uninterp_const(t))
continue;
if (is_app(t) && to_app(t)->get_family_id() == m.get_basic_family_id() && to_app(t)->get_num_args() > 0) {
decl_kind k = to_app(t)->get_decl_kind();
if (k == OP_OR || k == OP_NOT || k == OP_IFF || ((k == OP_EQ || k == OP_ITE) && m.is_bool(to_app(t)->get_arg(1)))) {
unsigned num = to_app(t)->get_num_args();
for (unsigned i = 0; i < num; i++) {
expr * arg = to_app(t)->get_arg(i);
if (fvisited.is_marked(arg))
continue;
fvisited.mark(arg);
todo.push_back(arg);
}
}
}
else {
quick_for_each_expr(proc, tvisited, t);
}
}
}
template<typename T>
void operator()(T const & g) {
unsigned sz = g.size();
for (unsigned i = 0; i < sz; i++)
process(g.form(i));
}
void operator()(unsigned sz, expr * const * fs) {
for (unsigned i = 0; i < sz; i++)
process(fs[i]);
}
};
template<typename T>
void collect_boolean_interface_core2(T const & s, obj_hashtable<expr> & r) {
collect_boolean_interface_proc2 proc(s.m(), r);
proc(s);
}
void collect_boolean_interface(assertion_set const & s, obj_hashtable<expr> & r) {
collect_boolean_interface_core2(s, r);
}

View file

@ -1,98 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
assertion_set2sat.h
Abstract:
"Compile" an assertion set into the SAT engine.
Atoms are "abstracted" into boolean variables.
The mapping between boolean variables and atoms
can be used to convert back the state of the
SAT engine into an assertion set.
The idea is to support scenarios such as:
1) simplify, blast, convert into SAT, and solve
2) convert into SAT, apply SAT for a while, learn new units, and translate back into an assertion set.
3) convert into SAT, apply SAT preprocessor (failed literal propagation, resolution, etc) and translate back into an assertion set.
4) Convert boolean structure into SAT, convert atoms into another engine, combine engines using lazy combination, solve.
Author:
Leonardo (leonardo) 2011-05-22
Notes:
--*/
#ifndef _ASSERTION_SET2SAT_H_
#define _ASSERTION_SET2SAT_H_
#include"assertion_set.h"
#include"sat_solver.h"
#include"strategy_exception.h"
#include"model_converter.h"
#include"atom2bool_var.h"
class assertion_set; // TODO: delete
void collect_boolean_interface(assertion_set const & s, obj_hashtable<expr> & r);
MK_ST_EXCEPTION(assertion_set2sat_exception);
class assertion_set2sat {
struct imp;
imp * m_imp;
struct scoped_set_imp;
public:
assertion_set2sat();
static void collect_param_descrs(param_descrs & r);
static bool has_unsupported_bool(assertion_set const & s);
/**
\brief "Compile" the assertion set into the given sat solver.
Store a mapping from atoms to boolean variables into m.
\remark m doesn't need to be empty. the definitions there are
reused.
*/
void operator()(assertion_set const & s, params_ref const & p, sat::solver & t, atom2bool_var & m);
/**
\brief "Compile" the formulas fs into the given sat solver.
Store a mapping from atoms to boolean variables into m.
\remark m doesn't need to be empty. the definitions there are
reused.
*/
void operator()(ast_manager & mng, unsigned num, expr * const * fs, params_ref const & p, sat::solver & t, atom2bool_var & m);
void operator()(ast_manager & mng, expr * f, params_ref const & p, sat::solver & t, atom2bool_var & m) { operator()(mng, 1, &f, p, t, m); }
void set_cancel(bool f);
};
MK_ST_EXCEPTION(sat2assertion_set_exception);
class sat2assertion_set {
struct imp;
imp * m_imp;
struct scoped_set_imp;
public:
sat2assertion_set();
static void collect_param_descrs(param_descrs & r);
/**
\brief Translate the state of the SAT engine back into an assertion set.
The SAT solver may use variables that are not in \c m. The translator
creates fresh boolean AST variables for them. They are stored in fvars.
*/
void operator()(sat::solver const & t, atom2bool_var const & m, params_ref const & p, assertion_set & s, model_converter_ref & mc);
void set_cancel(bool f);
};
#endif

View file

@ -1,187 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
sat_solver_strategy.cpp
Abstract:
Strategy for using the SAT solver preprocessing capabilities.
Author:
Leonardo (leonardo) 2011-05-23
Notes:
--*/
#include"sat_solver_strategy.h"
#include"assertion_set2sat.h"
#include"sat_solver.h"
#include"filter_model_converter.h"
#include"ast_smt2_pp.h"
#include"model_v2_pp.h"
struct sat_solver_strategy::imp {
ast_manager & m;
assertion_set2sat m_as2sat;
sat2assertion_set m_sat2as;
sat::solver m_solver;
params_ref m_params;
bool m_produce_models;
imp(ast_manager & _m, params_ref const & p):
m(_m),
m_solver(p, 0),
m_params(p) {
SASSERT(!m.proofs_enabled());
m_produce_models = p.get_bool(":produce-models", false);
}
void operator()(assertion_set & s, model_converter_ref & mc) {
if (s.m().proofs_enabled())
throw sat::solver_exception("sat solver does not support proofs");
TRACE("before_sat_solver", s.display(tout););
s.elim_redundancies();
if (s.inconsistent()) {
mc = 0;
return;
}
atom2bool_var map(m);
m_as2sat(s, m_params, m_solver, map);
TRACE("sat_solver_unknown", tout << "interpreted_atoms: " << map.interpreted_atoms() << "\n";
atom2bool_var::iterator it = map.begin();
atom2bool_var::iterator end = map.end();
for (; it != end; ++it) {
if (!is_uninterp_const(it->m_key))
tout << mk_ismt2_pp(it->m_key, m) << "\n";
});
s.reset();
s.m().compact_memory();
CASSERT("sat_solver", m_solver.check_invariant());
IF_VERBOSE(ST_VERBOSITY_LVL, m_solver.display_status(verbose_stream()););
TRACE("sat_dimacs", m_solver.display_dimacs(tout););
lbool r = m_solver.check();
if (r == l_false) {
mc = 0;
s.reset();
s.assert_expr(m.mk_false(), 0);
return;
}
else if (r == l_true && !map.interpreted_atoms()) {
// register model
model_ref md = alloc(model, m);
sat::model const & ll_m = m_solver.get_model();
TRACE("sat_solver_strategy", for (unsigned i = 0; i < ll_m.size(); i++) tout << i << ":" << ll_m[i] << " "; tout << "\n";);
atom2bool_var::iterator it = map.begin();
atom2bool_var::iterator end = map.end();
for (; it != end; ++it) {
expr * n = it->m_key;
sat::bool_var v = it->m_value;
TRACE("sat_solver_strategy", tout << "extracting value of " << mk_ismt2_pp(n, m) << "\nvar: " << v << "\n";);
switch (sat::value_at(v, ll_m)) {
case l_true:
md->register_decl(to_app(n)->get_decl(), m.mk_true());
break;
case l_false:
md->register_decl(to_app(n)->get_decl(), m.mk_false());
break;
default:
break;
}
}
s.reset();
TRACE("sat_solver_strategy", model_v2_pp(tout, *md););
mc = model2model_converter(md.get());
}
else {
// get simplified problem.
#if 0
IF_VERBOSE(ST_VERBOSITY_LVL, verbose_stream() << "\"formula constains interpreted atoms, recovering formula from sat solver...\"\n";);
#endif
m_solver.pop(m_solver.scope_lvl());
m_sat2as(m_solver, map, m_params, s, mc);
}
}
void set_cancel(bool f) {
m_as2sat.set_cancel(f);
m_sat2as.set_cancel(f);
m_solver.set_cancel(f);
}
};
sat_solver_strategy::sat_solver_strategy(ast_manager & m, params_ref const & p):
m_imp(0),
m_params(p) {
}
sat_solver_strategy::~sat_solver_strategy() {
SASSERT(m_imp == 0);
}
void sat_solver_strategy::updt_params(params_ref const & p) {
m_params = p;
}
void sat_solver_strategy::get_param_descrs(param_descrs & r) {
assertion_set2sat::collect_param_descrs(r);
sat2assertion_set::collect_param_descrs(r);
sat::solver::collect_param_descrs(r);
insert_produce_models(r);
}
struct sat_solver_strategy::scoped_set_imp {
sat_solver_strategy * m_owner;
scoped_set_imp(sat_solver_strategy * o, sat_solver_strategy::imp * i):m_owner(o) {
#pragma omp critical (sat_solver_strategy)
{
m_owner->m_imp = i;
}
}
~scoped_set_imp() {
#pragma omp critical (sat_solver_strategy)
{
m_owner->m_imp = 0;
}
}
};
void sat_solver_strategy::operator()(assertion_set & s, model_converter_ref & mc) {
imp proc(s.m(), m_params);
scoped_set_imp set(this, &proc);
try {
proc(s, mc);
proc.m_solver.collect_statistics(m_stats);
}
catch (sat::solver_exception & ex) {
proc.m_solver.collect_statistics(m_stats);
throw ex;
}
TRACE("sat_stats", m_stats.display_smt2(tout););
}
void sat_solver_strategy::cleanup() {
SASSERT(m_imp == 0);
}
void sat_solver_strategy::set_cancel(bool f) {
#pragma omp critical (sat_solver_strategy)
{
if (m_imp)
m_imp->set_cancel(f);
}
}
void sat_solver_strategy::reset_statistics() {
m_stats.reset();
}
void sat_solver_strategy::collect_statistics(statistics & st) const {
st.copy(m_stats);
}

View file

@ -1,65 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
sat_solver_strategy.h
Abstract:
Strategy for using the SAT solver.
If the assertion set contains theory atoms, then the sat solver just
checks whether the boolean abstraction is satisfiable or not.
Author:
Leonardo (leonardo) 2011-06-02
Notes:
--*/
#ifndef _SAT_SOLVER_STRATEGY_H_
#define _SAT_SOLVER_STRATEGY_H_
#include"assertion_set_strategy.h"
class assertion_set;
class sat_solver_strategy : public assertion_set_strategy {
struct imp;
imp * m_imp;
params_ref m_params;
statistics m_stats;
struct scoped_set_imp;
public:
sat_solver_strategy(ast_manager & m, params_ref const & p = params_ref());
virtual ~sat_solver_strategy();
virtual void updt_params(params_ref const & p);
static void get_param_descrs(param_descrs & r);
virtual void collect_param_descrs(param_descrs & r) { get_param_descrs(r); }
virtual void operator()(assertion_set & s, model_converter_ref & mc);
virtual void cleanup();
virtual void collect_statistics(statistics & st) const;
virtual void reset_statistics();
protected:
virtual void set_cancel(bool f);
};
inline as_st * mk_sat_solver(ast_manager & m, params_ref const & p = params_ref()) {
return clean(alloc(sat_solver_strategy, m, p));
}
// Apply only simplification procedures
inline as_st * mk_sat_preprocessor(ast_manager & m, params_ref const & p = params_ref()) {
params_ref p_aux;
p_aux.set_uint(":max-conflicts", 0);
as_st * st = clean(using_params(mk_sat_solver(m), p_aux));
st->updt_params(p);
return st;
}
#endif

View file

@ -1,116 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
atom2bool_var.cpp
Abstract:
The mapping between SAT boolean variables and atoms
Author:
Leonardo (leonardo) 2011-10-25
Notes:
--*/
#include"atom2bool_var.h"
#include"ast_smt2_pp.h"
#include"ref_util.h"
#include"goal.h"
void atom2bool_var::mk_inv(expr_ref_vector & lit2expr) const {
obj_map<expr, var>::iterator it = m_mapping.begin();
obj_map<expr, var>::iterator end = m_mapping.end();
for (; it != end; ++it) {
sat::literal l(static_cast<sat::bool_var>(it->m_value), false);
lit2expr.set(l.index(), it->m_key);
l.neg();
lit2expr.set(l.index(), m().mk_not(it->m_key));
}
}
sat::bool_var atom2bool_var::to_bool_var(expr * n) const {
sat::bool_var v = sat::null_bool_var;
m_mapping.find(n, v);
return v;
}
struct collect_boolean_interface_proc {
struct visitor {
obj_hashtable<expr> & m_r;
visitor(obj_hashtable<expr> & r):m_r(r) {}
void operator()(var * n) {}
void operator()(app * n) { if (is_uninterp_const(n)) m_r.insert(n); }
void operator()(quantifier * n) {}
};
ast_manager & m;
expr_fast_mark2 fvisited;
expr_fast_mark1 tvisited;
ptr_vector<expr> todo;
visitor proc;
collect_boolean_interface_proc(ast_manager & _m, obj_hashtable<expr> & r):
m(_m),
proc(r) {
}
void process(expr * f) {
if (fvisited.is_marked(f))
return;
fvisited.mark(f);
todo.push_back(f);
while (!todo.empty()) {
expr * t = todo.back();
todo.pop_back();
if (is_uninterp_const(t))
continue;
if (is_app(t) && to_app(t)->get_family_id() == m.get_basic_family_id() && to_app(t)->get_num_args() > 0) {
decl_kind k = to_app(t)->get_decl_kind();
if (k == OP_OR || k == OP_NOT || k == OP_IFF || ((k == OP_EQ || k == OP_ITE) && m.is_bool(to_app(t)->get_arg(1)))) {
unsigned num = to_app(t)->get_num_args();
for (unsigned i = 0; i < num; i++) {
expr * arg = to_app(t)->get_arg(i);
if (fvisited.is_marked(arg))
continue;
fvisited.mark(arg);
todo.push_back(arg);
}
}
}
else {
quick_for_each_expr(proc, tvisited, t);
}
}
}
template<typename T>
void operator()(T const & g) {
unsigned sz = g.size();
for (unsigned i = 0; i < sz; i++)
process(g.form(i));
}
void operator()(unsigned sz, expr * const * fs) {
for (unsigned i = 0; i < sz; i++)
process(fs[i]);
}
};
template<typename T>
void collect_boolean_interface_core(T const & s, obj_hashtable<expr> & r) {
collect_boolean_interface_proc proc(s.m(), r);
proc(s);
}
void collect_boolean_interface(goal const & g, obj_hashtable<expr> & r) {
collect_boolean_interface_core(g, r);
}
void collect_boolean_interface(ast_manager & m, unsigned num, expr * const * fs, obj_hashtable<expr> & r) {
collect_boolean_interface_proc proc(m, r);
proc(num, fs);
}

View file

@ -1,43 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
atom2bool_var.h
Abstract:
The mapping between SAT boolean variables and atoms
Author:
Leonardo (leonardo) 2011-10-25
Notes:
--*/
#ifndef _ATOM2BOOL_VAR_H_
#define _ATOM2BOOL_VAR_H_
#include"expr2var.h"
#include"sat_types.h"
/**
\brief Mapping from atoms into SAT boolean variables.
*/
class atom2bool_var : public expr2var {
public:
atom2bool_var(ast_manager & m):expr2var(m) {}
void insert(expr * n, sat::bool_var v) { expr2var::insert(n, v); }
sat::bool_var to_bool_var(expr * n) const;
void mk_inv(expr_ref_vector & lit2expr) const;
// return true if the mapping contains uninterpreted atoms.
bool interpreted_atoms() const { return expr2var::interpreted_vars(); }
};
class goal;
void collect_boolean_interface(goal const & g, obj_hashtable<expr> & r);
void collect_boolean_interface(ast_manager & m, unsigned num, expr * const * fs, obj_hashtable<expr> & r);
#endif

View file

@ -1,711 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
goal2sat.cpp
Abstract:
"Compile" a goal into the SAT engine.
Atoms are "abstracted" into boolean variables.
The mapping between boolean variables and atoms
can be used to convert back the state of the
SAT engine into a goal.
The idea is to support scenarios such as:
1) simplify, blast, convert into SAT, and solve
2) convert into SAT, apply SAT for a while, learn new units, and translate back into a goal.
3) convert into SAT, apply SAT preprocessor (failed literal propagation, resolution, etc) and translate back into a goal.
4) Convert boolean structure into SAT, convert atoms into another engine, combine engines using lazy combination, solve.
Author:
Leonardo (leonardo) 2011-10-26
Notes:
--*/
#include"goal2sat.h"
#include"ast_smt2_pp.h"
#include"ref_util.h"
#include"cooperate.h"
#include"filter_model_converter.h"
#include"model_evaluator.h"
#include"for_each_expr.h"
#include"model_v2_pp.h"
#include"tactic.h"
struct goal2sat::imp {
struct frame {
app * m_t;
unsigned m_root:1;
unsigned m_sign:1;
unsigned m_idx;
frame(app * t, bool r, bool s, unsigned idx):
m_t(t), m_root(r), m_sign(s), m_idx(idx) {}
};
ast_manager & m;
svector<frame> m_frame_stack;
svector<sat::literal> m_result_stack;
obj_map<app, sat::literal> m_cache;
obj_hashtable<expr> m_interface_vars;
sat::solver & m_solver;
atom2bool_var & m_map;
sat::bool_var m_true;
bool m_ite_extra;
unsigned long long m_max_memory;
volatile bool m_cancel;
imp(ast_manager & _m, params_ref const & p, sat::solver & s, atom2bool_var & map):
m(_m),
m_solver(s),
m_map(map) {
updt_params(p);
m_cancel = false;
m_true = sat::null_bool_var;
}
void updt_params(params_ref const & p) {
m_ite_extra = p.get_bool(":ite-extra", true);
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
}
void throw_op_not_handled() {
throw tactic_exception("operator not supported, apply simplifier before invoking translator");
}
void mk_clause(sat::literal l) {
TRACE("goal2sat", tout << "mk_clause: " << l << "\n";);
m_solver.mk_clause(1, &l);
}
void mk_clause(sat::literal l1, sat::literal l2) {
TRACE("goal2sat", tout << "mk_clause: " << l1 << " " << l2 << "\n";);
m_solver.mk_clause(l1, l2);
}
void mk_clause(sat::literal l1, sat::literal l2, sat::literal l3) {
TRACE("goal2sat", tout << "mk_clause: " << l1 << " " << l2 << " " << l3 << "\n";);
m_solver.mk_clause(l1, l2, l3);
}
void mk_clause(unsigned num, sat::literal * lits) {
TRACE("goal2sat", tout << "mk_clause: "; for (unsigned i = 0; i < num; i++) tout << lits[i] << " "; tout << "\n";);
m_solver.mk_clause(num, lits);
}
sat::bool_var mk_true() {
// create fake variable to represent true;
if (m_true == sat::null_bool_var) {
m_true = m_solver.mk_var();
mk_clause(sat::literal(m_true, false)); // v is true
}
return m_true;
}
void convert_atom(expr * t, bool root, bool sign) {
SASSERT(m.is_bool(t));
sat::literal l;
sat::bool_var v = m_map.to_bool_var(t);
if (v == sat::null_bool_var) {
if (m.is_true(t)) {
l = sat::literal(mk_true(), sign);
}
else if (m.is_false(t)) {
l = sat::literal(mk_true(), !sign);
}
else {
bool ext = !is_uninterp_const(t) || m_interface_vars.contains(t);
sat::bool_var v = m_solver.mk_var(ext);
m_map.insert(t, v);
l = sat::literal(v, sign);
TRACE("goal2sat", tout << "new_var: " << v << "\n" << mk_ismt2_pp(t, m) << "\n";);
}
}
else {
SASSERT(v != sat::null_bool_var);
l = sat::literal(v, sign);
}
SASSERT(l != sat::null_literal);
if (root)
mk_clause(l);
else
m_result_stack.push_back(l);
}
bool process_cached(app * t, bool root, bool sign) {
sat::literal l;
if (m_cache.find(t, l)) {
if (sign)
l.neg();
if (root)
mk_clause(l);
else
m_result_stack.push_back(l);
return true;
}
return false;
}
bool visit(expr * t, bool root, bool sign) {
if (!is_app(t)) {
convert_atom(t, root, sign);
return true;
}
if (process_cached(to_app(t), root, sign))
return true;
if (to_app(t)->get_family_id() != m.get_basic_family_id()) {
convert_atom(t, root, sign);
return true;
}
switch (to_app(t)->get_decl_kind()) {
case OP_NOT:
case OP_OR:
case OP_IFF:
m_frame_stack.push_back(frame(to_app(t), root, sign, 0));
return false;
case OP_ITE:
case OP_EQ:
if (m.is_bool(to_app(t)->get_arg(1))) {
m_frame_stack.push_back(frame(to_app(t), root, sign, 0));
return false;
}
convert_atom(t, root, sign);
return true;
case OP_AND:
case OP_XOR:
case OP_IMPLIES:
case OP_DISTINCT:
TRACE("goal2sat_not_handled", tout << mk_ismt2_pp(t, m) << "\n";);
throw_op_not_handled();
default:
convert_atom(t, root, sign);
return true;
}
}
void convert_or(app * t, bool root, bool sign) {
TRACE("goal2sat", tout << "convert_or:\n" << mk_ismt2_pp(t, m) << "\n";);
unsigned num = t->get_num_args();
if (root) {
SASSERT(num == m_result_stack.size());
if (sign) {
// this case should not really happen.
for (unsigned i = 0; i < num; i++) {
sat::literal l = m_result_stack[i];
l.neg();
mk_clause(l);
}
}
else {
mk_clause(m_result_stack.size(), m_result_stack.c_ptr());
m_result_stack.reset();
}
}
else {
SASSERT(num <= m_result_stack.size());
sat::bool_var k = m_solver.mk_var();
sat::literal l(k, false);
m_cache.insert(t, l);
sat::literal * lits = m_result_stack.end() - num;
for (unsigned i = 0; i < num; i++) {
mk_clause(~lits[i], l);
}
m_result_stack.push_back(~l);
lits = m_result_stack.end() - num - 1;
// remark: mk_clause may perform destructive updated to lits.
// I have to execute it after the binary mk_clause above.
mk_clause(num+1, lits);
unsigned old_sz = m_result_stack.size() - num - 1;
m_result_stack.shrink(old_sz);
if (sign)
l.neg();
m_result_stack.push_back(l);
}
}
void convert_ite(app * n, bool root, bool sign) {
unsigned sz = m_result_stack.size();
SASSERT(sz >= 3);
sat::literal c = m_result_stack[sz-3];
sat::literal t = m_result_stack[sz-2];
sat::literal e = m_result_stack[sz-1];
if (root) {
SASSERT(sz == 3);
if (sign) {
mk_clause(~c, ~t);
mk_clause(c, ~e);
}
else {
mk_clause(~c, t);
mk_clause(c, e);
}
m_result_stack.reset();
}
else {
sat::bool_var k = m_solver.mk_var();
sat::literal l(k, false);
m_cache.insert(n, l);
mk_clause(~l, ~c, t);
mk_clause(~l, c, e);
mk_clause(l, ~c, ~t);
mk_clause(l, c, ~e);
if (m_ite_extra) {
mk_clause(~t, ~e, l);
mk_clause(t, e, ~l);
}
m_result_stack.shrink(sz-3);
if (sign)
l.neg();
m_result_stack.push_back(l);
}
}
void convert_iff(app * t, bool root, bool sign) {
TRACE("goal2sat", tout << "convert_iff " << root << " " << sign << "\n" << mk_ismt2_pp(t, m) << "\n";);
unsigned sz = m_result_stack.size();
SASSERT(sz >= 2);
sat::literal l1 = m_result_stack[sz-1];
sat::literal l2 = m_result_stack[sz-2];
if (root) {
SASSERT(sz == 2);
if (sign) {
mk_clause(l1, l2);
mk_clause(~l1, ~l2);
}
else {
mk_clause(l1, ~l2);
mk_clause(~l1, l2);
}
m_result_stack.reset();
}
else {
sat::bool_var k = m_solver.mk_var();
sat::literal l(k, false);
m_cache.insert(t, l);
mk_clause(~l, l1, ~l2);
mk_clause(~l, ~l1, l2);
mk_clause(l, l1, l2);
mk_clause(l, ~l1, ~l2);
m_result_stack.shrink(sz-2);
if (sign)
l.neg();
m_result_stack.push_back(l);
}
}
void convert(app * t, bool root, bool sign) {
SASSERT(t->get_family_id() == m.get_basic_family_id());
switch (to_app(t)->get_decl_kind()) {
case OP_OR:
convert_or(t, root, sign);
break;
case OP_ITE:
convert_ite(t, root, sign);
break;
case OP_IFF:
case OP_EQ:
convert_iff(t, root, sign);
break;
default:
UNREACHABLE();
}
}
void process(expr * n) {
TRACE("goal2sat", tout << "converting: " << mk_ismt2_pp(n, m) << "\n";);
if (visit(n, true, false)) {
SASSERT(m_result_stack.empty());
return;
}
while (!m_frame_stack.empty()) {
loop:
cooperate("goal2sat");
if (m_cancel)
throw tactic_exception(TACTIC_CANCELED_MSG);
if (memory::get_allocation_size() > m_max_memory)
throw tactic_exception(TACTIC_MAX_MEMORY_MSG);
frame & fr = m_frame_stack.back();
app * t = fr.m_t;
bool root = fr.m_root;
bool sign = fr.m_sign;
TRACE("goal2sat_bug", tout << "result stack\n";
tout << mk_ismt2_pp(t, m) << " root: " << root << " sign: " << sign << "\n";
for (unsigned i = 0; i < m_result_stack.size(); i++) tout << m_result_stack[i] << " ";
tout << "\n";);
if (fr.m_idx == 0 && process_cached(t, root, sign)) {
m_frame_stack.pop_back();
continue;
}
if (m.is_not(t)) {
m_frame_stack.pop_back();
visit(t->get_arg(0), root, !sign);
continue;
}
unsigned num = t->get_num_args();
while (fr.m_idx < num) {
expr * arg = t->get_arg(fr.m_idx);
fr.m_idx++;
if (!visit(arg, false, false))
goto loop;
}
TRACE("goal2sat_bug", tout << "converting\n";
tout << mk_ismt2_pp(t, m) << " root: " << root << " sign: " << sign << "\n";
for (unsigned i = 0; i < m_result_stack.size(); i++) tout << m_result_stack[i] << " ";
tout << "\n";);
convert(t, root, sign);
m_frame_stack.pop_back();
}
SASSERT(m_result_stack.empty());
}
void operator()(goal const & g) {
m_interface_vars.reset();
collect_boolean_interface(g, m_interface_vars);
unsigned size = g.size();
for (unsigned idx = 0; idx < size; idx++) {
expr * f = g.form(idx);
process(f);
}
}
void operator()(unsigned sz, expr * const * fs) {
m_interface_vars.reset();
collect_boolean_interface(m, sz, fs, m_interface_vars);
for (unsigned i = 0; i < sz; i++)
process(fs[i]);
}
void set_cancel(bool f) { m_cancel = f; }
};
struct unsupported_bool_proc {
struct found {};
ast_manager & m;
unsupported_bool_proc(ast_manager & _m):m(_m) {}
void operator()(var *) {}
void operator()(quantifier *) {}
void operator()(app * n) {
if (n->get_family_id() == m.get_basic_family_id()) {
switch (n->get_decl_kind()) {
case OP_AND:
case OP_XOR:
case OP_IMPLIES:
case OP_DISTINCT:
throw found();
default:
break;
}
}
}
};
/**
\brief Return true if s contains an unsupported Boolean operator.
goal_rewriter (with the following configuration) can be used to
eliminate unsupported operators.
:elim-and true
:blast-distinct true
*/
bool goal2sat::has_unsupported_bool(goal const & g) {
return test<unsupported_bool_proc>(g);
}
goal2sat::goal2sat():m_imp(0) {
}
void goal2sat::collect_param_descrs(param_descrs & r) {
insert_max_memory(r);
r.insert(":ite-extra", CPK_BOOL, "(default: true) add redundant clauses (that improve unit propagation) when encoding if-then-else formulas");
}
struct goal2sat::scoped_set_imp {
goal2sat * m_owner;
scoped_set_imp(goal2sat * o, goal2sat::imp * i):m_owner(o) {
#pragma omp critical (goal2sat)
{
m_owner->m_imp = i;
}
}
~scoped_set_imp() {
#pragma omp critical (goal2sat)
{
m_owner->m_imp = 0;
}
}
};
void goal2sat::operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m) {
imp proc(g.m(), p, t, m);
scoped_set_imp set(this, &proc);
proc(g);
}
void goal2sat::set_cancel(bool f) {
#pragma omp critical (goal2sat)
{
if (m_imp)
m_imp->set_cancel(f);
}
}
struct sat2goal::imp {
// Wrapper for sat::model_converter: converts it into an "AST level" model_converter.
class sat_model_converter : public model_converter {
sat::model_converter m_mc;
// TODO: the following mapping is storing a lot of useless information, and may be a performance bottleneck.
// We need to save only the expressions associated with variables that occur in m_mc.
// This information may be stored as a vector of pairs.
// The mapping is only created during the model conversion.
expr_ref_vector m_var2expr;
ref<filter_model_converter> m_fmc; // filter for eliminating fresh variables introduced in the assertion-set --> sat conversion
sat_model_converter(ast_manager & m):
m_var2expr(m) {
}
public:
sat_model_converter(ast_manager & m, sat::solver const & s):m_var2expr(m) {
m_mc.copy(s.get_model_converter());
m_fmc = alloc(filter_model_converter, m);
}
ast_manager & m() { return m_var2expr.get_manager(); }
void insert(expr * atom, bool aux) {
m_var2expr.push_back(atom);
if (aux) {
SASSERT(is_uninterp_const(atom));
SASSERT(m().is_bool(atom));
m_fmc->insert(to_app(atom)->get_decl());
}
}
virtual void operator()(model_ref & md, unsigned goal_idx) {
SASSERT(goal_idx == 0);
TRACE("sat_mc", tout << "before sat_mc\n"; model_v2_pp(tout, *md); display(tout););
// REMARK: potential problem
// model_evaluator can't evaluate quantifiers. Then,
// an eliminated variable that depends on a quantified expression can't be recovered.
// A similar problem also affects any model_converter that uses elim_var_model_converter.
//
// Possible solution:
// model_converters reject any variable elimination that depends on a quantified expression.
model_evaluator ev(*md);
ev.set_model_completion(false);
// create a SAT model using md
sat::model sat_md;
unsigned sz = m_var2expr.size();
expr_ref val(m());
for (sat::bool_var v = 0; v < sz; v++) {
expr * atom = m_var2expr.get(v);
ev(atom, val);
if (m().is_true(val))
sat_md.push_back(l_true);
else if (m().is_false(val))
sat_md.push_back(l_false);
else
sat_md.push_back(l_undef);
}
// apply SAT model converter
m_mc(sat_md);
// register value of non-auxiliary boolean variables back into md
sz = m_var2expr.size();
for (sat::bool_var v = 0; v < sz; v++) {
expr * atom = m_var2expr.get(v);
if (is_uninterp_const(atom)) {
func_decl * d = to_app(atom)->get_decl();
lbool new_val = sat_md[v];
if (new_val == l_true)
md->register_decl(d, m().mk_true());
else if (new_val == l_false)
md->register_decl(d, m().mk_false());
}
}
// apply filter model converter
(*m_fmc)(md);
TRACE("sat_mc", tout << "after sat_mc\n"; model_v2_pp(tout, *md););
}
virtual model_converter * translate(ast_translation & translator) {
sat_model_converter * res = alloc(sat_model_converter, translator.to());
res->m_fmc = static_cast<filter_model_converter*>(m_fmc->translate(translator));
unsigned sz = m_var2expr.size();
for (unsigned i = 0; i < sz; i++)
res->m_var2expr.push_back(translator(m_var2expr.get(i)));
return res;
}
void display(std::ostream & out) {
out << "(sat-model-converter\n";
m_mc.display(out);
sat::bool_var_set vars;
m_mc.collect_vars(vars);
out << "(atoms";
unsigned sz = m_var2expr.size();
for (unsigned i = 0; i < sz; i++) {
if (vars.contains(i)) {
out << "\n (" << i << "\n " << mk_ismt2_pp(m_var2expr.get(i), m(), 2) << ")";
}
}
out << ")\n";
m_fmc->display(out);
out << ")\n";
}
};
ast_manager & m;
expr_ref_vector m_lit2expr;
unsigned long long m_max_memory;
bool m_learned;
volatile bool m_cancel;
imp(ast_manager & _m, params_ref const & p):m(_m), m_lit2expr(m), m_cancel(false) {
updt_params(p);
}
void updt_params(params_ref const & p) {
m_learned = p.get_bool(":learned", false);
m_max_memory = megabytes_to_bytes(p.get_uint(":max-memory", UINT_MAX));
}
void checkpoint() {
if (m_cancel)
throw tactic_exception(TACTIC_CANCELED_MSG);
if (memory::get_allocation_size() > m_max_memory)
throw tactic_exception(TACTIC_MAX_MEMORY_MSG);
}
void init_lit2expr(sat::solver const & s, atom2bool_var const & map, model_converter_ref & mc, bool produce_models) {
ref<sat_model_converter> _mc;
if (produce_models)
_mc = alloc(sat_model_converter, m, s);
unsigned num_vars = s.num_vars();
m_lit2expr.resize(num_vars * 2);
map.mk_inv(m_lit2expr);
sort * b = m.mk_bool_sort();
for (sat::bool_var v = 0; v < num_vars; v++) {
checkpoint();
sat::literal l(v, false);
if (m_lit2expr.get(l.index()) == 0) {
SASSERT(m_lit2expr.get((~l).index()) == 0);
app * aux = m.mk_fresh_const(0, b);
if (_mc)
_mc->insert(aux, true);
m_lit2expr.set(l.index(), aux);
m_lit2expr.set((~l).index(), m.mk_not(aux));
}
else {
if (_mc)
_mc->insert(m_lit2expr.get(l.index()), false);
SASSERT(m_lit2expr.get((~l).index()) != 0);
}
}
mc = _mc.get();
}
expr * lit2expr(sat::literal l) {
return m_lit2expr.get(l.index());
}
void assert_clauses(sat::clause * const * begin, sat::clause * const * end, goal & r) {
ptr_buffer<expr> lits;
for (sat::clause * const * it = begin; it != end; it++) {
checkpoint();
lits.reset();
sat::clause const & c = *(*it);
unsigned sz = c.size();
for (unsigned i = 0; i < sz; i++) {
lits.push_back(lit2expr(c[i]));
}
r.assert_expr(m.mk_or(lits.size(), lits.c_ptr()));
}
}
void operator()(sat::solver const & s, atom2bool_var const & map, goal & r, model_converter_ref & mc) {
if (s.inconsistent()) {
r.assert_expr(m.mk_false());
return;
}
init_lit2expr(s, map, mc, r.models_enabled());
// collect units
unsigned num_vars = s.num_vars();
for (sat::bool_var v = 0; v < num_vars; v++) {
checkpoint();
switch (s.value(v)) {
case l_true:
r.assert_expr(lit2expr(sat::literal(v, false)));
break;
case l_false:
r.assert_expr(lit2expr(sat::literal(v, true)));
break;
case l_undef:
break;
}
}
// collect binary clauses
svector<sat::solver::bin_clause> bin_clauses;
s.collect_bin_clauses(bin_clauses, m_learned);
svector<sat::solver::bin_clause>::iterator it = bin_clauses.begin();
svector<sat::solver::bin_clause>::iterator end = bin_clauses.end();
for (; it != end; ++it) {
checkpoint();
r.assert_expr(m.mk_or(lit2expr(it->first), lit2expr(it->second)));
}
// collect clauses
assert_clauses(s.begin_clauses(), s.end_clauses(), r);
if (m_learned)
assert_clauses(s.begin_learned(), s.end_learned(), r);
}
void set_cancel(bool f) { m_cancel = f; }
};
sat2goal::sat2goal():m_imp(0) {
}
void sat2goal::collect_param_descrs(param_descrs & r) {
insert_max_memory(r);
r.insert(":learned", CPK_BOOL, "(default: false) collect also learned clauses.");
}
struct sat2goal::scoped_set_imp {
sat2goal * m_owner;
scoped_set_imp(sat2goal * o, sat2goal::imp * i):m_owner(o) {
#pragma omp critical (sat2goal)
{
m_owner->m_imp = i;
}
}
~scoped_set_imp() {
#pragma omp critical (sat2goal)
{
m_owner->m_imp = 0;
}
}
};
void sat2goal::operator()(sat::solver const & t, atom2bool_var const & m, params_ref const & p,
goal & g, model_converter_ref & mc) {
imp proc(g.m(), p);
scoped_set_imp set(this, &proc);
proc(t, m, g, mc);
}
void sat2goal::set_cancel(bool f) {
#pragma omp critical (sat2goal)
{
if (m_imp)
m_imp->set_cancel(f);
}
}

View file

@ -1,86 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
goal2sat.h
Abstract:
"Compile" a goal into the SAT engine.
Atoms are "abstracted" into boolean variables.
The mapping between boolean variables and atoms
can be used to convert back the state of the
SAT engine into a goal.
The idea is to support scenarios such as:
1) simplify, blast, convert into SAT, and solve
2) convert into SAT, apply SAT for a while, learn new units, and translate back into a goal.
3) convert into SAT, apply SAT preprocessor (failed literal propagation, resolution, etc) and translate back into a goal.
4) Convert boolean structure into SAT, convert atoms into another engine, combine engines using lazy combination, solve.
Author:
Leonardo (leonardo) 2011-10-26
Notes:
--*/
#ifndef _GOAL2SAT_H_
#define _GOAL2SAT_H_
#include"goal.h"
#include"sat_solver.h"
#include"model_converter.h"
#include"atom2bool_var.h"
class goal2sat {
struct imp;
imp * m_imp;
struct scoped_set_imp;
public:
goal2sat();
static void collect_param_descrs(param_descrs & r);
static bool has_unsupported_bool(goal const & s);
/**
\brief "Compile" the goal into the given sat solver.
Store a mapping from atoms to boolean variables into m.
\remark m doesn't need to be empty. the definitions there are
reused.
\warning conversion throws a tactic_exception, if it is interrupted (by set_cancel),
an unsupported operator is found, or memory consumption limit is reached (set with param :max-memory).
*/
void operator()(goal const & g, params_ref const & p, sat::solver & t, atom2bool_var & m);
void set_cancel(bool f);
};
class sat2goal {
struct imp;
imp * m_imp;
struct scoped_set_imp;
public:
sat2goal();
static void collect_param_descrs(param_descrs & r);
/**
\brief Translate the state of the SAT engine back into a goal.
The SAT solver may use variables that are not in \c m. The translator
creates fresh boolean AST variables for them. They are stored in fvars.
\warning conversion throws a tactic_exception, if it is interrupted (by set_cancel),
or memory consumption limit is reached (set with param :max-memory).
*/
void operator()(sat::solver const & t, atom2bool_var const & m, params_ref const & p, goal & s, model_converter_ref & mc);
void set_cancel(bool f);
};
#endif

View file

@ -1,218 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
sat_tactic.cpp
Abstract:
Tactic for using the SAT solver and its preprocessing capabilities.
Author:
Leonardo (leonardo) 2011-10-25
Notes:
--*/
#include"tactical.h"
#include"goal2sat.h"
#include"sat_solver.h"
#include"filter_model_converter.h"
#include"ast_smt2_pp.h"
#include"model_v2_pp.h"
class sat_tactic : public tactic {
struct imp {
ast_manager & m;
goal2sat m_goal2sat;
sat2goal m_sat2goal;
sat::solver m_solver;
params_ref m_params;
imp(ast_manager & _m, params_ref const & p):
m(_m),
m_solver(p, 0),
m_params(p) {
SASSERT(!m.proofs_enabled());
}
void operator()(goal_ref const & g,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc,
expr_dependency_ref & core) {
mc = 0; pc = 0; core = 0;
fail_if_proof_generation("sat", g);
fail_if_unsat_core_generation("sat", g);
bool produce_models = g->models_enabled();
TRACE("before_sat_solver", g->display(tout););
g->elim_redundancies();
atom2bool_var map(m);
m_goal2sat(*g, m_params, m_solver, map);
TRACE("sat_solver_unknown", tout << "interpreted_atoms: " << map.interpreted_atoms() << "\n";
atom2bool_var::iterator it = map.begin();
atom2bool_var::iterator end = map.end();
for (; it != end; ++it) {
if (!is_uninterp_const(it->m_key))
tout << mk_ismt2_pp(it->m_key, m) << "\n";
});
g->reset();
g->m().compact_memory();
CASSERT("sat_solver", m_solver.check_invariant());
IF_VERBOSE(TACTIC_VERBOSITY_LVL, m_solver.display_status(verbose_stream()););
TRACE("sat_dimacs", m_solver.display_dimacs(tout););
lbool r = m_solver.check();
if (r == l_false) {
g->assert_expr(m.mk_false(), 0, 0);
}
else if (r == l_true && !map.interpreted_atoms()) {
// register model
if (produce_models) {
model_ref md = alloc(model, m);
sat::model const & ll_m = m_solver.get_model();
TRACE("sat_tactic", for (unsigned i = 0; i < ll_m.size(); i++) tout << i << ":" << ll_m[i] << " "; tout << "\n";);
atom2bool_var::iterator it = map.begin();
atom2bool_var::iterator end = map.end();
for (; it != end; ++it) {
expr * n = it->m_key;
sat::bool_var v = it->m_value;
TRACE("sat_tactic", tout << "extracting value of " << mk_ismt2_pp(n, m) << "\nvar: " << v << "\n";);
switch (sat::value_at(v, ll_m)) {
case l_true:
md->register_decl(to_app(n)->get_decl(), m.mk_true());
break;
case l_false:
md->register_decl(to_app(n)->get_decl(), m.mk_false());
break;
default:
break;
}
}
TRACE("sat_tactic", model_v2_pp(tout, *md););
mc = model2model_converter(md.get());
}
}
else {
// get simplified problem.
#if 0
IF_VERBOSE(TACTIC_VERBOSITY_LVL, verbose_stream() << "\"formula constains interpreted atoms, recovering formula from sat solver...\"\n";);
#endif
m_solver.pop(m_solver.scope_lvl());
m_sat2goal(m_solver, map, m_params, *(g.get()), mc);
}
g->inc_depth();
result.push_back(g.get());
}
void set_cancel(bool f) {
m_goal2sat.set_cancel(f);
m_sat2goal.set_cancel(f);
m_solver.set_cancel(f);
}
};
struct scoped_set_imp {
sat_tactic * m_owner;
scoped_set_imp(sat_tactic * o, imp * i):m_owner(o) {
#pragma omp critical (sat_tactic)
{
m_owner->m_imp = i;
}
}
~scoped_set_imp() {
#pragma omp critical (sat_tactic)
{
m_owner->m_imp = 0;
}
}
};
imp * m_imp;
params_ref m_params;
statistics m_stats;
public:
sat_tactic(ast_manager & m, params_ref const & p):
m_imp(0),
m_params(p) {
}
virtual tactic * translate(ast_manager & m) {
return alloc(sat_tactic, m, m_params);
}
virtual ~sat_tactic() {
SASSERT(m_imp == 0);
}
virtual void updt_params(params_ref const & p) {
m_params = p;
}
virtual void collect_param_descrs(param_descrs & r) {
goal2sat::collect_param_descrs(r);
sat2goal::collect_param_descrs(r);
sat::solver::collect_param_descrs(r);
}
void operator()(goal_ref const & g,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc,
expr_dependency_ref & core) {
imp proc(g->m(), m_params);
scoped_set_imp set(this, &proc);
try {
proc(g, result, mc, pc, core);
proc.m_solver.collect_statistics(m_stats);
}
catch (sat::solver_exception & ex) {
proc.m_solver.collect_statistics(m_stats);
throw tactic_exception(ex.msg());
}
TRACE("sat_stats", m_stats.display_smt2(tout););
}
virtual void cleanup() {
SASSERT(m_imp == 0);
}
virtual void collect_statistics(statistics & st) const {
st.copy(m_stats);
}
virtual void reset_statistics() {
m_stats.reset();
}
protected:
virtual void set_cancel(bool f) {
#pragma omp critical (sat_tactic)
{
if (m_imp)
m_imp->set_cancel(f);
}
}
};
tactic * mk_sat_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(sat_tactic, m, p));
}
tactic * mk_sat_preprocessor_tactic(ast_manager & m, params_ref const & p) {
params_ref p_aux;
p_aux.set_uint(":max-conflicts", 0);
tactic * t = clean(using_params(mk_sat_tactic(m, p), p_aux));
t->updt_params(p);
return t;
}

View file

@ -1,30 +0,0 @@
/*++
Copyright (c) 2011 Microsoft Corporation
Module Name:
sat_tactic.cpp
Abstract:
Tactic for using the SAT solver and its preprocessing capabilities.
Author:
Leonardo (leonardo) 2011-10-26
Notes:
--*/
#ifndef _SAT_TACTIC_H_
#define _SAT_TACTIC_H_
#include"params.h"
class ast_manager;
class tactic;
tactic * mk_sat_tactic(ast_manager & m, params_ref const & p = params_ref());
tactic * mk_sat_preprocessor_tactic(ast_manager & m, params_ref const & p = params_ref());
#endif