/*++ Copyright (c) 2006 Microsoft Corporation Module Name: dl_mk_interp_tail_simplifier.cpp Abstract: Rule transformer which simplifies interpreted tails Author: Krystof Hoder (t-khoder) 2011-10-01. Revision History: --*/ #include #include"ast_pp.h" #include"bool_rewriter.h" #include"rewriter.h" #include"rewriter_def.h" #include"dl_mk_rule_inliner.h" #include"dl_mk_interp_tail_simplifier.h" namespace datalog { // ----------------------------------- // // mk_interp_tail_simplifier::rule_substitution // // ----------------------------------- void mk_interp_tail_simplifier::rule_substitution::reset(rule * r) { unsigned var_cnt = m_context.get_rule_manager().get_var_counter().get_max_var(*r)+1; m_subst.reset(); m_subst.reserve(1, var_cnt); m_rule = r; } bool mk_interp_tail_simplifier::rule_substitution::unify(expr * e1, expr * e2) { SASSERT(m_rule); //we need to apply the current substitution in order to ensure the unifier //works in an incremental way expr_ref e1_s(m); expr_ref e2_s(m); m_subst.apply(e1,e1_s); m_subst.apply(e2,e2_s); //and we need to reset the cache as we're going to modify the substitution m_subst.reset_cache(); return m_unif (e1_s, e2_s, m_subst, false); } void mk_interp_tail_simplifier::rule_substitution::apply(app * a, app_ref& res) { SASSERT(m_rule); expr_ref res_e(m); m_subst.apply(a, res_e); SASSERT(is_app(res_e.get())); res = to_app(res_e.get()); } void mk_interp_tail_simplifier::rule_substitution::get_result(rule_ref & res) { SASSERT(m_rule); app_ref new_head(m); apply(m_rule->get_head(), new_head); app_ref_vector tail(m); svector tail_neg; unsigned tail_len = m_rule->get_tail_size(); for (unsigned i=0; iget_tail(i), new_tail_el); tail.push_back(new_tail_el); tail_neg.push_back(m_rule->is_neg_tail(i)); } mk_rule_inliner::remove_duplicate_tails(tail, tail_neg); SASSERT(tail.size() == tail_neg.size()); res = m_context.get_rule_manager().mk(new_head, tail.size(), tail.c_ptr(), tail_neg.c_ptr()); res->set_accounting_parent_object(m_context, m_rule); res->norm_vars(res.get_manager()); } // ----------------------------------- // // mk_interp_tail_simplifier // // ----------------------------------- class mk_interp_tail_simplifier::normalizer_cfg : public default_rewriter_cfg { struct expr_cmp { ast_manager& m; expr_cmp(ast_manager& m) : m(m) {} bool operator()(expr * ae, expr * be) { return cmp_expr(ae, be, 4) == -1; } template static int cmp(T a, T b) { return (a>b) ? 1 : ((a == b) ? 0 : -1); } int cmp_expr(expr * ae, expr * be, int depth) { if (ae == be) { return 0; } //remove negations bool a_neg = m.is_not(ae, ae); bool b_neg = m.is_not(be, be); if (ae==be) { return cmp(a_neg, b_neg); } if (!is_app(ae) && !is_app(be)) { return cmp(ae->get_id(), be->get_id()); } if (!is_app(ae)) { return -1; } if (!is_app(be)) { return 1; } app * a = to_app(ae); app * b = to_app(be); if (a->get_decl()!=b->get_decl()) { return cmp(a->get_decl()->get_id(), b->get_decl()->get_id()); } if (a->get_num_args()!=b->get_num_args()) { return cmp(a->get_num_args(), b->get_num_args()); } if (depth==0) { return cmp(a->get_id(),b->get_id()); } unsigned arg_cnt = a->get_num_args(); unsigned neg_comparison = 0; for (unsigned i=0; iget_arg(i); expr * arg_b = b->get_arg(i); //we normalize away negations bool a_is_neg = m.is_not(arg_a, arg_a); bool b_is_neg = m.is_not(arg_b, arg_b); if (neg_comparison==0 && a_is_neg!=b_is_neg) { neg_comparison = a_is_neg ? -1 : 1; } int res = cmp_expr(arg_a, arg_b, depth-1); if (res!=0) { return res; } } if (neg_comparison!=0) { return neg_comparison; } //by normalizing away negation we may have put non-equal terms to be equal, so here we check return cmp(a->get_id(),b->get_id()); } }; ast_manager& m; bool_rewriter m_brwr; //instead of a local variable expr_ref_vector m_app_args; expr_cmp m_expr_cmp; public: normalizer_cfg(ast_manager& m) : m(m), m_brwr(m), m_app_args(m), m_expr_cmp(m) { } static void remove_duplicates(expr_ref_vector& v) { expr * a = v[0].get(); unsigned read_idx = 1; unsigned write_idx = 1; for (;;) { while(read_idx arg_pair; bool match_arg_pair(expr * e, arg_pair& pair, bool seek_conjunction) { if (seek_conjunction) { return m.is_and(e, pair.first, pair.second); } else { return m.is_or(e, pair.first, pair.second); } } /** If inside_disjunction is false, we're inside a conjunction (and arg pairs represent disjunctions). */ app * detect_equivalence(const arg_pair& p1, const arg_pair& p2, bool inside_disjunction) { if (m.is_not(p1.first)==m.is_not(p2.first)) { return 0; } if (m.is_not(p1.second)==m.is_not(p2.second)) { return 0; } expr * first_bare = 0; if (m.is_not(p1.first, first_bare) && p2.first!=first_bare) { return 0; } if (m.is_not(p2.first, first_bare) && p1.first!=first_bare) { return 0; } SASSERT(first_bare); expr * second_bare = 0; if (m.is_not(p1.second, second_bare) && p2.second!=second_bare) { return 0; } if (m.is_not(p2.second, second_bare) && p1.second!=second_bare) { return 0; } SASSERT(second_bare); if (!m.is_bool(first_bare) || !m.is_bool(second_bare)) { return 0; } //both negations are in the same pair bool negs_together = m.is_not(p1.first)==m.is_not(p1.second); if (negs_together==inside_disjunction) { return m.mk_eq(first_bare, second_bare); } else { return m.mk_eq(first_bare, m.mk_not(second_bare)); } } bool detect_equivalences(expr_ref_vector& v, bool inside_disjunction) { bool have_pair = false; unsigned prev_pair_idx; arg_pair ap; unsigned read_idx = 0; unsigned write_idx = 0; while(read_idx rwr(m, false, r_cfg); expr_ref dl_form_e(m); rwr(simp1_res.get(), res); /*if (simp1_res.get()!=res.get()) { std::cout<<"pre norm:\n"<get_uninterpreted_tail_size(); unsigned len = r->get_tail_size(); if (u_len==len) { return false; } ptr_vector todo; for (unsigned i=u_len; iget_tail(i)); SASSERT(!r->is_neg_tail(i)); } m_rule_subst.reset(r); bool found_something = false; #define TRY_UNIFY(_x,_y) if (m_rule_subst.unify(_x,_y)) { found_something = true; } #define IS_FLEX(_x) (is_var(_x) || m.is_value(_x)) while (!todo.empty()) { expr * arg1, *arg2; expr * t0 = todo.back(); todo.pop_back(); expr* t = t0; bool neg = m.is_not(t, t); if (is_var(t)) { TRY_UNIFY(t, neg ? m.mk_false() : m.mk_true()); } else if (!neg && m.is_and(t)) { app* a = to_app(t); todo.append(a->get_num_args(), a->get_args()); } else if (!neg && m.is_eq(t, arg1, arg2) && IS_FLEX(arg1) && IS_FLEX(arg2)) { TRY_UNIFY(arg1, arg2); } else if (m.is_iff(t, arg1, arg2)) { //determine the polarity of the equivalence and remove the negations while (m.is_not(arg1, arg1)) neg = !neg; while (m.is_not(arg2, arg2)) neg = !neg; if (!is_var(arg1)) { std::swap(arg1, arg2); } if (!IS_FLEX(arg1) || !IS_FLEX(arg2)) { // no-op } else if (is_var(arg1) && !neg) { TRY_UNIFY(arg1, arg2); } else if (is_var(arg1) && neg && m.is_true(arg2)) { TRY_UNIFY(arg1, m.mk_false()); } else if (is_var(arg1) && neg && m.is_false(arg2)) { TRY_UNIFY(arg1, m.mk_true()); } } } if (!found_something) { return false; } TRACE("dl_interp_tail_simplifier_propagation_pre", tout << "will propagate rule:\n"; r->display(m_context, tout); ); m_rule_subst.get_result(res); TRACE("dl_interp_tail_simplifier_propagation", tout << "propagated equivalences of:\n"; r->display(m_context, tout); tout << "into:\n"; res->display(m_context, tout); ); return true; } bool mk_interp_tail_simplifier::transform_rule(rule * r0, rule_ref & res) { rule_ref r(r0, m_context.get_rule_manager()); start: unsigned u_len = r->get_uninterpreted_tail_size(); unsigned len = r->get_tail_size(); if (u_len==len) { res = r; return true; } app_ref head(r->get_head(), m); app_ref_vector tail(m); svector tail_neg; for (unsigned i=0; iget_tail(i)); tail_neg.push_back(r->is_neg_tail(i)); } bool modified = false; app_ref itail(m); if (u_len+1==len) { //we have only one interpreted tail itail = r->get_tail(u_len); SASSERT(!r->is_neg_tail(u_len)); } else { expr_ref_vector itail_members(m); for (unsigned i=u_len; iget_tail(i)); SASSERT(!r->is_neg_tail(i)); } itail = m.mk_and(itail_members.size(), itail_members.c_ptr()); modified = true; } expr_ref simp_res(m); simplify_expr(itail.get(), simp_res); modified |= itail.get()!=simp_res.get(); if (is_app(simp_res.get())) { itail = to_app(simp_res.get()); } else if (m.is_bool(simp_res)) { itail = m.mk_eq(simp_res, m.mk_true()); } else { throw default_exception("simplification resulted in non-boolean non-function"); } if (m.is_false(itail.get())) { //the tail member is never true, so we may delete the rule TRACE("dl", r->display(m_context, tout << "rule in infeasible\n");); return false; } if (!m.is_true(itail.get())) { //if the simplified tail is not a tautology, we add it to the rule tail.push_back(itail); tail_neg.push_back(false); } else { modified = true; } SASSERT(tail.size() == tail_neg.size()); if (modified) { res = m_context.get_rule_manager().mk(head, tail.size(), tail.c_ptr(), tail_neg.c_ptr()); res->set_accounting_parent_object(m_context, r); } else { res = r; } rule_ref pro_var_eq_result(m_context.get_rule_manager()); if (propagate_variable_equivalences(res, pro_var_eq_result)) { SASSERT(var_counter().get_max_var(*r.get())==0 || var_counter().get_max_var(*r.get()) > var_counter().get_max_var(*pro_var_eq_result.get())); r = pro_var_eq_result; goto start; } CTRACE("dl", (res != r0), r0->display(m_context, tout << "old:\n"); res->display(m_context, tout << "new:\n");); return true; } bool mk_interp_tail_simplifier::transform_rules(const rule_set & orig, rule_set & tgt) { bool modified = false; rule_set::iterator rit = orig.begin(); rule_set::iterator rend = orig.end(); for (; rit!=rend; ++rit) { rule_ref new_rule(m_context.get_rule_manager()); if (transform_rule(*rit, new_rule)) { bool is_modified = *rit != new_rule; modified |= is_modified; tgt.add_rule(new_rule); } else { modified = true; } } return modified; } rule_set * mk_interp_tail_simplifier::operator()(rule_set const & source, model_converter_ref& mc, proof_converter_ref& pc) { // TODO mc, pc if (source.get_num_rules() == 0) { return 0; } rule_set * res = alloc(rule_set, m_context); if (!transform_rules(source, *res)) { dealloc(res); res = 0; } return res; } };