/*++ Copyright (c) 2010 Microsoft Corporation Module Name: qe_bool_plugin.cpp Abstract: Eliminate Boolean variable from formula Author: Nikolaj Bjorner (nbjorner) 2010-02-19 Revision History: Notes: The procedure is a bit naive. Consider a co-factoring approach that eliminates all Boolean variables in scope in one shot, similar to what we do with BDDs. --*/ #include "qe/qe.h" #include "ast/rewriter/expr_safe_replace.h" #include "ast/ast_pp.h" #include "model/model_evaluator.h" namespace qe { class bool_plugin : public qe_solver_plugin { expr_safe_replace m_replace; public: bool_plugin(i_solver_context& ctx, ast_manager& m): qe_solver_plugin(m, m.get_basic_family_id(), ctx), m_replace(m) {} void assign(contains_app& x, expr* fml, rational const& vl) override { SASSERT(vl.is_zero() || vl.is_one()); } bool get_num_branches(contains_app& x, expr* fml, rational& nb) override { nb = rational(2); return true; } void subst(contains_app& x, rational const& vl, expr_ref& fml, expr_ref* def) override { SASSERT(vl.is_one() || vl.is_zero()); expr* tf = (vl.is_one())?m.mk_true():m.mk_false(); m_replace.apply_substitution(x.x(), tf, fml); if (def) { *def = tf; } } bool project(contains_app& x, model_ref& model, expr_ref& fml) override { model_evaluator model_eval(*model); expr_ref val_x(m); rational val; model_eval(x.x(), val_x); CTRACE("qe", (!m.is_true(val_x) && !m.is_false(val_x)), tout << "Boolean is a don't care: " << mk_pp(x.x(), m) << "\n";); val = m.is_true(val_x)?rational::one():rational::zero(); subst(x, val, fml, nullptr); return true; } unsigned get_weight(contains_app& contains_x, expr* fml) override { app* x = contains_x.x(); bool p = m_ctx.pos_atoms().contains(x); bool n = m_ctx.neg_atoms().contains(x); if (p && n) { return 3; } return 0; } bool solve(conj_enum& conjs,expr* fml) override { return solve_units(conjs, fml) || solve_polarized(fml); } bool is_uninterpreted(app* a) override { return false; } private: bool solve_units(conj_enum& conjs, expr* _fml) { expr_ref fml(_fml, m); unsigned idx; for (expr * e : conjs) { if (!is_app(e)) { continue; } app* a = to_app(e); expr* e1; if (m_ctx.is_var(a, idx)) { m_replace.apply_substitution(a, m.mk_true(), fml); m_ctx.elim_var(idx, fml, m.mk_true()); return true; } else if (m.is_not(e, e1) && m_ctx.is_var(e1, idx)) { m_replace.apply_substitution(to_app(e1), m.mk_false(), fml); m_ctx.elim_var(idx, fml, m.mk_false()); return true; } } return false; } bool solve_polarized(expr* _fml) { unsigned num_vars = m_ctx.get_num_vars(); expr_ref fml(_fml, m), def(m); for (unsigned i = 0; i < num_vars; ++i) { if (m.is_bool(m_ctx.get_var(i)) && solve_polarized(m_ctx.contains(i), fml, def)) { m_ctx.elim_var(i, fml, def); return true; } } return false; } bool solve_polarized( contains_app& contains_x, expr_ref& fml, expr_ref& def) { app* x = contains_x.x(); bool p = m_ctx.pos_atoms().contains(x); bool n = m_ctx.neg_atoms().contains(x); TRACE("quant_elim", tout << mk_pp(x, m) << " " << mk_pp(fml, m) << "\n";); if (p && n) { return false; } else if (p && !n) { for (expr* y : m_ctx.pos_atoms()) { if (x != y && contains_x(y)) return false; } for (expr* y : m_ctx.neg_atoms()) { if (contains_x(y)) return false; } // only occurrences of 'x' must be in positive atoms def = m.mk_true(); m_replace.apply_substitution(x, def, fml); return true; } else if (!p && n) { for (expr* y : m_ctx.pos_atoms()) { if (contains_x(y)) return false; } for (expr* y : m_ctx.neg_atoms()) { if (x != y && contains_x(y)) return false; } def = m.mk_false(); m_replace.apply_substitution(x, def, fml); return true; } else if (contains_x(fml)) { return false; } else { def = m.mk_false(); return true; } } }; qe_solver_plugin* mk_bool_plugin(i_solver_context& ctx) { return alloc(bool_plugin, ctx, ctx.get_manager()); } }