diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index a74f87142..735079350 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -2038,11 +2038,13 @@ app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const } } } + check_args(decl, num_args, new_args.c_ptr()); SASSERT(new_args.size() == num_args); new_node = new (mem) app(decl, num_args, new_args.c_ptr()); r = register_node(new_node); } else { + check_args(decl, num_args, args); new_node = new (mem) app(decl, num_args, args); r = register_node(new_node); } @@ -2064,6 +2066,22 @@ app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const return r; } +void ast_manager::check_args(func_decl* f, unsigned n, expr* const* es) { + for (unsigned i = 0; i < n; i++) { + sort * actual_sort = get_sort(es[i]); + sort * expected_sort = f->is_associative() ? f->get_domain(0) : f->get_domain(i); + if (expected_sort != actual_sort) { + std::ostringstream buffer; + buffer << "Sort mismatch at argument #" << (i+1) + << " for function " << mk_pp(f,*this) + << " supplied sort is " + << mk_pp(actual_sort, *this); + throw ast_exception(buffer.str().c_str()); + } + } +} + + inline app * ast_manager::mk_app_core(func_decl * decl, expr * arg1, expr * arg2) { expr * args[2] = { arg1, arg2 }; return mk_app_core(decl, 2, args); diff --git a/src/ast/ast.h b/src/ast/ast.h index 214668536..c1ee603c7 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1460,6 +1460,9 @@ protected: bool coercion_needed(func_decl * decl, unsigned num_args, expr * const * args); + void check_args(func_decl* f, unsigned n, expr* const* es); + + public: ast_manager(proof_gen_mode = PGM_DISABLED, char const * trace_file = 0, bool is_format_manager = false); ast_manager(proof_gen_mode, std::fstream * trace_stream, bool is_format_manager = false); diff --git a/src/ast/pb_decl_plugin.cpp b/src/ast/pb_decl_plugin.cpp index 36103c4f0..f4e3fdf97 100644 --- a/src/ast/pb_decl_plugin.cpp +++ b/src/ast/pb_decl_plugin.cpp @@ -280,3 +280,9 @@ bool pb_util::has_unit_coefficients(func_decl* f) const { } return true; } + +app* pb_util::mk_fresh_bool() { + symbol name = m.mk_fresh_var_name("pb"); + func_decl_info info(m_fid, OP_PB_AUX_BOOL, 0, 0); + return m.mk_const(m.mk_func_decl(name, 0, (sort *const*)0, m.mk_bool_sort(), info)); +} diff --git a/src/ast/pb_decl_plugin.h b/src/ast/pb_decl_plugin.h index f38f577b3..d4264b9b9 100644 --- a/src/ast/pb_decl_plugin.h +++ b/src/ast/pb_decl_plugin.h @@ -35,6 +35,7 @@ enum pb_op_kind { OP_PB_LE, // pseudo-Boolean <= (generalizes at_most_k) OP_PB_GE, // pseudo-Boolean >= OP_PB_EQ, // equality + OP_PB_AUX_BOOL, // auxiliary internal Boolean variable. LAST_PB_OP }; @@ -71,6 +72,7 @@ public: virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range); virtual void get_op_names(svector & op_names, symbol const & logic); + }; @@ -101,6 +103,7 @@ public: bool is_ge(func_decl* a) const; bool is_ge(expr* a) const { return is_app(a) && is_ge(to_app(a)->get_decl()); } bool is_ge(expr* a, rational& k) const; + bool is_aux_bool(expr* e) const { return is_app_of(e, m_fid, OP_PB_AUX_BOOL); } rational get_coeff(expr* a, unsigned index) const { return get_coeff(to_app(a)->get_decl(), index); } rational get_coeff(func_decl* a, unsigned index) const; bool has_unit_coefficients(func_decl* f) const; @@ -111,6 +114,8 @@ public: bool is_eq(expr* e) const { return is_app(e) && is_eq(to_app(e)->get_decl()); } bool is_eq(expr* e, rational& k) const; + app* mk_fresh_bool(); + private: rational to_rational(parameter const& p) const; diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index d50dd06d6..d7d96b2d1 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -31,7 +31,6 @@ Notes: #include "propagate_values_tactic.h" #include "solve_eqs_tactic.h" #include "elim_uncnstr_tactic.h" -#include "elim_term_ite_tactic.h" #include "tactical.h" #include "model_smt2_pp.h" #include "card2bv_tactic.h" @@ -652,7 +651,6 @@ namespace opt { and_then(mk_simplify_tactic(m), mk_propagate_values_tactic(m), mk_solve_eqs_tactic(m), - mk_elim_term_ite_tactic(m), // NB: mk_elim_uncstr_tactic(m) is not sound with soft constraints mk_simplify_tactic(m)); opt_params optp(m_params); @@ -660,14 +658,16 @@ namespace opt { if (optp.elim_01()) { tac2 = mk_elim01_tactic(m); tac3 = mk_lia2card_tactic(m); - tac4 = mk_elim_term_ite_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(), tac4.get())); + set_simplify(and_then(tac0.get(), tac2.get(), tac3.get(), mk_simplify_tactic(m))); } else { - set_simplify(tac0.get()); + tactic_ref tac1 = + and_then(tac0.get(), + mk_simplify_tactic(m)); + set_simplify(tac1.get()); } proof_converter_ref pc; expr_dependency_ref core(m); @@ -875,9 +875,11 @@ namespace opt { TRACE("opt", tout << "maxsat: " << id << " offset:" << offset << "\n";); } else if (is_maximize(fml, tr, orig_term, index)) { + purify(tr); m_objectives[index].m_term = tr; } else if (is_minimize(fml, tr, orig_term, index)) { + purify(tr); m_objectives[index].m_term = tr; m_objectives[index].m_adjust_value.set_negate(true); } @@ -887,6 +889,50 @@ namespace opt { } } + void context::purify(app_ref& term) { + filter_model_converter_ref fm; + if (m_arith.is_add(term)) { + expr_ref_vector args(m); + unsigned sz = term->get_num_args(); + for (unsigned i = 0; i < sz; ++i) { + expr* arg = term->get_arg(i); + if (is_mul_const(arg)) { + args.push_back(arg); + } + else { + args.push_back(purify(fm, arg)); + } + } + term = m_arith.mk_add(args.size(), args.c_ptr()); + } + else if (m_arith.is_arith_expr(term) && !is_mul_const(term)) { + TRACE("opt", tout << "Purifying " << term << "\n";); + term = purify(fm, term); + } + if (fm) { + m_model_converter = concat(m_model_converter.get(), fm.get()); + } + } + + bool context::is_mul_const(expr* e) { + expr* e1, *e2; + return + is_uninterp_const(e) || + m_arith.is_numeral(e) || + (m_arith.is_mul(e, e1, e2) && m_arith.is_numeral(e1) && is_uninterp_const(e2)) || + (m_arith.is_mul(e, e2, e1) && m_arith.is_numeral(e1) && is_uninterp_const(e2)); + } + + app* context::purify(filter_model_converter_ref& fm, expr* term) { + std::ostringstream out; + out << mk_pp(term, m); + app* q = m.mk_fresh_const(out.str().c_str(), m.get_sort(term)); + if (!fm) fm = alloc(filter_model_converter, m); + m_hard_constraints.push_back(m.mk_eq(q, term)); + fm->insert(q->get_decl()); + return q; + } + /** To select the proper theory solver we have to ensure that all theory symbols from soft constraints are reflected in the hard constraints. @@ -894,7 +940,7 @@ namespace opt { - filter "obj" from generated model. */ void context::mk_atomic(expr_ref_vector& terms) { - ref fm; + filter_model_converter_ref fm; for (unsigned i = 0; i < terms.size(); ++i) { expr_ref p(terms[i].get(), m); app_ref q(m); @@ -902,11 +948,7 @@ namespace opt { terms[i] = p; } else { - q = m.mk_fresh_const("obj", m.mk_bool_sort()); - terms[i] = q; - m_hard_constraints.push_back(m.mk_iff(p, q)); - if (!fm) fm = alloc(filter_model_converter, m); - fm->insert(q->get_decl()); + terms[i] = purify(fm, p); } } if (fm) { diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index 315e2473a..d17cc3748 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -244,6 +244,9 @@ namespace opt { bool is_maxsat(expr* fml, expr_ref_vector& terms, vector& weights, rational& offset, bool& neg, symbol& id, unsigned& index); + void purify(app_ref& term); + app* purify(filter_model_converter_ref& fm, expr* e); + bool is_mul_const(expr* e); expr* mk_maximize(unsigned index, app* t); expr* mk_minimize(unsigned index, app* t); expr* mk_maxsat(unsigned index, unsigned num_fmls, expr* const* fmls); diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index 7be53ff86..c82076e87 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -410,18 +410,24 @@ namespace smt { } bool theory_pb::internalize_atom(app * atom, bool gate_ctx) { - SASSERT(m_util.is_at_most_k(atom) || m_util.is_le(atom) || - m_util.is_ge(atom) || m_util.is_at_least_k(atom) || - m_util.is_eq(atom)); - context& ctx = get_context(); if (ctx.b_internalized(atom)) { return false; } - SASSERT(!ctx.b_internalized(atom)); m_stats.m_num_predicates++; + if (m_util.is_aux_bool(atom)) { + bool_var abv = ctx.mk_bool_var(atom); + ctx.set_var_theory(abv, get_id()); + return true; + } + SASSERT(m_util.is_at_most_k(atom) || m_util.is_le(atom) || + m_util.is_ge(atom) || m_util.is_at_least_k(atom) || + m_util.is_eq(atom)); + + + unsigned num_args = atom->get_num_args(); bool_var abv = ctx.mk_bool_var(atom); ctx.set_var_theory(abv, get_id()); @@ -617,15 +623,15 @@ namespace smt { // function internalize, where enodes for each argument // is available. if (!has_bv) { - expr_ref tmp(m), fml(m); - tmp = m.mk_fresh_const("pb_proxy",m.mk_bool_sort()); + app_ref tmp(m), fml(m); + pb_util pb(m); + tmp = pb.mk_fresh_bool(); fml = m.mk_iff(tmp, arg); TRACE("pb", tout << "create proxy " << fml << "\n";); ctx.internalize(fml, false); SASSERT(ctx.b_internalized(tmp)); bv = ctx.get_bool_var(tmp); - SASSERT(null_theory_var == ctx.get_var_theory(bv)); - ctx.set_var_theory(bv, get_id()); + SASSERT(get_id() == ctx.get_var_theory(bv)); literal lit(ctx.get_bool_var(fml)); ctx.mk_th_axiom(get_id(), 1, &lit); ctx.mark_as_relevant(tmp.get()); @@ -1148,17 +1154,19 @@ namespace smt { context& ctx; ast_manager& m; theory_pb& th; + pb_util pb; typedef smt::literal literal; typedef smt::literal_vector literal_vector; psort_expr(context& c, theory_pb& th): ctx(c), m(c.get_manager()), - th(th) {} + th(th), + pb(m) {} literal fresh() { app_ref y(m); - y = m.mk_fresh_const("y", m.mk_bool_sort()); + y = pb.mk_fresh_bool(); return literal(ctx.mk_bool_var(y)); } diff --git a/src/tactic/filter_model_converter.h b/src/tactic/filter_model_converter.h index 0113e9fd9..6fc8fdd77 100644 --- a/src/tactic/filter_model_converter.h +++ b/src/tactic/filter_model_converter.h @@ -45,4 +45,6 @@ public: virtual model_converter * translate(ast_translation & translator); }; +typedef ref filter_model_converter_ref; + #endif