From 980e74b4fffeefa7efb07ff41cc10d2b9c534970 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner <nbjorner@microsoft.com> Date: Thu, 20 Aug 2015 06:39:11 -0700 Subject: [PATCH] add tactic to recognize small discrete domains and convert them into bit-vectors Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com> --- src/opt/opt_context.cpp | 5 +- src/tactic/arith/eq2bv_tactic.cpp | 333 ++++++++++++++++++++++++++++++ src/tactic/arith/eq2bv_tactic.h | 34 +++ 3 files changed, 370 insertions(+), 2 deletions(-) create mode 100644 src/tactic/arith/eq2bv_tactic.cpp create mode 100644 src/tactic/arith/eq2bv_tactic.h diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 857e0ae7e..86c5447a9 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -34,7 +34,7 @@ Notes: #include "tactical.h" #include "model_smt2_pp.h" #include "card2bv_tactic.h" -#include "nnf_tactic.h" +#include "eq2bv_tactic.h" #include "inc_sat_solver.h" #include "bv_decl_plugin.h" #include "pb_decl_plugin.h" @@ -667,10 +667,11 @@ namespace opt { if (optp.elim_01()) { tac2 = mk_elim01_tactic(m); tac3 = mk_lia2card_tactic(m); + tac4 = mk_eq2bv_tactic(m); params_ref lia_p; lia_p.set_bool("compile_equality", optp.pb_compile_equality()); tac3->updt_params(lia_p); - set_simplify(and_then(tac0.get(), tac2.get(), tac3.get(), mk_simplify_tactic(m))); + set_simplify(and_then(tac0.get(), tac2.get(), tac3.get(), tac4.get(), mk_simplify_tactic(m))); } else { tactic_ref tac1 = diff --git a/src/tactic/arith/eq2bv_tactic.cpp b/src/tactic/arith/eq2bv_tactic.cpp new file mode 100644 index 000000000..fda51f0fa --- /dev/null +++ b/src/tactic/arith/eq2bv_tactic.cpp @@ -0,0 +1,333 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + eq2bv_tactic.cpp + +Abstract: + + Extract integer variables that are used as finite domain indicators. + The integer variables can only occur in equalities. + +Author: + + Nikolaj Bjorner (nbjorner) 2015-8-19 + +Notes: + +--*/ +#include"tactical.h" +#include"cooperate.h" +#include"bound_manager.h" +#include"ast_pp.h" +#include"arith_decl_plugin.h" +#include"bv_decl_plugin.h" +#include"rewriter_def.h" +#include"ast_util.h" +#include"ast_pp_util.h" + +class eq2bv_tactic : public tactic { + struct eq_rewriter_cfg : public default_rewriter_cfg { + ast_manager& m; + eq2bv_tactic& t; + + bool is_fd(expr* x, expr* y, expr_ref& result) { + expr* z; + rational r; + if (t.m_fd.find(x, z) && t.a.is_numeral(y, r)) { + result = m.mk_eq(z, t.bv.mk_numeral(r, m.get_sort(z))); + return true; + } + else { + return false; + } + } + + br_status mk_app_core(func_decl* f, unsigned sz, expr*const* es, expr_ref& result) { + if (m.is_eq(f)) { + if (is_fd(es[0], es[1], result)) { + return BR_DONE; + } + else if (is_fd(es[1], es[0], result)) { + return BR_DONE; + } + } + return BR_FAILED; + } + + bool rewrite_patterns() const { return false; } + bool flat_assoc(func_decl * f) const { return false; } + br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) { + result_pr = 0; + return mk_app_core(f, num, args, result); + } + eq_rewriter_cfg(eq2bv_tactic& t):m(t.m), t(t) {} + }; + + class eq_rewriter : public rewriter_tpl<eq_rewriter_cfg> { + eq_rewriter_cfg m_cfg; + public: + eq_rewriter(eq2bv_tactic& t): + rewriter_tpl<eq_rewriter_cfg>(t.m, false, m_cfg), + m_cfg(t) + {} + }; + + class bvmc : public model_converter { + obj_map<func_decl, func_decl*> m_map; + public: + + void insert(func_decl* c_new, func_decl* c_old) { + m_map.insert(c_new, c_old); + } + + virtual void operator()(model_ref& mdl) { + ast_manager& m = mdl->get_manager(); + bv_util bv(m); + arith_util a(m); + rational r; + model_ref new_m = alloc(model, m); + new_m->copy_func_interps(*mdl); + new_m->copy_usort_interps(*mdl); + unsigned sz = mdl->get_num_constants(), bvsz; + for (unsigned i = 0; i < sz; ++i) { + func_decl* f = mdl->get_constant(i), *g; + expr* val = mdl->get_const_interp(f); + if (m_map.find(f, g) && bv.is_numeral(val, r, bvsz)) { + val = a.mk_numeral(r, true); + new_m->register_decl(g, val); + } + else { + new_m->register_decl(f, val); + } + } + mdl = new_m; + } + + virtual model_converter* translate(ast_translation & translator) { + bvmc* v = alloc(bvmc); + obj_map<func_decl, func_decl*>::iterator it = m_map.begin(), end = m_map.end(); + for (; it != end; ++it) { + v->m_map.insert(translator(it->m_key), translator(it->m_value)); + } + return v; + } + }; + +public: + ast_manager & m; + arith_util a; + bv_util bv; + eq_rewriter m_rw; + expr_ref_vector m_trail; + bound_manager m_bounds; + obj_map<expr, expr*> m_fd; + obj_map<expr, unsigned> m_max; + expr_mark m_nonfd; + ptr_vector<expr> m_todo; + + eq2bv_tactic(ast_manager & _m): + m(_m), + a(m), + bv(m), + m_rw(*this), + m_trail(m), + m_bounds(m) { + } + + virtual ~eq2bv_tactic() { + } + + void set_cancel(bool f) { + m_rw.set_cancel(f); + } + + void updt_params(params_ref const & p) { + } + + virtual void operator()( + goal_ref const & g, + goal_ref_buffer & result, + model_converter_ref & mc, + proof_converter_ref & pc, + expr_dependency_ref & core) { + SASSERT(g->is_well_sorted()); + mc = 0; pc = 0; core = 0; + m_trail.reset(); + m_fd.reset(); + m_max.reset(); + m_nonfd.reset(); + m_bounds.reset(); + ref<bvmc> mc1 = alloc(bvmc); + + tactic_report report("eq2bv", *g); + + m_bounds(*g); + + for (unsigned i = 0; i < g->size(); i++) { + collect_fd(g->form(i)); + } + cleanup_fd(mc1); + + if (m_max.empty()) { + result.push_back(g.get()); + return; + } + + for (unsigned i = 0; i < g->size(); i++) { + expr_ref new_curr(m); + proof_ref new_pr(m); + if (is_bound(g->form(i))) { + g->update(i, m.mk_true(), 0, 0); + continue; + } + m_rw(g->form(i), new_curr, new_pr); + if (m.proofs_enabled() && !new_pr) { + new_pr = m.mk_rewrite(g->form(i), new_curr); + new_pr = m.mk_modus_ponens(g->pr(i), new_pr); + } + g->update(i, new_curr, new_pr, g->dep(i)); + } + obj_map<expr, unsigned>::iterator it = m_max.begin(), end = m_max.end(); + for (; it != end; ++it) { + expr* c = it->m_key; + bool strict; + rational r; + if (m_bounds.has_lower(c, r, strict)) { + SASSERT(!strict); + expr* d = m_fd.find(c); + g->assert_expr(bv.mk_ule(bv.mk_numeral(r, m.get_sort(d)), d), m_bounds.lower_dep(c)); + } + if (m_bounds.has_upper(c, r, strict)) { + SASSERT(!strict); + expr* d = m_fd.find(c); + g->assert_expr(bv.mk_ule(d, bv.mk_numeral(r, m.get_sort(d))), m_bounds.upper_dep(c)); + } + } + g->inc_depth(); + mc = mc1.get(); + result.push_back(g.get()); + TRACE("pb", g->display(tout);); + SASSERT(g->is_well_sorted()); + } + + + virtual tactic * translate(ast_manager & m) { + return alloc(eq2bv_tactic, m); + } + + virtual void collect_param_descrs(param_descrs & r) { + } + + virtual void cleanup() { + } + + void cleanup_fd(ref<bvmc>& mc) { + SASSERT(m_fd.empty()); + ptr_vector<expr> rm; + obj_map<expr, unsigned>::iterator it = m_max.begin(), end = m_max.end(); + for (; it != end; ++it) { + if (m_nonfd.is_marked(it->m_key)) { + rm.push_back(it->m_key); + } + } + for (unsigned i = 0; i < rm.size(); ++i) { + m_max.erase(rm[i]); + } + it = m_max.begin(); + end = m_max.end(); + for (; it != end; ++it) { + // ensure there are enough elements. + bool strict; + rational val; + if (m_bounds.has_upper(it->m_key, val, strict)) { + SASSERT(!strict); + if (val.get_unsigned() > it->m_value) it->m_value = val.get_unsigned(); + } + else { + ++it->m_value; + } + unsigned p = next_power_of_two(it->m_value); + if (p <= 1) p = 2; + unsigned n = log2(p); + app* z = m.mk_fresh_const("z", bv.mk_sort(n)); + m_trail.push_back(z); + m_fd.insert(it->m_key, z); + mc->insert(z->get_decl(), to_app(it->m_key)->get_decl()); + } + } + + bool is_upper(expr* f) { + expr* e1, *e2; + rational r; + if ((a.is_le(f, e1, e2) || a.is_ge(f, e2, e1)) && + is_uninterp_const(e1) && a.is_numeral(e2, r) && r.is_unsigned() && !m_nonfd.is_marked(e1)) { + SASSERT(m_bounds.has_upper(e1)); + return true; + } + return false; + } + + bool is_lower(expr* f) { + expr* e1, *e2; + rational r; + if ((a.is_le(f, e1, e2) || a.is_ge(f, e2, e1)) && + is_uninterp_const(e2) && a.is_numeral(e1, r) && r.is_unsigned() && !m_nonfd.is_marked(e2)) { + SASSERT(m_bounds.has_lower(e2)); + return true; + } + return false; + } + + bool is_bound(expr* f) { + return is_lower(f) || is_upper(f); + } + + + void collect_fd(expr* f) { + if (is_bound(f)) return; + m_todo.push_back(f); + while (!m_todo.empty()) { + f = m_todo.back(); + m_todo.pop_back(); + if (m_nonfd.is_marked(f)) { + continue; + } + m_nonfd.mark(f, true); + expr* e1, *e2; + if (m.is_eq(f, e1, e2)) { + if (is_fd(e1, e2)) { + continue; + } + if (is_fd(e2, e1)) { + continue; + } + } + m_todo.append(to_app(f)->get_num_args(), to_app(f)->get_args()); + } + } + + bool is_fd(expr* v, expr* c) { + unsigned val; + rational r; + if (is_uninterp_const(v) && a.is_numeral(c, r) && !m_nonfd.is_marked(v) && a.is_int(v) && r.is_unsigned()) { + val = r.get_unsigned(); + add_fd(v, val); + return true; + } + return false; + } + + void add_fd(expr* c, unsigned val) { + unsigned val2; + if (!m_max.find(c, val2) || val2 < val) { + m_max.insert(c, val); + } + } +}; + +tactic * mk_eq2bv_tactic(ast_manager & m) { + return clean(alloc(eq2bv_tactic, m)); +} + diff --git a/src/tactic/arith/eq2bv_tactic.h b/src/tactic/arith/eq2bv_tactic.h new file mode 100644 index 000000000..e336b1e0a --- /dev/null +++ b/src/tactic/arith/eq2bv_tactic.h @@ -0,0 +1,34 @@ +/*++ +Copyright (c) 2015 Microsoft Corporation + +Module Name: + + eq2bv_tactic.h + +Abstract: + + Extract integer variables that are used as finite domain indicators. + The integer variables can only occur in equalities. + +Author: + + Nikolaj Bjorner (nbjorner) 2015-8-19 + +Notes: + +--*/ +#ifndef EQ2BV_TACTIC_H_ +#define EQ2BV_TACTIC_H_ + +#include"params.h" +class ast_manager; +class tactic; + +tactic * mk_eq2bv_tactic(ast_manager & m); + +/* + ADD_TACTIC("eq2bv", "convert integer variables used as finite domain elements to bit-vectors.", "mk_eq2bv_tactic(m)") +*/ + + +#endif