From 3b82604590078d570bbfe03550eb839dbfc3d245 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 1 Apr 2016 15:37:40 +0100 Subject: [PATCH 01/66] whitespace --- src/model/func_interp.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/model/func_interp.h b/src/model/func_interp.h index b264f4f1d..7ad2fefd8 100644 --- a/src/model/func_interp.h +++ b/src/model/func_interp.h @@ -12,7 +12,7 @@ Abstract: modulo a model. Main goal: Remove some code duplication and make the evaluator more efficient. - + Example of code duplication that existed in Z3: - smt_model_generator was creating func_values that were essentially partial func_interps - smt_model_generator was creating if-then-else (lambda) exprs representing func_values @@ -39,17 +39,17 @@ class func_entry { bool m_args_are_values; //!< true if is_value(m_args[i]) is true for all i in [0, arity) // m_result and m_args[i] must be ground terms. - + expr * m_result; expr * m_args[]; static unsigned get_obj_size(unsigned arity) { return sizeof(func_entry) + arity * sizeof(expr*); } func_entry(ast_manager & m, unsigned arity, expr * const * args, expr * result); - + friend class func_interp; - + void set_result(ast_manager & m, expr * r); - + public: static func_entry * mk(ast_manager & m, unsigned arity, expr * const * args, expr * result); bool args_are_values() const { return m_args_are_values; } @@ -69,7 +69,7 @@ class func_interp { ptr_vector m_entries; expr * m_else; bool m_args_are_values; //!< true if forall e in m_entries e.args_are_values() == true - + expr * m_interp; //!< cache for representing the whole interpretation as a single expression (it uses ite terms). void reset_interp_cache(); @@ -83,7 +83,7 @@ public: ast_manager & m () const { return m_manager; } func_interp * copy() const; - + unsigned get_arity() const { return m_arity; } bool is_partial() const { return m_else == 0; } @@ -95,7 +95,7 @@ public: expr * get_else() const { return m_else; } void set_else(expr * e); - + void insert_entry(expr * const * args, expr * r); void insert_new_entry(expr * const * args, expr * r); func_entry * get_entry(expr * const * args) const; From ccd18283e7d3b5b26fe21bdbc4cacc958d5894f9 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 1 Apr 2016 15:38:38 +0100 Subject: [PATCH 02/66] Moved extension_converter func_interp entry compression to func_interp. Relates to #547 --- src/model/func_interp.cpp | 55 +++++++++++++++++++++--- src/model/func_interp.h | 3 ++ src/tactic/extension_model_converter.cpp | 44 +------------------ src/tactic/extension_model_converter.h | 3 -- 4 files changed, 53 insertions(+), 52 deletions(-) diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index 90810f294..45bcd7780 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -104,11 +104,54 @@ void func_interp::reset_interp_cache() { m_manager.dec_ref(m_interp); m_interp = 0; } - + +bool func_interp::is_fi_entry_expr(expr * e, ptr_vector & args) { + args.reset(); + if (!is_app(e) || !m().is_ite(to_app(e))) + return false; + + app * a = to_app(e); + expr * c = a->get_arg(0); + expr * t = a->get_arg(1); + expr * f = a->get_arg(2); + + if ((m_arity == 0) || + (m_arity == 1 && (!m().is_eq(c) || to_app(c)->get_num_args() != 2)) || + (m_arity > 1 && (!m().is_and(c) || to_app(c)->get_num_args() != m_arity))) + return false; + + args.resize(m_arity, 0); + for (unsigned i = 0; i < m_arity; i++) { + expr * ci = (m_arity == 1 && i == 0) ? to_app(c) : to_app(c)->get_arg(i); + + if (!m().is_eq(ci) || to_app(ci)->get_num_args() != 2) + return false; + + expr * a0 = to_app(ci)->get_arg(0); + expr * a1 = to_app(ci)->get_arg(1); + if (is_var(a0) && to_var(a0)->get_idx() == i) + args[i] = a1; + else if (is_var(a1) && to_var(a1)->get_idx() == i) + args[i] = a0; + else + return false; + } + + return true; +} + void func_interp::set_else(expr * e) { reset_interp_cache(); - m_manager.inc_ref(e); m_manager.dec_ref(m_else); + + ptr_vector args; + while (is_fi_entry_expr(e, args)) { + TRACE("func_interp", tout << "fi entry expr: " << mk_ismt2_pp(e, m()) << std::endl;); + insert_entry(args.c_ptr(), to_app(e)->get_arg(1)); + e = to_app(e)->get_arg(2); + } + + m_manager.inc_ref(e); m_else = e; } @@ -148,7 +191,7 @@ func_entry * func_interp::get_entry(expr * const * args) const { void func_interp::insert_entry(expr * const * args, expr * r) { reset_interp_cache(); - func_entry * entry = get_entry(args); + func_entry * entry = get_entry(args); if (entry != 0) { entry->set_result(m_manager, r); return; @@ -201,7 +244,7 @@ expr * func_interp::get_max_occ_result() const { for (; it != end; ++it) { func_entry * curr = *it; expr * r = curr->get_result(); - unsigned occs = 0; + unsigned occs = 0; num_occs.find(r, occs); occs++; num_occs.insert(r, occs); @@ -283,13 +326,13 @@ expr * func_interp::get_interp() const { return r; } -func_interp * func_interp::translate(ast_translation & translator) const { +func_interp * func_interp::translate(ast_translation & translator) const { func_interp * new_fi = alloc(func_interp, translator.to(), m_arity); ptr_vector::const_iterator it = m_entries.begin(); ptr_vector::const_iterator end = m_entries.end(); for (; it != end; ++it) { - func_entry * curr = *it; + func_entry * curr = *it; ptr_buffer new_args; for (unsigned i=0; iget_arg(i))); diff --git a/src/model/func_interp.h b/src/model/func_interp.h index 7ad2fefd8..d0d61546e 100644 --- a/src/model/func_interp.h +++ b/src/model/func_interp.h @@ -110,6 +110,9 @@ public: expr * get_interp() const; func_interp * translate(ast_translation & translator) const; + +private: + bool is_fi_entry_expr(expr * e, ptr_vector & args); }; #endif diff --git a/src/tactic/extension_model_converter.cpp b/src/tactic/extension_model_converter.cpp index ebd530a58..cdd096455 100644 --- a/src/tactic/extension_model_converter.cpp +++ b/src/tactic/extension_model_converter.cpp @@ -43,41 +43,6 @@ static void display_decls_info(std::ostream & out, model_ref & md) { } } -bool extension_model_converter::is_fi_entry_expr(expr * e, unsigned arity, ptr_vector & args) { - args.reset(); - if (!is_app(e) || !m().is_ite(to_app(e))) - return false; - - app * a = to_app(e); - expr * c = a->get_arg(0); - expr * t = a->get_arg(1); - expr * f = a->get_arg(2); - - if ((arity == 0) || - (arity == 1 && (!m().is_eq(c) || to_app(c)->get_num_args() != 2)) || - (arity > 1 && (!m().is_and(c) || to_app(c)->get_num_args() != arity))) - return false; - - args.resize(arity, 0); - for (unsigned i = 0; i < arity; i++) { - expr * ci = (arity == 1 && i == 0) ? to_app(c) : to_app(c)->get_arg(i); - - if (!m().is_eq(ci) || to_app(ci)->get_num_args() != 2) - return false; - - expr * a0 = to_app(ci)->get_arg(0); - expr * a1 = to_app(ci)->get_arg(1); - if (is_var(a0) && to_var(a0)->get_idx() == i) - args[i] = a1; - else if (is_var(a1) && to_var(a1)->get_idx() == i) - args[i] = a0; - else - return false; - } - - return true; -} - void extension_model_converter::operator()(model_ref & md, unsigned goal_idx) { SASSERT(goal_idx == 0); TRACE("extension_mc", model_v2_pp(tout, *md); display_decls_info(tout, md);); @@ -97,14 +62,7 @@ void extension_model_converter::operator()(model_ref & md, unsigned goal_idx) { } else { func_interp * new_fi = alloc(func_interp, m(), arity); - expr * e = val.get(); - ptr_vector args; - while (is_fi_entry_expr(e, arity, args)) { - TRACE("extension_mc", tout << "fi entry: " << mk_ismt2_pp(e, m()) << std::endl;); - new_fi->insert_entry(args.c_ptr(), to_app(e)->get_arg(1)); - e = to_app(e)->get_arg(2); - } - new_fi->set_else(e); + new_fi->set_else(val); md->register_decl(f, new_fi); } } diff --git a/src/tactic/extension_model_converter.h b/src/tactic/extension_model_converter.h index 9431906f3..46644eec2 100644 --- a/src/tactic/extension_model_converter.h +++ b/src/tactic/extension_model_converter.h @@ -43,9 +43,6 @@ public: void insert(func_decl * v, expr * def); virtual model_converter * translate(ast_translation & translator); - -private: - bool is_fi_entry_expr(expr * e, unsigned arity, ptr_vector & args); }; From e527aca29619d241a869c142bf75729ef466a2b0 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 6 Apr 2016 15:39:32 +0100 Subject: [PATCH 03/66] Bugfix for unspecified else-case in func_interps. --- src/model/func_interp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index 45bcd7780..7103c495d 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -145,7 +145,7 @@ void func_interp::set_else(expr * e) { m_manager.dec_ref(m_else); ptr_vector args; - while (is_fi_entry_expr(e, args)) { + while (e && is_fi_entry_expr(e, args)) { TRACE("func_interp", tout << "fi entry expr: " << mk_ismt2_pp(e, m()) << std::endl;); insert_entry(args.c_ptr(), to_app(e)->get_arg(1)); e = to_app(e)->get_arg(2); From 3a532c08a680beb1e5c8ba48a489cb87817a9d70 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 6 Apr 2016 19:24:08 +0100 Subject: [PATCH 04/66] Bugfix for func_interp else-case compression --- src/model/func_interp.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index 7103c495d..5c53e64ba 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -141,8 +141,10 @@ bool func_interp::is_fi_entry_expr(expr * e, ptr_vector & args) { } void func_interp::set_else(expr * e) { + if (e == m_else) + return; + reset_interp_cache(); - m_manager.dec_ref(m_else); ptr_vector args; while (e && is_fi_entry_expr(e, args)) { @@ -152,6 +154,7 @@ void func_interp::set_else(expr * e) { } m_manager.inc_ref(e); + m_manager.dec_ref(m_else); m_else = e; } From 5971c2065351888af7d9468195ce6eb8c1f606ae Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 7 Apr 2016 13:08:17 +0100 Subject: [PATCH 05/66] Bugfixes for bv_trailing. --- src/ast/rewriter/bv_trailing.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ast/rewriter/bv_trailing.cpp b/src/ast/rewriter/bv_trailing.cpp index d3087e776..414854f3a 100644 --- a/src/ast/rewriter/bv_trailing.cpp +++ b/src/ast/rewriter/bv_trailing.cpp @@ -194,8 +194,7 @@ struct bv_trailing::imp { return 0; } - if (!i) {// all args eaten completely - SASSERT(new_last.get() == NULL); + if (!i && !new_last) {// all args eaten completely SASSERT(retv == m_util.get_bv_size(a)); result = NULL; return retv; @@ -204,7 +203,7 @@ struct bv_trailing::imp { expr_ref_vector new_args(m); for (unsigned j = 0; j < i; ++j) new_args.push_back(a->get_arg(j)); - if (new_last.get()) new_args.push_back(new_last); + if (new_last) new_args.push_back(new_last); result = new_args.size() == 1 ? new_args.get(0) : m_util.mk_concat(new_args.size(), new_args.c_ptr()); return retv; @@ -228,7 +227,7 @@ struct bv_trailing::imp { unsigned retv = 0; numeral e_val; if (m_util.is_numeral(e, e_val, sz)) { - retv = remove_trailing(n, e_val); + retv = remove_trailing(std::min(n, sz), e_val); const unsigned new_sz = sz - retv; result = new_sz ? (retv ? m_util.mk_numeral(e_val, new_sz) : e) : NULL; return retv; From 0597b579b1e043d0f9b0115cbb1f62e2763502bb Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 7 Apr 2016 19:10:31 +0100 Subject: [PATCH 06/66] Bugfixes for bvarray2uf conversion. --- src/tactic/bv/bvarray2uf_rewriter.cpp | 42 ++++++++++++++++----------- src/tactic/bv/bvarray2uf_tactic.cpp | 12 ++++---- 2 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/tactic/bv/bvarray2uf_rewriter.cpp b/src/tactic/bv/bvarray2uf_rewriter.cpp index 54c654d54..d4e738ed7 100644 --- a/src/tactic/bv/bvarray2uf_rewriter.cpp +++ b/src/tactic/bv/bvarray2uf_rewriter.cpp @@ -25,6 +25,7 @@ Notes: #include"ast_pp.h" #include"bvarray2uf_rewriter.h" #include"rewriter_def.h" +#include"ref_util.h" // [1] C. M. Wintersteiger, Y. Hamadi, and L. de Moura: Efficiently Solving // Quantified Bit-Vector Formulas, in Formal Methods in System Design, @@ -50,10 +51,7 @@ bvarray2uf_rewriter_cfg::bvarray2uf_rewriter_cfg(ast_manager & m, params_ref con } bvarray2uf_rewriter_cfg::~bvarray2uf_rewriter_cfg() { - for (obj_map::iterator it = m_arrays_fs.begin(); - it != m_arrays_fs.end(); - it++) - m_manager.dec_ref(it->m_value); + dec_ref_map_key_values(m_manager, m_arrays_fs); } void bvarray2uf_rewriter_cfg::reset() {} @@ -110,12 +108,12 @@ func_decl_ref bvarray2uf_rewriter_cfg::mk_uf_for_array(expr * e) { if (m_array_util.is_as_array(e)) return func_decl_ref(static_cast(to_app(e)->get_decl()->get_parameter(0).get_ast()), m_manager); else { - app * a = to_app(e); func_decl * bv_f = 0; if (!m_arrays_fs.find(e, bv_f)) { - sort * domain = get_index_sort(a); - sort * range = get_value_sort(a); + sort * domain = get_index_sort(e); + sort * range = get_value_sort(e); bv_f = m_manager.mk_fresh_func_decl("f_t", "", 1, &domain, range); + TRACE("bvarray2uf_rw", tout << "for " << mk_ismt2_pp(e, m_manager) << " new func_decl is " << mk_ismt2_pp(bv_f, m_manager) << std::endl; ); if (is_uninterp_const(e)) { if (m_emc) m_emc->insert(to_app(e)->get_decl(), @@ -124,8 +122,12 @@ func_decl_ref bvarray2uf_rewriter_cfg::mk_uf_for_array(expr * e) { else if (m_fmc) m_fmc->insert(bv_f); m_arrays_fs.insert(e, bv_f); + m_manager.inc_ref(e); m_manager.inc_ref(bv_f); } + else { + TRACE("bvarray2uf_rw", tout << "for " << mk_ismt2_pp(e, m_manager) << " found " << mk_ismt2_pp(bv_f, m_manager) << std::endl; ); + } return func_decl_ref(bv_f, m_manager); } @@ -138,18 +140,24 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr SASSERT(num == 2); // From [1]: Finally, we replace equations of the form t = s, // where t and s are arrays, with \forall x . f_t(x) = f_s(x). - func_decl_ref f_t(mk_uf_for_array(args[0]), m_manager); - func_decl_ref f_s(mk_uf_for_array(args[1]), m_manager); + if (m_manager.are_equal(args[0], args[1])) { + result = m_manager.mk_true(); + res = BR_DONE; + } + else { + func_decl_ref f_t(mk_uf_for_array(args[0]), m_manager); + func_decl_ref f_s(mk_uf_for_array(args[1]), m_manager); - sort * sorts[1] = { get_index_sort(m_manager.get_sort(args[0])) }; - symbol names[1] = { symbol("x") }; - var_ref x(m_manager.mk_var(0, sorts[0]), m_manager); + sort * sorts[1] = { get_index_sort(m_manager.get_sort(args[0])) }; + symbol names[1] = { symbol("x") }; + var_ref x(m_manager.mk_var(0, sorts[0]), m_manager); - expr_ref body(m_manager); - body = m_manager.mk_eq(m_manager.mk_app(f_t, x.get()), m_manager.mk_app(f_s, x.get())); + expr_ref body(m_manager); + body = m_manager.mk_eq(m_manager.mk_app(f_t, x.get()), m_manager.mk_app(f_s, x.get())); - result = m_manager.mk_forall(1, sorts, names, body); - res = BR_DONE; + result = m_manager.mk_forall(1, sorts, names, body); + res = BR_DONE; + } } else if (m_manager.is_distinct(f) && is_bv_array(f->get_domain()[0])) { result = m_manager.mk_distinct_expanded(num, args); @@ -310,7 +318,7 @@ br_status bvarray2uf_rewriter_cfg::reduce_app(func_decl * f, unsigned num, expr } } - CTRACE("bvarray2uf_rw", res==BR_DONE, tout << "result: " << mk_ismt2_pp(result, m()) << std::endl; ); + CTRACE("bvarray2uf_rw", res == BR_DONE, tout << "result: " << mk_ismt2_pp(result, m()) << std::endl; ); return res; } diff --git a/src/tactic/bv/bvarray2uf_tactic.cpp b/src/tactic/bv/bvarray2uf_tactic.cpp index 42ceaf78c..ecf1889a2 100644 --- a/src/tactic/bv/bvarray2uf_tactic.cpp +++ b/src/tactic/bv/bvarray2uf_tactic.cpp @@ -62,7 +62,6 @@ class bvarray2uf_tactic : public tactic { SASSERT(g->is_well_sorted()); tactic_report report("bvarray2uf", *g); mc = 0; pc = 0; core = 0; result.reset(); - fail_if_proof_generation("bvarray2uf", g); fail_if_unsat_core_generation("bvarray2uf", g); TRACE("bvarray2uf", tout << "Before: " << std::endl; g->display(tout); ); @@ -73,7 +72,6 @@ class bvarray2uf_tactic : public tactic { filter_model_converter * fmc = alloc(filter_model_converter, m_manager); mc = concat(emc, fmc); m_rw.set_mcs(emc, fmc); - } @@ -86,10 +84,10 @@ class bvarray2uf_tactic : public tactic { break; expr * curr = g->form(idx); m_rw(curr, new_curr, new_pr); - //if (m_produce_proofs) { - // proof * pr = g->pr(idx); - // new_pr = m.mk_modus_ponens(pr, new_pr); - //} + if (m_produce_proofs) { + proof * pr = g->pr(idx); + new_pr = m_manager.mk_modus_ponens(pr, new_pr); + } g->update(idx, new_curr, new_pr, g->dep(idx)); } @@ -143,7 +141,7 @@ public: virtual void cleanup() { ast_manager & m = m_imp->m(); imp * d = alloc(imp, m, m_params); - std::swap(d, m_imp); + std::swap(d, m_imp); dealloc(d); } From bd0bd08ecf15c97a37cd17d021513a83255a37ff Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 8 Apr 2016 16:58:11 +0100 Subject: [PATCH 07/66] add is_considered_uninterpreted checks into acker_helper --- src/ackermannization/ackr_helper.h | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/ackermannization/ackr_helper.h b/src/ackermannization/ackr_helper.h index 5c572907e..70d6ab57b 100644 --- a/src/ackermannization/ackr_helper.h +++ b/src/ackermannization/ackr_helper.h @@ -33,19 +33,8 @@ class ackr_helper { which are not marked as uninterpreted but effectively are. */ inline bool should_ackermannize(app const * a) const { - if (a->get_family_id() == m_bvutil.get_family_id()) { - switch (a->get_decl_kind()) { - case OP_BSDIV0: - case OP_BUDIV0: - case OP_BSREM0: - case OP_BUREM0: - case OP_BSMOD0: - return true; - default: - return is_uninterp(a); - } - } - return (is_uninterp(a)); + decl_plugin * p = m_bvutil.get_manager().get_plugin(a->get_family_id()); + return p->is_considered_uninterpreted(a->get_decl()); } inline bv_util& bvutil() { return m_bvutil; } From 16e487b32a07a3c218cb0f0f76afc9321c9ed37d Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Fri, 8 Apr 2016 17:20:09 +0100 Subject: [PATCH 08/66] Bugfix for ackermann helper --- src/ackermannization/ackr_helper.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/ackermannization/ackr_helper.h b/src/ackermannization/ackr_helper.h index 70d6ab57b..327763da4 100644 --- a/src/ackermannization/ackr_helper.h +++ b/src/ackermannization/ackr_helper.h @@ -33,8 +33,12 @@ class ackr_helper { which are not marked as uninterpreted but effectively are. */ inline bool should_ackermannize(app const * a) const { - decl_plugin * p = m_bvutil.get_manager().get_plugin(a->get_family_id()); - return p->is_considered_uninterpreted(a->get_decl()); + if (is_uninterp(a)) + return true; + else { + decl_plugin * p = m_bvutil.get_manager().get_plugin(a->get_family_id()); + return p->is_considered_uninterpreted(a->get_decl()); + } } inline bv_util& bvutil() { return m_bvutil; } From cc6f72aba74b11e6c1138f7c8abb78f91d27d874 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 10 Apr 2016 01:48:35 +0200 Subject: [PATCH 09/66] fix handing of ite conditions that have to be included in projection, thanks to bug report by Zak Signed-off-by: Nikolaj Bjorner --- src/qe/qe_arith.cpp | 3 +- src/qe/qe_mbp.cpp | 58 +++++++++++-- src/qe/qsat.cpp | 113 ++++++++++++++++++++++++- src/tactic/smtlogics/quant_tactics.cpp | 2 + 4 files changed, 167 insertions(+), 9 deletions(-) diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 2b00efba0..e80adb40e 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -117,9 +117,10 @@ namespace qe { else if (extract_mod(model, t, val)) { ts.push_back(mk_mul(mul, val)); } - else if (m.is_ite(t, t1, t2, t3)) { + else if (m.is_ite(t, t1, t2, t3)) { VERIFY(model.eval(t1, val)); SASSERT(m.is_true(val) || m.is_false(val)); + TRACE("qe", tout << mk_pp(t1, m) << " := " << val << "\n";); if (m.is_true(val)) { is_linear(model, mul, t2, c, ts); } diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index cb8546b6c..3d9046a4e 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -107,6 +107,7 @@ void project_plugin::push_back(expr_ref_vector& lits, expr* e) { class mbp::impl { ast_manager& m; ptr_vector m_plugins; + expr_mark m_visited; void add_plugin(project_plugin* p) { family_id fid = p->get_family_id(); @@ -175,12 +176,48 @@ class mbp::impl { return false; } + + void extract_bools(model& model, expr_ref_vector& fmls, expr* fml) { + TRACE("qe", tout << "extract bools: " << mk_pp(fml, m) << "\n";); + ptr_vector todo; + if (is_app(fml)) { + todo.append(to_app(fml)->get_num_args(), to_app(fml)->get_args()); + } + while (!todo.empty()) { + expr* e = todo.back(); + todo.pop_back(); + if (m_visited.is_marked(e)) { + continue; + } + m_visited.mark(e); + if (m.is_bool(e)) { + expr_ref val(m); + VERIFY(model.eval(e, val)); + TRACE("qe", tout << "found: " << mk_pp(e, m) << "\n";); + if (m.is_true(val)) { + fmls.push_back(e); + } + else { + fmls.push_back(mk_not(m, e)); + } + } + else if (is_app(e)) { + todo.append(to_app(e)->get_num_args(), to_app(e)->get_args()); + } + else { + TRACE("qe", tout << "expression not handled " << mk_pp(e, m) << "\n";); + } + } + } + public: + void extract_literals(model& model, expr_ref_vector& fmls) { expr_ref val(m); for (unsigned i = 0; i < fmls.size(); ++i) { expr* fml = fmls[i].get(), *nfml, *f1, *f2, *f3; + SASSERT(m.is_bool(fml)); if (m.is_not(fml, nfml) && m.is_distinct(nfml)) { fmls[i] = project_plugin::pick_equality(m, model, nfml); --i; @@ -205,26 +242,28 @@ public: f1 = mk_not(m, f1); f2 = mk_not(m, f2); } - project_plugin::push_back(fmls, f1); + fmls[i] = f1; project_plugin::push_back(fmls, f2); - project_plugin::erase(fmls, i); + --i; } else if (m.is_implies(fml, f1, f2)) { VERIFY (model.eval(f2, val)); if (m.is_true(val)) { - project_plugin::push_back(fmls, f2); + fmls[i] = f2; } else { - project_plugin::push_back(fmls, mk_not(m, f1)); + fmls[i] = mk_not(m, f1); } - project_plugin::erase(fmls, i); + --i; } else if (m.is_ite(fml, f1, f2, f3)) { VERIFY (model.eval(f1, val)); if (m.is_true(val)) { + project_plugin::push_back(fmls, f1); project_plugin::push_back(fmls, f2); } else { + project_plugin::push_back(fmls, mk_not(m, f1)); project_plugin::push_back(fmls, f3); } project_plugin::erase(fmls, i); @@ -269,17 +308,24 @@ public: else if (m.is_not(fml, nfml) && m.is_ite(nfml, f1, f2, f3)) { VERIFY (model.eval(f1, val)); if (m.is_true(val)) { + project_plugin::push_back(fmls, f1); project_plugin::push_back(fmls, mk_not(m, f2)); } else { + project_plugin::push_back(fmls, mk_not(m, f1)); project_plugin::push_back(fmls, mk_not(m, f3)); } project_plugin::erase(fmls, i); } + else if (m.is_not(fml, nfml)) { + extract_bools(model, fmls, nfml); + } else { + extract_bools(model, fmls, fml); // TBD other Boolean operations. } } + m_visited.reset(); } impl(ast_manager& m):m(m) { @@ -310,6 +356,7 @@ public: app_ref var(m); th_rewriter rw(m); bool progress = true; + TRACE("qe", tout << vars << " " << fmls << "\n";); while (progress && !vars.empty()) { preprocess_solve(model, vars, fmls); app_ref_vector new_vars(m); @@ -345,6 +392,7 @@ public: } vars.append(new_vars); } + TRACE("qe", tout << vars << " " << fmls << "\n";); } }; diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index 1b0555b22..01a429038 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -31,6 +31,8 @@ Notes: #include "expr_abstract.h" #include "qe.h" #include "label_rewriter.h" +#include "expr_replacer.h" +#include "th_rewriter.h" namespace qe { @@ -674,16 +676,19 @@ namespace qe { is_forall = true; hoist.pull_quantifier(is_forall, fml, vars); m_vars.push_back(vars); + filter_vars(vars); } else { hoist.pull_quantifier(is_forall, fml, vars); m_vars.back().append(vars); + filter_vars(vars); } do { is_forall = !is_forall; vars.reset(); hoist.pull_quantifier(is_forall, fml, vars); m_vars.push_back(vars); + filter_vars(vars); } while (!vars.empty()); SASSERT(m_vars.back().empty()); @@ -691,6 +696,101 @@ namespace qe { TRACE("qe", tout << fml << "\n";); } + void filter_vars(app_ref_vector const& vars) { + for (unsigned i = 0; i < vars.size(); ++i) { + m_pred_abs.fmc()->insert(vars[i]->get_decl()); + } + } + +#if 0 + void hoist_ite(expr_ref& fml) { + app* ite; + scoped_ptr replace = mk_default_expr_replacer(m); + th_rewriter rewriter(m); + while (find_ite(fml, ite)) { + expr* cond, *th, *el; + VERIFY(m.is_ite(ite, cond, th, el)); + expr_ref tmp1(fml, m), tmp2(fml, m); + replace->apply_substitution(cond, m.mk_true(), tmp1); + replace->apply_substitution(cond, m.mk_false(), tmp2); + fml = m.mk_ite(cond, tmp1, tmp2); + rewriter(fml); + } + } + + bool find_ite(expr* e, app*& ite) { + ptr_vector todo; + todo.push_back(e); + ast_mark visited; + while(!todo.empty()) { + e = todo.back(); + todo.pop_back(); + if (visited.is_marked(e)) { + continue; + } + visited.mark(e, true); + if (m.is_ite(e) && !m.is_bool(e)) { + ite = to_app(e); + return true; + } + if (is_app(e)) { + app* a = to_app(e); + todo.append(a->get_num_args(), a->get_args()); + } + } + return false; + } + + // slower + void hoist_ite2(expr_ref& fml) { + obj_map result; + expr_ref_vector trail(m); + ptr_vector todo, args; + todo.push_back(fml); + + while (!todo.empty()) { + expr* e = todo.back(); + if (result.contains(e)) { + todo.pop_back(); + continue; + } + if (!is_app(e)) { + todo.pop_back(); + result.insert(e, e); + continue; + } + app* a = to_app(e); + expr* r; + unsigned sz = a->get_num_args(); + args.reset(); + for (unsigned i = 0; i < sz; ++i) { + if (result.find(a->get_arg(i), r)) { + args.push_back(r); + } + else { + todo.push_back(a->get_arg(i)); + } + } + if (sz == args.size()) { + r = m.mk_app(a->get_decl(), args.size(), args.c_ptr()); + trail.push_back(r); + if (m.is_bool(e) && m.get_basic_family_id() != a->get_family_id()) { + expr_ref fml(r, m); + hoist_ite(fml); + trail.push_back(fml); + r = fml; + } + result.insert(e, r); + todo.pop_back(); + } + } + fml = result.find(fml); + } +#endif + + + + void initialize_levels() { // initialize levels. for (unsigned i = 0; i < m_vars.size(); ++i) { @@ -941,7 +1041,7 @@ namespace qe { expr_ref_vector fmls(m); fmls.append(core.size(), core.c_ptr()); fmls.append(k.size(), k.get_formulas()); - return check_fmls(fmls); + return check_fmls(fmls) || m.canceled(); } bool check_fmls(expr_ref_vector const& fmls) { @@ -953,7 +1053,7 @@ namespace qe { lbool is_sat = solver.check(); CTRACE("qe", is_sat != l_false, tout << fmls << "\nare not unsat\n";); - return (is_sat == l_false); + return (is_sat == l_false) || m.canceled(); } bool validate_model(expr_ref_vector const& asms) { @@ -967,7 +1067,7 @@ namespace qe { bool validate_model(model& mdl, unsigned sz, expr* const* fmls) { expr_ref val(m); for (unsigned i = 0; i < sz; ++i) { - if (!m_model->eval(fmls[i], val)) { + if (!m_model->eval(fmls[i], val) && !m.canceled()) { TRACE("qe", tout << "Formula does not evaluate in model: " << mk_pp(fmls[i], m) << "\n";); return false; } @@ -1001,6 +1101,9 @@ namespace qe { TRACE("qe", tout << "Projection is false in model\n";); return false; } + if (m.canceled()) { + return true; + } for (unsigned i = 0; i < m_avars.size(); ++i) { contains_app cont(m, m_avars[i].get()); if (cont(proj)) { @@ -1029,6 +1132,7 @@ namespace qe { return true; } + public: qsat(ast_manager& m, params_ref const& p, bool qelim, bool force_elim): @@ -1070,6 +1174,8 @@ namespace qe { expr_ref fml(m); mc = 0; pc = 0; core = 0; in->get_formulas(fmls); + + fml = mk_and(m, fmls.size(), fmls.c_ptr()); // for now: @@ -1091,6 +1197,7 @@ namespace qe { fml = push_not(fml); } hoist(fml); +// hoist_ite(fml); redundant provided theories understand to deal with ite terms. m_pred_abs.abstract_atoms(fml, defs); fml = m_pred_abs.mk_abstract(fml); m_ex.assert_expr(mk_and(defs)); diff --git a/src/tactic/smtlogics/quant_tactics.cpp b/src/tactic/smtlogics/quant_tactics.cpp index f501ed75a..045b0ec87 100644 --- a/src/tactic/smtlogics/quant_tactics.cpp +++ b/src/tactic/smtlogics/quant_tactics.cpp @@ -27,6 +27,7 @@ Revision History: #include"nlqsat.h" #include"ctx_simplify_tactic.h" #include"smt_tactic.h" +#include"elim_term_ite_tactic.h" static tactic * mk_quant_preprocessor(ast_manager & m, bool disable_gaussian = false) { params_ref pull_ite_p; @@ -112,6 +113,7 @@ tactic * mk_lra_tactic(ast_manager & m, params_ref const & p) { )); #else tactic * st = and_then(mk_quant_preprocessor(m), + mk_qe_lite_tactic(m, p), or_else(mk_qsat_tactic(m, p), and_then(mk_qe_tactic(m), mk_smt_tactic()))); #endif From 2033719c14f2163609824fff5fa4c8cd501f1135 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 9 Apr 2016 20:58:57 -0700 Subject: [PATCH 10/66] fix optimization pre-processing reported by Gereon Kremer Signed-off-by: Nikolaj Bjorner --- src/tactic/arith/eq2bv_tactic.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/tactic/arith/eq2bv_tactic.cpp b/src/tactic/arith/eq2bv_tactic.cpp index 7957edbc4..ac1ee9afd 100644 --- a/src/tactic/arith/eq2bv_tactic.cpp +++ b/src/tactic/arith/eq2bv_tactic.cpp @@ -191,15 +191,18 @@ public: expr* c = it->m_key; bool strict; rational r; - if (m_bounds.has_lower(c, r, strict)) { + expr_ref fml(m); + if (m_bounds.has_lower(c, r, strict) && !r.is_neg()) { 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)); + fml = bv.mk_ule(bv.mk_numeral(r, m.get_sort(d)), d); + g->assert_expr(fml, m_bounds.lower_dep(c)); } - if (m_bounds.has_upper(c, r, strict)) { + if (m_bounds.has_upper(c, r, strict) && !r.is_neg()) { 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)); + fml = bv.mk_ule(d, bv.mk_numeral(r, m.get_sort(d))); + g->assert_expr(fml, m_bounds.upper_dep(c)); } } g->inc_depth(); @@ -245,8 +248,9 @@ public: else { ++it->m_value; } - unsigned p = next_power_of_two(it->m_value); + unsigned p = next_power_of_two(it->m_value); if (p <= 1) p = 2; + if (it->m_value == p) p *= 2; unsigned n = log2(p); app* z = m.mk_fresh_const("z", bv.mk_sort(n)); m_trail.push_back(z); @@ -302,16 +306,16 @@ public: m_nonfd.mark(f, true); expr* e1, *e2; if (m.is_eq(f, e1, e2)) { - if (is_fd(e1, e2) || is_fd(e2, e1)) { + if (is_fd(e1, e2) || is_fd(e2, e1)) { continue; } } - if (is_app(f)) { - m_todo.append(to_app(f)->get_num_args(), to_app(f)->get_args()); - } - else if (is_quantifier(f)) { - m_todo.push_back(to_quantifier(f)->get_expr()); - } + if (is_app(f)) { + m_todo.append(to_app(f)->get_num_args(), to_app(f)->get_args()); + } + else if (is_quantifier(f)) { + m_todo.push_back(to_quantifier(f)->get_expr()); + } } } From aa7b5d80fe480730bd036463a99163cab657bdcc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 12 Apr 2016 09:41:50 -0700 Subject: [PATCH 11/66] extract array terms for evaluator Signed-off-by: Nikolaj Bjorner --- src/model/model_evaluator.cpp | 40 +++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 3111f2b6c..93f70c4cc 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -118,8 +118,8 @@ struct evaluator_cfg : public default_rewriter_cfg { expr * val = m_model.get_const_interp(f); if (val != 0) { result = val; + expand_value(result); return BR_DONE; -// return m().is_value(val)?BR_DONE:BR_REWRITE_FULL; } if (m_model_completion) { @@ -188,6 +188,21 @@ struct evaluator_cfg : public default_rewriter_cfg { return st; } + void expand_value(expr_ref& val) { + vector stores; + expr_ref else_case(m()); + if (m_ar.is_array(val) && extract_array_func_interp(val, stores, else_case)) { + sort* srt = m().get_sort(val); + val = m_ar.mk_const_array(srt, else_case); + for (unsigned i = stores.size(); i > 0; ) { + --i; + expr_ref_vector args(m()); + args.push_back(val); + args.append(stores[i].size(), stores[i].c_ptr()); + val = m_ar.mk_store(args.size(), args.c_ptr()); + } + } + } bool get_macro(func_decl * f, expr * & def, quantifier * & q, proof * & def_pr) { @@ -306,6 +321,21 @@ struct evaluator_cfg : public default_rewriter_cfg { TRACE("model_evaluator", tout << "non-ground else case " << mk_pp(a, m()) << "\n" << mk_pp(else_case, m()) << "\n";); return false; } + bool is_value = true; + for (unsigned i = stores.size(); is_value && i > 0; ) { + --i; + if (else_case == stores[i].back()) { + for (unsigned j = i + 1; j < stores.size(); ++j) { + stores[j-1].reset(); + stores[j-1].append(stores[j]); + } + stores.pop_back(); + continue; + } + for (unsigned j = 0; is_value && j + 1 < stores[i].size(); ++j) { + is_value = m().is_value(stores[i][j].get()); + } + } TRACE("model_evaluator", tout << "else case: " << mk_pp(else_case, m()) << "\n";); return true; } @@ -358,14 +388,10 @@ unsigned model_evaluator::get_num_steps() const { return m_imp->get_num_steps(); } - void model_evaluator::cleanup(params_ref const & p) { model_core & md = m_imp->cfg().m_model; - #pragma omp critical (model_evaluator) - { - dealloc(m_imp); - m_imp = alloc(imp, md, p); - } + dealloc(m_imp); + m_imp = alloc(imp, md, p); } void model_evaluator::reset(params_ref const & p) { From 0f93853a4c57bbee1a1c08456cb52cfc13bf7d42 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 12 Apr 2016 13:17:10 -0700 Subject: [PATCH 12/66] remove labels from evaluation result Signed-off-by: Nikolaj Bjorner --- src/model/model_evaluator.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/model/model_evaluator.cpp b/src/model/model_evaluator.cpp index 93f70c4cc..e67e3cb5e 100644 --- a/src/model/model_evaluator.cpp +++ b/src/model/model_evaluator.cpp @@ -172,6 +172,10 @@ struct evaluator_cfg : public default_rewriter_cfg { st = m_f_rw.mk_app_core(f, num, args, result); else if (fid == m_seq_rw.get_fid()) st = m_seq_rw.mk_app_core(f, num, args, result); + else if (fid == m().get_label_family_id() && num == 1) { + result = args[0]; + st = BR_DONE; + } else if (evaluate(f, num, args, result)) { TRACE("model_evaluator", tout << "reduce_app " << f->get_name() << "\n"; for (unsigned i = 0; i < num; i++) tout << mk_ismt2_pp(args[i], m()) << "\n"; From 4ebf392da7145b10a59ccfd049e6f379353d7f2e Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 16 Apr 2016 09:26:13 -0700 Subject: [PATCH 13/66] Fixes #564: use std::vector on std::strings Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.cpp | 8 ++++---- src/cmd_context/cmd_context.h | 4 +++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 8f1718141..cfad0d394 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1322,7 +1322,7 @@ void cmd_context::restore_func_decls(unsigned old_sz) { sf_pair const & p = *it; erase_func_decl_core(p.first, p.second); } - m_func_decls_stack.shrink(old_sz); + m_func_decls_stack.resize(old_sz); } void cmd_context::restore_psort_decls(unsigned old_sz) { @@ -1389,7 +1389,7 @@ void cmd_context::restore_assertions(unsigned old_sz) { if (produce_unsat_cores()) restore(m(), m_assertion_names, old_sz); if (m_interactive_mode) - m_assertion_strings.shrink(old_sz); + m_assertion_strings.resize(old_sz); } void cmd_context::pop(unsigned n) { @@ -1724,8 +1724,8 @@ void cmd_context::display_statistics(bool show_total_time, double total_time) { void cmd_context::display_assertions() { if (!m_interactive_mode) throw cmd_exception("command is only available in interactive mode, use command (set-option :interactive-mode true)"); - vector::const_iterator it = m_assertion_strings.begin(); - vector::const_iterator end = m_assertion_strings.end(); + std::vector::const_iterator it = m_assertion_strings.begin(); + std::vector::const_iterator end = m_assertion_strings.end(); regular_stream() << "("; for (bool first = true; it != end; ++it) { std::string const & s = *it; diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index 865999042..be3a1f4f8 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -22,6 +22,7 @@ Notes: #define CMD_CONTEXT_H_ #include +#include #include"ast.h" #include"ast_printer.h" #include"pdecl.h" @@ -38,6 +39,7 @@ Notes: #include"scoped_ptr_vector.h" #include"context_params.h" + class func_decls { func_decl * m_decls; public: @@ -189,7 +191,7 @@ protected: // ptr_vector m_aux_pdecls; ptr_vector m_assertions; - vector m_assertion_strings; + std::vector m_assertion_strings; ptr_vector m_assertion_names; // named assertions are represented using boolean variables. struct scope { From d383fd851a17ceca7102d04e6b538c0c9c7eee14 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 16 Apr 2016 09:34:27 -0700 Subject: [PATCH 14/66] move vector --- src/muz/base/dl_util.h | 3 ++- src/parsers/smt2/smt2parser.cpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index f20a77bda..78ce453ec 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -19,6 +19,7 @@ Revision History: #ifndef DL_UTIL_H_ #define DL_UTIL_H_ +#include #include"ast.h" #include"hashtable.h" #include"obj_hashtable.h" @@ -67,7 +68,7 @@ namespace datalog { typedef idx_set var_idx_set; typedef u_map varidx2var_map; typedef obj_hashtable func_decl_set; //!< Rule dependencies. - typedef vector string_vector; + typedef std::vector string_vector; bool contains_var(expr * trm, unsigned var_idx); diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 91be93c0e..71ef2a06e 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -298,7 +298,7 @@ namespace smt2 { } unsigned m_cache_end; - vector m_cached_strings; + std::vector m_cached_strings; int m_num_open_paren; @@ -2197,7 +2197,7 @@ namespace smt2 { m_scanner.start_caching(); m_cache_end = 0; - m_cached_strings.reset(); + m_cached_strings.resize(0); check_lparen_next("invalid get-value command, '(' expected"); while (!curr_is_rparen()) { From 1c8e0918d82a1fc417e4e5d94291c4b65b35bfcd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 16 Apr 2016 10:08:29 -0700 Subject: [PATCH 15/66] move to std::vector in replayer Signed-off-by: Nikolaj Bjorner --- src/api/z3_replayer.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/api/z3_replayer.cpp b/src/api/z3_replayer.cpp index 8ce81ec31..b1baa6de2 100644 --- a/src/api/z3_replayer.cpp +++ b/src/api/z3_replayer.cpp @@ -23,6 +23,7 @@ Notes: #include"symbol.h" #include"trace.h" #include +#include void register_z3_replayer_cmds(z3_replayer & in); @@ -46,7 +47,7 @@ struct z3_replayer::imp { size_t m_ptr; size_t_map m_heap; svector m_cmds; - vector m_cmds_names; + std::vector m_cmds_names; enum value_kind { INT64, UINT64, DOUBLE, STRING, SYMBOL, OBJECT, UINT_ARRAY, INT_ARRAY, SYMBOL_ARRAY, OBJECT_ARRAY, FLOAT }; @@ -676,7 +677,9 @@ struct z3_replayer::imp { void register_cmd(unsigned id, z3_replayer_cmd cmd, char const* name) { m_cmds.reserve(id+1, 0); - m_cmds_names.reserve(id+1, ""); + while (static_cast(m_cmds_names.size()) <= id+1) { + m_cmds_names.push_back(""); + } m_cmds[id] = cmd; m_cmds_names[id] = name; } From 0094b366369df9b76f8bcec3add854de43f9a967 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 16 Apr 2016 12:25:29 -0700 Subject: [PATCH 16/66] fix bounds check to fix segfault reported in issue #565 Signed-off-by: Nikolaj Bjorner --- src/opt/opt_context.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index a28500568..b4dbb3dd5 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -1205,7 +1205,7 @@ namespace opt { } inf_eps context::get_lower_as_num(unsigned idx) { - if (idx > m_objectives.size()) { + if (idx >= m_objectives.size()) { throw default_exception("index out of bounds"); } objective const& obj = m_objectives[idx]; @@ -1223,7 +1223,7 @@ namespace opt { } inf_eps context::get_upper_as_num(unsigned idx) { - if (idx > m_objectives.size()) { + if (idx >= m_objectives.size()) { throw default_exception("index out of bounds"); } objective const& obj = m_objectives[idx]; From 8287f7ba82ced4bc11a24a0b634b2449e228862a Mon Sep 17 00:00:00 2001 From: Xavier DELPIERRE Date: Sun, 17 Apr 2016 02:39:35 +0200 Subject: [PATCH 17/66] nmake->make, little error --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3985fad27..41863ee67 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ then: ```bash cd build -nmake +make ``` ## Building Z3 using make and GCC/Clang From 14e1e247a408ba5fd8be4adcd81057829baaa5b6 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Sun, 17 Apr 2016 11:35:27 +0100 Subject: [PATCH 18/66] Revert "nmake->make, little error" This reverts commit 8287f7ba82ced4bc11a24a0b634b2449e228862a. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 41863ee67..3985fad27 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ then: ```bash cd build -make +nmake ``` ## Building Z3 using make and GCC/Clang From b3713e7496eb3a32a7109a5f3b2007b190344644 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Wed, 6 Apr 2016 22:47:28 +0100 Subject: [PATCH 19/66] Refactor ``mk_z3consts_java()`` code into ``mk_z3consts_java_internal()`` and move that into ``mk_genfile_common.py``. Then adapt ``mk_util.py`` and ``mk_consts_files.py`` to call into the code at its new location. The purpose of this change is to have Python code common to the Python and CMake build systems separate from Python code that is only used for the Python build system. --- scripts/mk_consts_files.py | 19 ++++++ scripts/mk_genfile_common.py | 110 +++++++++++++++++++++++++++++++++++ scripts/mk_util.py | 106 +++------------------------------ 3 files changed, 137 insertions(+), 98 deletions(-) diff --git a/scripts/mk_consts_files.py b/scripts/mk_consts_files.py index 482b13c94..e582d8468 100755 --- a/scripts/mk_consts_files.py +++ b/scripts/mk_consts_files.py @@ -17,6 +17,11 @@ def main(args): parser.add_argument("api_files", nargs="+") parser.add_argument("--z3py-output-dir", dest="z3py_output_dir", default=None) parser.add_argument("--dotnet-output-dir", dest="dotnet_output_dir", default=None) + parser.add_argument("--java-output-dir", dest="java_output_dir", default=None) + parser.add_argument("--java-package-name", + dest="java_package_name", + default=None, + help="Name to give the Java package (e.g. ``com.microsoft.z3``).") pargs = parser.parse_args(args) if not mk_genfile_common.check_files_exist(pargs.api_files): @@ -41,6 +46,20 @@ def main(args): logging.info('Generated "{}"'.format(output)) count += 1 + if pargs.java_output_dir: + if pargs.java_package_name == None: + logging.error('Java package name must be specified') + return 1 + if not mk_genfile_common.check_dir_exists(pargs.java_output_dir): + return 1 + outputs = mk_genfile_common.mk_z3consts_java_internal( + pargs.api_files, + pargs.java_package_name, + pargs.java_output_dir) + for generated_file in outputs: + logging.info('Generated "{}"'.format(generated_file)) + count += 1 + if count == 0: logging.info('No files generated. You need to specific an output directory' ' for the relevant langauge bindings') diff --git a/scripts/mk_genfile_common.py b/scripts/mk_genfile_common.py index 0734d52b0..d747baee8 100644 --- a/scripts/mk_genfile_common.py +++ b/scripts/mk_genfile_common.py @@ -255,6 +255,116 @@ def mk_z3consts_dotnet_internal(api_files, output_dir): z3consts.close() return z3consts_output_path + +def mk_z3consts_java_internal(api_files, package_name, output_dir): + """ + Generate "com.microsoft.z3.enumerations" package from the list of API + header files in ``api_files`` and write the package directory into + the ``output_dir`` directory + + Returns a list of the generated java source files. + """ + blank_pat = re.compile("^ *$") + comment_pat = re.compile("^ *//.*$") + typedef_pat = re.compile("typedef enum *") + typedef2_pat = re.compile("typedef enum { *") + openbrace_pat = re.compile("{ *") + closebrace_pat = re.compile("}.*;") + + DeprecatedEnums = [ 'Z3_search_failure' ] + gendir = os.path.join(output_dir, "enumerations") + if not os.path.exists(gendir): + os.mkdir(gendir) + + generated_enumeration_files = [] + for api_file in api_files: + api = open(api_file, 'r') + + SEARCHING = 0 + FOUND_ENUM = 1 + IN_ENUM = 2 + + mode = SEARCHING + decls = {} + idx = 0 + + linenum = 1 + for line in api: + m1 = blank_pat.match(line) + m2 = comment_pat.match(line) + if m1 or m2: + # skip blank lines and comments + linenum = linenum + 1 + elif mode == SEARCHING: + m = typedef_pat.match(line) + if m: + mode = FOUND_ENUM + m = typedef2_pat.match(line) + if m: + mode = IN_ENUM + decls = {} + idx = 0 + elif mode == FOUND_ENUM: + m = openbrace_pat.match(line) + if m: + mode = IN_ENUM + decls = {} + idx = 0 + else: + assert False, "Invalid %s, line: %s" % (api_file, linenum) + else: + assert mode == IN_ENUM + words = re.split('[^\-a-zA-Z0-9_]+', line) + m = closebrace_pat.match(line) + if m: + name = words[1] + if name not in DeprecatedEnums: + efile = open('%s.java' % os.path.join(gendir, name), 'w') + generated_enumeration_files.append(efile.name) + efile.write('/**\n * Automatically generated file\n **/\n\n') + efile.write('package %s.enumerations;\n\n' % package_name) + + efile.write('/**\n') + efile.write(' * %s\n' % name) + efile.write(' **/\n') + efile.write('public enum %s {\n' % name) + efile.write + first = True + for k in decls: + i = decls[k] + if first: + first = False + else: + efile.write(',\n') + efile.write(' %s (%s)' % (k, i)) + efile.write(";\n") + efile.write('\n private final int intValue;\n\n') + efile.write(' %s(int v) {\n' % name) + efile.write(' this.intValue = v;\n') + efile.write(' }\n\n') + efile.write(' public static final %s fromInt(int v) {\n' % name) + efile.write(' for (%s k: values()) \n' % name) + efile.write(' if (k.intValue == v) return k;\n') + efile.write(' return values()[0];\n') + efile.write(' }\n\n') + efile.write(' public final int toInt() { return this.intValue; }\n') + # efile.write(';\n %s(int v) {}\n' % name) + efile.write('}\n\n') + efile.close() + mode = SEARCHING + else: + if words[2] != '': + if len(words[2]) > 1 and words[2][1] == 'x': + idx = int(words[2], 16) + else: + idx = int(words[2]) + decls[words[1]] = idx + idx = idx + 1 + linenum = linenum + 1 + api.close() + return generated_enumeration_files + + ############################################################################### # Functions for generating a "module definition file" for MSVC ############################################################################### diff --git a/scripts/mk_util.py b/scripts/mk_util.py index be641ba35..38bf2a854 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2753,109 +2753,19 @@ def mk_z3consts_dotnet(api_files): # Extract enumeration types from z3_api.h, and add Java definitions def mk_z3consts_java(api_files): - blank_pat = re.compile("^ *$") - comment_pat = re.compile("^ *//.*$") - typedef_pat = re.compile("typedef enum *") - typedef2_pat = re.compile("typedef enum { *") - openbrace_pat = re.compile("{ *") - closebrace_pat = re.compile("}.*;") - java = get_component(JAVA_COMPONENT) - - DeprecatedEnums = [ 'Z3_search_failure' ] - gendir = os.path.join(java.src_dir, "enumerations") - if not os.path.exists(gendir): - os.mkdir(gendir) - + full_path_api_files = [] for api_file in api_files: api_file_c = java.find_file(api_file, java.name) api_file = os.path.join(api_file_c.src_dir, api_file) - - api = open(api_file, 'r') - - SEARCHING = 0 - FOUND_ENUM = 1 - IN_ENUM = 2 - - mode = SEARCHING - decls = {} - idx = 0 - - linenum = 1 - for line in api: - m1 = blank_pat.match(line) - m2 = comment_pat.match(line) - if m1 or m2: - # skip blank lines and comments - linenum = linenum + 1 - elif mode == SEARCHING: - m = typedef_pat.match(line) - if m: - mode = FOUND_ENUM - m = typedef2_pat.match(line) - if m: - mode = IN_ENUM - decls = {} - idx = 0 - elif mode == FOUND_ENUM: - m = openbrace_pat.match(line) - if m: - mode = IN_ENUM - decls = {} - idx = 0 - else: - assert False, "Invalid %s, line: %s" % (api_file, linenum) - else: - assert mode == IN_ENUM - words = re.split('[^\-a-zA-Z0-9_]+', line) - m = closebrace_pat.match(line) - if m: - name = words[1] - if name not in DeprecatedEnums: - efile = open('%s.java' % os.path.join(gendir, name), 'w') - efile.write('/**\n * Automatically generated file\n **/\n\n') - efile.write('package %s.enumerations;\n\n' % java.package_name) - - efile.write('/**\n') - efile.write(' * %s\n' % name) - efile.write(' **/\n') - efile.write('public enum %s {\n' % name) - efile.write - first = True - for k in decls: - i = decls[k] - if first: - first = False - else: - efile.write(',\n') - efile.write(' %s (%s)' % (k, i)) - efile.write(";\n") - efile.write('\n private final int intValue;\n\n') - efile.write(' %s(int v) {\n' % name) - efile.write(' this.intValue = v;\n') - efile.write(' }\n\n') - efile.write(' public static final %s fromInt(int v) {\n' % name) - efile.write(' for (%s k: values()) \n' % name) - efile.write(' if (k.intValue == v) return k;\n') - efile.write(' return values()[0];\n') - efile.write(' }\n\n') - efile.write(' public final int toInt() { return this.intValue; }\n') - # efile.write(';\n %s(int v) {}\n' % name) - efile.write('}\n\n') - efile.close() - mode = SEARCHING - else: - if words[2] != '': - if len(words[2]) > 1 and words[2][1] == 'x': - idx = int(words[2], 16) - else: - idx = int(words[2]) - decls[words[1]] = idx - idx = idx + 1 - linenum = linenum + 1 - api.close() + full_path_api_files.append(api_file) + generated_files = mk_genfile_common.mk_z3consts_java_internal( + full_path_api_files, + java.package_name, + java.src_dir) if VERBOSE: - print("Generated '%s'" % ('%s' % gendir)) + for generated_file in generated_files: + print("Generated '{}'".format(generated_file)) # Extract enumeration types from z3_api.h, and add ML definitions def mk_z3consts_ml(api_files): From 3042f0f9649e8595e8f317ea1844decf971305f1 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Wed, 6 Apr 2016 22:53:07 +0100 Subject: [PATCH 20/66] Fix inconsistent emission of Java enumeration files. The ordering of emitted enum values is not consistent between python 2 or 3. The root cause of the problem was a dictionary's keys being iterated over which has no defined order. This has been fixed by iterating over the dictionary's items and ordering by values. We could order by key rather than the values but seeing as these represent an enum, ordering by value makes more sense. --- scripts/mk_genfile_common.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/mk_genfile_common.py b/scripts/mk_genfile_common.py index d747baee8..b8d6ac5e1 100644 --- a/scripts/mk_genfile_common.py +++ b/scripts/mk_genfile_common.py @@ -330,13 +330,13 @@ def mk_z3consts_java_internal(api_files, package_name, output_dir): efile.write('public enum %s {\n' % name) efile.write first = True - for k in decls: - i = decls[k] + # Iterate over key-value pairs ordered by value + for k, v in sorted(decls.items(), key=lambda pair: pair[1]): if first: first = False else: efile.write(',\n') - efile.write(' %s (%s)' % (k, i)) + efile.write(' %s (%s)' % (k, v)) efile.write(";\n") efile.write('\n private final int intValue;\n\n') efile.write(' %s(int v) {\n' % name) From 022039535ac36d03cc483cdcc2b5907ca0e574f1 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Sat, 16 Apr 2016 03:52:11 -0500 Subject: [PATCH 21/66] [CMake] Implement support for building and installing the Java bindings. I'm not entirely happy with some parts of the implementation * The default locations for installing ``com.microsoft.z3.jar`` and ``libz3java.so`` aren't correct. CMake cache variables have been provided that allow the user to change where these get installed. * The name of ``libz3java.so``. It doesn't conform to the Debian packaging guidelines (https://www.debian.org/doc/packaging-manuals/java-policy/x126.html) and I have not provided an option to change this. * The fact that ``SONAME`` and ``VERSION`` are not set on ``libz3java.so``. These issues should be addressed once we know the correct way to handle installation. --- README-CMake.md | 4 + contrib/cmake/src/CMakeLists.txt | 12 ++ contrib/cmake/src/api/java/CMakeLists.txt | 235 ++++++++++++++++++++++ 3 files changed, 251 insertions(+) create mode 100644 contrib/cmake/src/api/java/CMakeLists.txt diff --git a/README-CMake.md b/README-CMake.md index 2cec1653a..dd50c05fa 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -279,6 +279,10 @@ The following useful options can be passed to CMake whilst configuring. * ``INSTALL_DOTNET_BINDINGS`` - BOOL. If set to ``TRUE`` and ``BUILD_DOTNET_BINDINGS`` is ``TRUE`` then running the ``install`` target will install Z3's .NET bindings. * ``DOTNET_CSC_EXECUTABLE`` - STRING. The path to the C# compiler to use. Only relevant if ``BUILD_DOTNET_BINDINGS`` is set to ``TRUE``. * ``DOTNET_GACUTIL_EXECUTABLE`` - STRING. The path to the gacutil program to use. Only relevant if ``BUILD_DOTNET_BINDINGS`` is set to ``TRUE``. +* ``BUILD_JAVA_BINDINGS`` - BOOL. If set to ``TRUE`` then Z3's Java bindings will be built. +* ``INSTALL_JAVA_BINDINGS`` - BOOL. If set to ``TRUE`` and ``BUILD_JAVA_BINDINGS`` is ``TRUE`` then running the ``install`` target will install Z3's Java bindings. +* ``Z3_JAVA_JAR_INSTALLDIR`` - STRING. The path to directory to install the Z3 Java ``.jar`` file. This path should be relative to ``CMAKE_INSTALL_PREFIX``. +* ``Z3_JAVA_JNI_LIB_INSTALLDIRR`` - STRING. The path to directory to install the Z3 Java JNI bridge library. This path should be relative to ``CMAKE_INSTALL_PREFIX``. On the command line these can be passed to ``cmake`` using the ``-D`` option. In ``ccmake`` and ``cmake-gui`` these can be set in the user interface. diff --git a/contrib/cmake/src/CMakeLists.txt b/contrib/cmake/src/CMakeLists.txt index 13403afb1..574dd1089 100644 --- a/contrib/cmake/src/CMakeLists.txt +++ b/contrib/cmake/src/CMakeLists.txt @@ -234,4 +234,16 @@ if (BUILD_DOTNET_BINDINGS) add_subdirectory(api/dotnet) endif() +################################################################################ +# Java bindings +################################################################################ +option(BUILD_JAVA_BINDINGS "Build Java bindings for Z3" OFF) +if (BUILD_JAVA_BINDINGS) + if (NOT BUILD_LIBZ3_SHARED) + message(FATAL_ERROR "The Java bindings will not work with a static libz3. " + "You either need to disable BUILD_JAVA_BINDINGS or enable BUILD_LIBZ3_SHARED") + endif() + add_subdirectory(api/java) +endif() + # TODO: Implement support for other bindigns diff --git a/contrib/cmake/src/api/java/CMakeLists.txt b/contrib/cmake/src/api/java/CMakeLists.txt new file mode 100644 index 000000000..e41fa5ed0 --- /dev/null +++ b/contrib/cmake/src/api/java/CMakeLists.txt @@ -0,0 +1,235 @@ +find_package(Java REQUIRED) +find_package(JNI REQUIRED) +include(UseJava) + +# Sanity check for dirty source tree +foreach (file_name "enumerations" "Native.cpp" "Native.java") + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${file_name}") + message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/${file_name}\"" + ${z3_polluted_tree_msg}) + endif() +endforeach() + +set(Z3_JAVA_PACKAGE_NAME "com.microsoft.z3") + +# Rule to generate ``Native.java`` and ``Native.cpp`` +set(Z3_JAVA_NATIVE_JAVA "${CMAKE_CURRENT_BINARY_DIR}/Native.java") +set(Z3_JAVA_NATIVE_CPP "${CMAKE_CURRENT_BINARY_DIR}/Native.cpp") +add_custom_command(OUTPUT "${Z3_JAVA_NATIVE_JAVA}" "${Z3_JAVA_NATIVE_CPP}" + COMMAND "${PYTHON_EXECUTABLE}" + "${CMAKE_SOURCE_DIR}/scripts/update_api.py" + ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} + "--java-output-dir" + "${CMAKE_CURRENT_BINARY_DIR}" + "--java-package-name" + ${Z3_JAVA_PACKAGE_NAME} + DEPENDS + ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} + "${CMAKE_SOURCE_DIR}/scripts/update_api.py" + ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} + # FIXME: When update_api.py no longer uses ``mk_util`` drop this dependency + "${CMAKE_SOURCE_DIR}/scripts/mk_util.py" + COMMENT "Generating \"${Z3_JAVA_NATIVE_JAVA}\" and \"${Z3_JAVA_NATIVE_CPP}\"" + ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} +) + +# Add rule to build native code that provides a bridge between +# ``Native.java`` and libz3's interfac3. +add_library(z3java SHARED ${Z3_JAVA_NATIVE_CPP}) +target_link_libraries(z3java PRIVATE libz3) +# FIXME: +# Not sure if using all the flags used by the Z3 components is really necessary +# here. At the bare minimum setting _AMD64_ depending on the target is +# necessary but seeing as the Python build system uses all the flags used for building +# Z3's components to build ``Native.cpp`` lets do the same for now. +target_compile_options(z3java PRIVATE ${Z3_COMPONENT_CXX_FLAGS}) +target_compile_definitions(z3java PRIVATE ${Z3_COMPONENT_CXX_DEFINES}) +z3_append_linker_flag_list_to_target(z3java ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) +target_include_directories(z3java PRIVATE + "${CMAKE_SOURCE_DIR}/src/api" + "${CMAKE_BINARY_DIR}/src/api" + ${JNI_INCLUDE_DIRS} +) +# FIXME: Should this library have SONAME and VERSION set? + +# This prevents CMake from automatically defining ``z3java_EXPORTS`` +set_property(TARGET z3java PROPERTY DEFINE_SYMBOL "") + +# Rule to generate the ``com.microsoft.z3.enumerations`` package +# FIXME: This list of files is fragile +set(Z3_JAVA_ENUMERATION_PACKAGE_FILES + Z3_ast_kind.java + Z3_ast_print_mode.java + Z3_decl_kind.java + Z3_error_code.java + Z3_goal_prec.java + Z3_lbool.java + Z3_param_kind.java + Z3_parameter_kind.java + Z3_sort_kind.java + Z3_symbol_kind.java +) +set(Z3_JAVA_ENUMERATION_PACKAGE_FILES_FULL_PATH "") +foreach (enum_file ${Z3_JAVA_ENUMERATION_PACKAGE_FILES}) + list(APPEND Z3_JAVA_ENUMERATION_PACKAGE_FILES_FULL_PATH + "${CMAKE_CURRENT_BINARY_DIR}/enumerations/${enum_file}" + ) +endforeach() +add_custom_command(OUTPUT ${Z3_JAVA_ENUMERATION_PACKAGE_FILES_FULL_PATH} + COMMAND "${PYTHON_EXECUTABLE}" + "${CMAKE_SOURCE_DIR}/scripts/mk_consts_files.py" + ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} + "--java-output-dir" + "${CMAKE_CURRENT_BINARY_DIR}" + "--java-package-name" + ${Z3_JAVA_PACKAGE_NAME} + DEPENDS + ${Z3_FULL_PATH_API_HEADER_FILES_TO_SCAN} + "${CMAKE_SOURCE_DIR}/scripts/mk_consts_files.py" + ${Z3_GENERATED_FILE_EXTRA_DEPENDENCIES} + COMMENT "Generating ${Z3_JAVA_PACKAGE_NAME}.enumerations package" + ${ADD_CUSTOM_COMMAND_USES_TERMINAL_ARG} +) + +set(Z3_JAVA_JAR_SOURCE_FILES + AlgebraicNum.java + ApplyResultDecRefQueue.java + ApplyResult.java + ArithExpr.java + ArithSort.java + ArrayExpr.java + ArraySort.java + ASTDecRefQueue.java + AST.java + AstMapDecRefQueue.java + ASTMap.java + AstVectorDecRefQueue.java + ASTVector.java + BitVecExpr.java + BitVecNum.java + BitVecSort.java + BoolExpr.java + BoolSort.java + Constructor.java + ConstructorList.java + Context.java + DatatypeExpr.java + DatatypeSort.java + EnumSort.java + Expr.java + FiniteDomainExpr.java + FiniteDomainNum.java + FiniteDomainSort.java + FixedpointDecRefQueue.java + Fixedpoint.java + FPExpr.java + FPNum.java + FPRMExpr.java + FPRMNum.java + FPRMSort.java + FPSort.java + FuncDecl.java + FuncInterpDecRefQueue.java + FuncInterpEntryDecRefQueue.java + FuncInterp.java + Global.java + GoalDecRefQueue.java + Goal.java + IDecRefQueue.java + IDisposable.java + InterpolationContext.java + IntExpr.java + IntNum.java + IntSort.java + IntSymbol.java + ListSort.java + Log.java + ModelDecRefQueue.java + Model.java + OptimizeDecRefQueue.java + Optimize.java + ParamDescrsDecRefQueue.java + ParamDescrs.java + ParamsDecRefQueue.java + Params.java + Pattern.java + ProbeDecRefQueue.java + Probe.java + Quantifier.java + RatNum.java + RealExpr.java + RealSort.java + ReExpr.java + RelationSort.java + ReSort.java + SeqExpr.java + SeqSort.java + SetSort.java + SolverDecRefQueue.java + Solver.java + Sort.java + StatisticsDecRefQueue.java + Statistics.java + Status.java + StringSymbol.java + Symbol.java + TacticDecRefQueue.java + Tactic.java + TupleSort.java + UninterpretedSort.java + Version.java + Z3Exception.java + Z3Object.java +) +set(Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH "") +foreach (java_src_file ${Z3_JAVA_JAR_SOURCE_FILES}) + list(APPEND Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${java_src_file}") +endforeach() +# Add generated files to list +list(APPEND Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH + ${Z3_JAVA_NATIVE_JAVA} + ${Z3_JAVA_ENUMERATION_PACKAGE_FILES_FULL_PATH} +) + +# Convenient top-level target +add_custom_target(build_z3_java_bindings + ALL + DEPENDS + z3java + z3JavaJar +) + +# Rule to build ``com.microsoft.z3.jar`` +# TODO: Add version +# TODO: Should we set ``CMAKE_JNI_TARGET`` to ``TRUE``? +add_jar(z3JavaJar + SOURCES ${Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH} + OUTPUT_NAME ${Z3_JAVA_PACKAGE_NAME} + OUTPUT_DIR "${CMAKE_BINARY_DIR}" +) + +############################################################################### +# Install +############################################################################### +option(INSTALL_JAVA_BINDINGS "Install Java bindings when invoking install target" ON) +if (INSTALL_JAVA_BINDINGS) + # Provide cache variables for the install locations that the user can change. + # FIXME: I don't think these defaults install locations conform to the Debian + # packaging guidelines or any packaging guidelines for that matter... + set(Z3_JAVA_JAR_INSTALLDIR + "${CMAKE_INSTALL_LIBDIR}" + CACHE + PATH + "Directory to install Z3 Java jar file relative to install prefix" + ) + set(Z3_JAVA_JNI_LIB_INSTALLDIR + "${CMAKE_INSTALL_LIBDIR}" + CACHE + PATH + "Directory to install Z3 Java JNI bridge library relative to install prefix" + ) + install(TARGETS z3java + LIBRARY DESTINATION "${Z3_JAVA_JNI_LIB_INSTALLDIR}" + ) + install_jar(z3JavaJar DESTINATION "${Z3_JAVA_JAR_INSTALLDIR}") +endif() From 48550732bf122aac1bb35af3f8db8691f697ebcc Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 18 Apr 2016 11:18:47 +0100 Subject: [PATCH 22/66] [CMake] Add a few notes on finding the Java installation. --- README-CMake.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/README-CMake.md b/README-CMake.md index dd50c05fa..ca3ded9fb 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -290,6 +290,23 @@ Example ``` cmake -DCMAKE_BUILD_TYPE=Release -DENABLE_TRACING=FALSE ../ + +``` + +## Z3 API Bindings + +Z3 exposes various language bindings for its API. Below are some notes on building +and/or installing these bindings when building Z3 with CMake. + +### Java bindings + +The CMake build uses the ``FindJava`` and ``FindJNI`` cmake modules to detect the +installation of Java. If CMake fails to find your installation of Java set the +``JAVA_HOME`` environment variable when invoking CMake so that it points at the +correct location. For example + +``` +JAVA_HOME=/usr/lib/jvm/default cmake -DBUILD_JAVA_BINDINGS=ON ../ ``` ## Developer/packager notes From 8f697287036901b824d792a1cda02935f25f4320 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 18 Apr 2016 11:28:23 +0100 Subject: [PATCH 23/66] [CMake] Change default install location for ``com.microsoft.z3.jar``. It seems ``.jar`` files are typically installed into ``/usr/share/java``. --- contrib/cmake/src/api/java/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contrib/cmake/src/api/java/CMakeLists.txt b/contrib/cmake/src/api/java/CMakeLists.txt index e41fa5ed0..b0ac2bedb 100644 --- a/contrib/cmake/src/api/java/CMakeLists.txt +++ b/contrib/cmake/src/api/java/CMakeLists.txt @@ -214,14 +214,15 @@ add_jar(z3JavaJar option(INSTALL_JAVA_BINDINGS "Install Java bindings when invoking install target" ON) if (INSTALL_JAVA_BINDINGS) # Provide cache variables for the install locations that the user can change. - # FIXME: I don't think these defaults install locations conform to the Debian - # packaging guidelines or any packaging guidelines for that matter... + # This defaults to ``/usr/local/java`` which seems to be the location for ``.jar`` + # files on Linux distributions set(Z3_JAVA_JAR_INSTALLDIR - "${CMAKE_INSTALL_LIBDIR}" + "${CMAKE_INSTALL_DATAROOTDIR}/java" CACHE PATH "Directory to install Z3 Java jar file relative to install prefix" ) + # FIXME: I don't think this the right installation location set(Z3_JAVA_JNI_LIB_INSTALLDIR "${CMAKE_INSTALL_LIBDIR}" CACHE From a1e831184494da6b69bd95be022fe0518b2c632d Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 18 Apr 2016 11:31:09 +0100 Subject: [PATCH 24/66] [CMake] Add version to ``com.microsoft.z3.jar``. On Linux systems the ``.jar`` file is created as ``com.microsoft.z3-4.4.2.0.jar`` and a symlink named ``com.microsoft.z3.jar`` is created which points to it in the build and install directory. --- contrib/cmake/src/api/java/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/cmake/src/api/java/CMakeLists.txt b/contrib/cmake/src/api/java/CMakeLists.txt index b0ac2bedb..3663422f0 100644 --- a/contrib/cmake/src/api/java/CMakeLists.txt +++ b/contrib/cmake/src/api/java/CMakeLists.txt @@ -200,12 +200,12 @@ add_custom_target(build_z3_java_bindings ) # Rule to build ``com.microsoft.z3.jar`` -# TODO: Add version # TODO: Should we set ``CMAKE_JNI_TARGET`` to ``TRUE``? add_jar(z3JavaJar SOURCES ${Z3_JAVA_JAR_SOURCE_FILES_FULL_PATH} OUTPUT_NAME ${Z3_JAVA_PACKAGE_NAME} OUTPUT_DIR "${CMAKE_BINARY_DIR}" + VERSION "${Z3_VERSION}" ) ############################################################################### From a56a53e72db9a885fc50ea5007c342e64bf4cb9f Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 18 Apr 2016 12:07:18 +0100 Subject: [PATCH 25/66] [CMake] Fix installation location of ``com.microsoft.z3.jar`` when using CMake 2.8.12.2. It seems ``install_jar()`` in the version of ``UseJava.cmake`` shipped with that version of CMake doesn't handle the ``DESTINATION`` argument correctly and treats that as the installation location so CMake would install to ``/usr/local/DESTINATION`` rather than ``/usr/locale/share``. --- contrib/cmake/src/api/java/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contrib/cmake/src/api/java/CMakeLists.txt b/contrib/cmake/src/api/java/CMakeLists.txt index 3663422f0..49d4a8841 100644 --- a/contrib/cmake/src/api/java/CMakeLists.txt +++ b/contrib/cmake/src/api/java/CMakeLists.txt @@ -232,5 +232,7 @@ if (INSTALL_JAVA_BINDINGS) install(TARGETS z3java LIBRARY DESTINATION "${Z3_JAVA_JNI_LIB_INSTALLDIR}" ) - install_jar(z3JavaJar DESTINATION "${Z3_JAVA_JAR_INSTALLDIR}") + # Note: Don't use ``DESTINATION`` here as the version of ``UseJava.cmake`` shipped + # with CMake 2.8.12.2 handles that incorrectly. + install_jar(z3JavaJar "${Z3_JAVA_JAR_INSTALLDIR}") endif() From c6965d5cb2f8868a8a05c9b0fef5833e065e9378 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 18 Apr 2016 14:07:09 +0100 Subject: [PATCH 26/66] [CMake] Try to fix the Windows build when building the Java bindings. On Windows the ``z3java`` target is not a ``LIBRARY`` target so just drop the ``LIBRARY`` keyword here. --- contrib/cmake/src/api/java/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contrib/cmake/src/api/java/CMakeLists.txt b/contrib/cmake/src/api/java/CMakeLists.txt index 49d4a8841..6e16ddb74 100644 --- a/contrib/cmake/src/api/java/CMakeLists.txt +++ b/contrib/cmake/src/api/java/CMakeLists.txt @@ -229,9 +229,7 @@ if (INSTALL_JAVA_BINDINGS) PATH "Directory to install Z3 Java JNI bridge library relative to install prefix" ) - install(TARGETS z3java - LIBRARY DESTINATION "${Z3_JAVA_JNI_LIB_INSTALLDIR}" - ) + install(TARGETS z3java DESTINATION "${Z3_JAVA_JNI_LIB_INSTALLDIR}") # Note: Don't use ``DESTINATION`` here as the version of ``UseJava.cmake`` shipped # with CMake 2.8.12.2 handles that incorrectly. install_jar(z3JavaJar "${Z3_JAVA_JAR_INSTALLDIR}") From 49c92e4bd7c69d198974d6ec1616342fd28ea6c1 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Mon, 18 Apr 2016 15:33:19 +0100 Subject: [PATCH 27/66] [CMake] Add a note about the name of the Z3 Java ``.jar`` file. --- README-CMake.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README-CMake.md b/README-CMake.md index ca3ded9fb..0943e7a7d 100644 --- a/README-CMake.md +++ b/README-CMake.md @@ -308,6 +308,10 @@ correct location. For example ``` JAVA_HOME=/usr/lib/jvm/default cmake -DBUILD_JAVA_BINDINGS=ON ../ ``` +Note that the built ``.jar`` file is named ``com.microsoft.z3-VERSION.jar`` +where ``VERSION`` is the Z3 version. Under non Windows systems a +symbolic link named ``com.microsoft.z3.jar`` is provided. This symbolic +link is not created when building under Windows. ## Developer/packager notes From 3ffcea0fe4d651fde35378c8304abcc22ce2653f Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 18 Apr 2016 16:52:12 +0100 Subject: [PATCH 28/66] whitespace --- src/ast/ast.cpp | 384 ++++++++++++++++++++++---------------------- src/tactic/goal.cpp | 80 ++++----- 2 files changed, 232 insertions(+), 232 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 21a06a71b..0365a267e 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -82,7 +82,7 @@ void parameter::del_eh(ast_manager & m, family_id fid) { } } -bool parameter::operator==(parameter const & p) const { +bool parameter::operator==(parameter const & p) const { if (m_kind != p.m_kind) return false; switch(m_kind) { case PARAM_INT: return m_int == p.m_int; @@ -138,7 +138,7 @@ void display_parameters(std::ostream & out, unsigned n, parameter const * p) { // ----------------------------------- family_id family_manager::mk_family_id(symbol const & s) { - family_id r; + family_id r; if (m_families.find(s, r)) { return r; } @@ -150,7 +150,7 @@ family_id family_manager::mk_family_id(symbol const & s) { } family_id family_manager::get_family_id(symbol const & s) const { - family_id r; + family_id r; if (m_families.find(s, r)) return r; else @@ -167,7 +167,7 @@ bool family_manager::has_family(symbol const & s) const { // // ----------------------------------- -decl_info::decl_info(family_id family_id, decl_kind k, unsigned num_parameters, +decl_info::decl_info(family_id family_id, decl_kind k, unsigned num_parameters, parameter const * parameters, bool private_params): m_family_id(family_id), m_kind(k), @@ -179,7 +179,7 @@ decl_info::decl_info(decl_info const& other) : m_family_id(other.m_family_id), m_kind(other.m_kind), m_parameters(other.m_parameters.size(), other.m_parameters.c_ptr()), - m_private_parameters(other.m_private_parameters) { + m_private_parameters(other.m_private_parameters) { } @@ -212,7 +212,7 @@ unsigned decl_info::hash() const { } bool decl_info::operator==(decl_info const & info) const { - return m_family_id == info.m_family_id && m_kind == info.m_kind && + return m_family_id == info.m_family_id && m_kind == info.m_kind && compare_arrays(m_parameters.begin(), info.m_parameters.begin(), m_parameters.size()); } @@ -269,13 +269,13 @@ func_decl_info::func_decl_info(family_id family_id, decl_kind k, unsigned num_pa } bool func_decl_info::operator==(func_decl_info const & info) const { - return decl_info::operator==(info) && - m_left_assoc == info.m_left_assoc && - m_right_assoc == info.m_right_assoc && + return decl_info::operator==(info) && + m_left_assoc == info.m_left_assoc && + m_right_assoc == info.m_right_assoc && m_flat_associative == info.m_flat_associative && - m_commutative == info.m_commutative && + m_commutative == info.m_commutative && m_chainable == info.m_chainable && - m_pairwise == info.m_pairwise && + m_pairwise == info.m_pairwise && m_injective == info.m_injective && m_skolem == info.m_skolem; } @@ -394,7 +394,7 @@ quantifier::quantifier(bool forall, unsigned num_decls, sort * const * decl_sort sort * get_sort(expr const * n) { while (true) { switch(n->get_kind()) { - case AST_APP: + case AST_APP: return to_app(n)->get_decl()->get_range(); case AST_VAR: return to_var(n)->get_sort(); @@ -426,7 +426,7 @@ unsigned get_node_size(ast const * n) { default: UNREACHABLE(); } return 0; -} +} bool compare_nodes(ast const * n1, ast const * n2) { if (n1->get_kind() != n2->get_kind()) { @@ -452,32 +452,32 @@ bool compare_nodes(ast const * n1, ast const * n2) { to_func_decl(n1)->get_name() == to_func_decl(n2)->get_name() && to_func_decl(n1)->get_arity() == to_func_decl(n2)->get_arity() && to_func_decl(n1)->get_range() == to_func_decl(n2)->get_range() && - compare_arrays(to_func_decl(n1)->get_domain(), + compare_arrays(to_func_decl(n1)->get_domain(), to_func_decl(n2)->get_domain(), to_func_decl(n1)->get_arity()); case AST_APP: - return + return to_app(n1)->get_decl() == to_app(n2)->get_decl() && to_app(n1)->get_num_args() == to_app(n2)->get_num_args() && compare_arrays(to_app(n1)->get_args(), to_app(n2)->get_args(), to_app(n1)->get_num_args()); case AST_VAR: - return + return to_var(n1)->get_idx() == to_var(n2)->get_idx() && to_var(n1)->get_sort() == to_var(n2)->get_sort(); case AST_QUANTIFIER: - return + return to_quantifier(n1)->is_forall() == to_quantifier(n2)->is_forall() && to_quantifier(n1)->get_num_decls() == to_quantifier(n2)->get_num_decls() && compare_arrays(to_quantifier(n1)->get_decl_sorts(), to_quantifier(n2)->get_decl_sorts(), to_quantifier(n1)->get_num_decls()) && - to_quantifier(n1)->get_expr() == to_quantifier(n2)->get_expr() && - to_quantifier(n1)->get_weight() == to_quantifier(n2)->get_weight() && + to_quantifier(n1)->get_expr() == to_quantifier(n2)->get_expr() && + to_quantifier(n1)->get_weight() == to_quantifier(n2)->get_weight() && to_quantifier(n1)->get_num_patterns() == to_quantifier(n2)->get_num_patterns() && - compare_arrays(to_quantifier(n1)->get_patterns(), + compare_arrays(to_quantifier(n1)->get_patterns(), to_quantifier(n2)->get_patterns(), to_quantifier(n1)->get_num_patterns()) && - to_quantifier(n1)->get_num_no_patterns() == to_quantifier(n2)->get_num_no_patterns() && + to_quantifier(n1)->get_num_no_patterns() == to_quantifier(n2)->get_num_no_patterns() && compare_arrays(to_quantifier(n1)->get_no_patterns(), to_quantifier(n2)->get_no_patterns(), to_quantifier(n1)->get_num_no_patterns()); @@ -503,7 +503,7 @@ inline unsigned ast_array_hash(T * const * array, unsigned size, unsigned init_v default: { unsigned a, b, c; a = b = 0x9e3779b9; - c = init_value; + c = init_value; while (size >= 3) { size--; a += array[size]->hash(); @@ -529,7 +529,7 @@ unsigned get_asts_hash(unsigned sz, ast * const* ns, unsigned init) { return ast_array_hash(ns, sz, init); } unsigned get_apps_hash(unsigned sz, app * const* ns, unsigned init) { - return ast_array_hash(ns, sz, init); + return ast_array_hash(ns, sz, init); } unsigned get_exprs_hash(unsigned sz, expr * const* ns, unsigned init) { return ast_array_hash(ns, sz, init); @@ -543,19 +543,19 @@ unsigned get_decl_hash(unsigned sz, func_decl* const* ns, unsigned init) { unsigned get_node_hash(ast const * n) { unsigned a, b, c; - + switch (n->get_kind()) { case AST_SORT: - if (to_sort(n)->get_info() == 0) + if (to_sort(n)->get_info() == 0) return to_sort(n)->get_name().hash(); else return combine_hash(to_sort(n)->get_name().hash(), to_sort(n)->get_info()->hash()); case AST_FUNC_DECL: - return ast_array_hash(to_func_decl(n)->get_domain(), to_func_decl(n)->get_arity(), - to_func_decl(n)->get_info() == 0 ? + return ast_array_hash(to_func_decl(n)->get_domain(), to_func_decl(n)->get_arity(), + to_func_decl(n)->get_info() == 0 ? to_func_decl(n)->get_name().hash() : combine_hash(to_func_decl(n)->get_name().hash(), to_func_decl(n)->get_info()->hash())); case AST_APP: - return ast_array_hash(to_app(n)->get_args(), + return ast_array_hash(to_app(n)->get_args(), to_app(n)->get_num_args(), to_app(n)->get_decl()->hash()); case AST_VAR: @@ -645,7 +645,7 @@ basic_decl_plugin::basic_decl_plugin(): m_not_decl(0), m_interp_decl(0), m_implies_decl(0), - + m_proof_sort(0), m_undef_decl(0), m_true_pr_decl(0), @@ -683,10 +683,10 @@ basic_decl_plugin::basic_decl_plugin(): bool basic_decl_plugin::check_proof_sorts(basic_op_kind k, unsigned arity, sort * const * domain) const { if (k == PR_UNDEF) return arity == 0; - if (arity == 0) + if (arity == 0) return false; else { - for (unsigned i = 0; i < arity - 1; i++) + for (unsigned i = 0; i < arity - 1; i++) if (domain[i] != m_proof_sort) return false; return domain[arity-1] == m_bool_sort || domain[arity-1] == m_proof_sort; @@ -696,14 +696,14 @@ bool basic_decl_plugin::check_proof_sorts(basic_op_kind k, unsigned arity, sort bool basic_decl_plugin::check_proof_args(basic_op_kind k, unsigned num_args, expr * const * args) const { if (k == PR_UNDEF) return num_args == 0; - if (num_args == 0) + if (num_args == 0) return false; else { - for (unsigned i = 0; i < num_args - 1; i++) + for (unsigned i = 0; i < num_args - 1; i++) if (m_manager->get_sort(args[i]) != m_proof_sort) return false; - return - m_manager->get_sort(args[num_args - 1]) == m_bool_sort || + return + m_manager->get_sort(args[num_args - 1]) == m_bool_sort || m_manager->get_sort(args[num_args - 1]) == m_proof_sort; } } @@ -711,7 +711,7 @@ bool basic_decl_plugin::check_proof_args(basic_op_kind k, unsigned num_args, exp func_decl * basic_decl_plugin::mk_bool_op_decl(char const * name, basic_op_kind k, unsigned num_args, bool assoc, bool comm, bool idempotent, bool flat_associative, bool chainable) { ptr_buffer domain; - for (unsigned i = 0; i < num_args; i++) + for (unsigned i = 0; i < num_args; i++) domain.push_back(m_bool_sort); func_decl_info info(m_family_id, k); info.set_associative(assoc); @@ -734,10 +734,10 @@ func_decl * basic_decl_plugin::mk_implies_decl() { } func_decl * basic_decl_plugin::mk_proof_decl( - char const * name, basic_op_kind k, + char const * name, basic_op_kind k, unsigned num_parameters, parameter const* params, unsigned num_parents) { ptr_buffer domain; - for (unsigned i = 0; i < num_parents; i++) + for (unsigned i = 0; i < num_parents; i++) domain.push_back(m_proof_sort); domain.push_back(m_bool_sort); func_decl_info info(m_family_id, k, num_parameters, params); @@ -746,7 +746,7 @@ func_decl * basic_decl_plugin::mk_proof_decl( func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k, unsigned num_parents) { ptr_buffer domain; - for (unsigned i = 0; i < num_parents; i++) + for (unsigned i = 0; i < num_parents; i++) domain.push_back(m_proof_sort); domain.push_back(m_bool_sort); func_decl * d = m_manager->mk_func_decl(symbol(name), num_parents+1, domain.c_ptr(), m_proof_sort, func_decl_info(m_family_id, k)); @@ -756,7 +756,7 @@ func_decl * basic_decl_plugin::mk_proof_decl(char const * name, basic_op_kind k, func_decl * basic_decl_plugin::mk_compressed_proof_decl(char const * name, basic_op_kind k, unsigned num_parents) { ptr_buffer domain; - for (unsigned i = 0; i < num_parents; i++) + for (unsigned i = 0; i < num_parents; i++) domain.push_back(m_proof_sort); func_decl * d = m_manager->mk_func_decl(symbol(name), num_parents, domain.c_ptr(), m_proof_sort, func_decl_info(m_family_id, k)); m_manager->inc_ref(d); @@ -793,7 +793,7 @@ func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_param #define MK_DECL(_decl_,_mk_decl_) if (!_decl_) _decl_ = _mk_decl_; return _decl_; - + func_decl * basic_decl_plugin::mk_proof_decl(char const* name, basic_op_kind k, unsigned num_parents, func_decl*& fn) { if (!fn) { fn = mk_proof_decl(name, k, num_parents); @@ -805,11 +805,11 @@ func_decl * basic_decl_plugin::mk_proof_decl(basic_op_kind k, unsigned num_paren SASSERT(k == PR_UNDEF || m_manager->proofs_enabled()); switch (static_cast(k)) { // - // A description of the semantics of the proof + // A description of the semantics of the proof // declarations is provided in z3_api.h // case PR_UNDEF: return m_undef_decl; - case PR_TRUE: return mk_proof_decl("true-axiom", k, 0, m_true_pr_decl); + case PR_TRUE: return mk_proof_decl("true-axiom", k, 0, m_true_pr_decl); case PR_ASSERTED: return mk_proof_decl("asserted", k, 0, m_asserted_decl); case PR_GOAL: return mk_proof_decl("goal", k, 2, m_goal_decl); case PR_MODUS_PONENS: return mk_proof_decl("mp", k, 2, m_modus_ponens_decl); @@ -869,11 +869,11 @@ void basic_decl_plugin::set_manager(ast_manager * m, family_id id) { m_not_decl = mk_bool_op_decl("not", OP_NOT, 1); m_interp_decl = mk_bool_op_decl("interp", OP_INTERP, 1); m_implies_decl = mk_implies_decl(); - + m_proof_sort = m->mk_sort(symbol("Proof"), sort_info(id, PROOF_SORT)); m->inc_ref(m_proof_sort); - - m_undef_decl = mk_compressed_proof_decl("undef", PR_UNDEF, 0); + + m_undef_decl = mk_compressed_proof_decl("undef", PR_UNDEF, 0); } void basic_decl_plugin::get_sort_names(svector & sort_names, symbol const & logic) { @@ -884,7 +884,7 @@ void basic_decl_plugin::get_sort_names(svector & sort_names, symbo void basic_decl_plugin::get_op_names(svector & op_names, symbol const & logic) { op_names.push_back(builtin_name("true", OP_TRUE)); - op_names.push_back(builtin_name("false", OP_FALSE)); + op_names.push_back(builtin_name("false", OP_FALSE)); op_names.push_back(builtin_name("=", OP_EQ)); op_names.push_back(builtin_name("distinct", OP_DISTINCT)); op_names.push_back(builtin_name("ite", OP_ITE)); @@ -931,7 +931,7 @@ void basic_decl_plugin::finalize() { DEC_REF(m_implies_decl); DEC_ARRAY_REF(m_eq_decls); DEC_ARRAY_REF(m_ite_decls); - + DEC_ARRAY_REF(m_oeq_decls); DEC_REF(m_proof_sort); DEC_REF(m_undef_decl); @@ -1072,7 +1072,7 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters case OP_DISTINCT: { func_decl_info info(m_family_id, OP_DISTINCT); info.set_pairwise(); - for (unsigned i = 1; i < arity; i++) { + for (unsigned i = 1; i < arity; i++) { if (domain[i] != domain[0]) { std::ostringstream buffer; buffer << "Sort mismatch between first argument and argument " << (i+1); @@ -1086,7 +1086,7 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters } SASSERT(is_proof(k)); - + if (!check_proof_sorts(static_cast(k), arity, domain)) m_manager->raise_exception("Invalid proof object."); @@ -1120,7 +1120,7 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters SASSERT(is_proof(k)); - if (!check_proof_args(static_cast(k), num_args, args)) + if (!check_proof_args(static_cast(k), num_args, args)) m_manager->raise_exception("Invalid proof object."); if (num_parameters == 0) { @@ -1135,19 +1135,19 @@ expr * basic_decl_plugin::get_some_value(sort * s) { return 0; } -bool basic_recognizers::is_ite(expr const * n, expr * & t1, expr * & t2, expr * & t3) const { - if (is_ite(n)) { - t1 = to_app(n)->get_arg(0); - t2 = to_app(n)->get_arg(1); +bool basic_recognizers::is_ite(expr const * n, expr * & t1, expr * & t2, expr * & t3) const { + if (is_ite(n)) { + t1 = to_app(n)->get_arg(0); + t2 = to_app(n)->get_arg(1); t3 = to_app(n)->get_arg(2); - return true; - } - return false; + return true; + } + return false; } // ----------------------------------- // -// label_decl_plugin +// label_decl_plugin // // ----------------------------------- @@ -1169,7 +1169,7 @@ sort * label_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, paramete return 0; } -func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, +func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { if (k == OP_LABEL) { if (arity != 1 || num_parameters < 2 || !parameters[0].is_int() || !parameters[1].is_symbol() || !m_manager->is_bool(domain[0])) { @@ -1182,7 +1182,7 @@ func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters return 0; } } - return m_manager->mk_func_decl(parameters[0].get_int() ? m_lblpos : m_lblneg, arity, domain, domain[0], + return m_manager->mk_func_decl(parameters[0].get_int() ? m_lblpos : m_lblneg, arity, domain, domain[0], func_decl_info(m_family_id, OP_LABEL, num_parameters, parameters)); } else { @@ -1204,7 +1204,7 @@ func_decl * label_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters // ----------------------------------- // -// pattern_decl_plugin +// pattern_decl_plugin // // ----------------------------------- @@ -1213,16 +1213,16 @@ sort * pattern_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, parame return 0; } -func_decl * pattern_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, +func_decl * pattern_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { - return m_manager->mk_func_decl(symbol("pattern"), arity, domain, + return m_manager->mk_func_decl(symbol("pattern"), arity, domain, m_manager->mk_bool_sort(), // the range can be an arbitrary sort. func_decl_info(m_family_id, OP_PATTERN)); } // ----------------------------------- // -// model_value_decl_plugin +// model_value_decl_plugin // // ----------------------------------- @@ -1231,7 +1231,7 @@ sort * model_value_decl_plugin::mk_sort(decl_kind k, unsigned num_parameters, pa return 0; } -func_decl * model_value_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, +func_decl * model_value_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { SASSERT(k == OP_MODEL_VALUE); if (arity != 0 || num_parameters != 2 || !parameters[0].is_int() || !parameters[1].is_ast() || !is_sort(parameters[1].get_ast())) { @@ -1269,7 +1269,7 @@ sort * user_sort_plugin::mk_sort(decl_kind k, unsigned num_parameters, parameter return m_manager->mk_sort(m_sort_names[k], si); } -func_decl * user_sort_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, +func_decl * user_sort_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { UNREACHABLE(); return 0; @@ -1289,7 +1289,7 @@ decl_plugin * user_sort_plugin::mk_fresh() { user_sort_plugin * p = alloc(user_sort_plugin); svector::iterator it = m_sort_names.begin(); svector::iterator end = m_sort_names.end(); - for (; it != end; ++it) + for (; it != end; ++it) p->register_name(*it); return p; } @@ -1318,7 +1318,7 @@ ast_manager::ast_manager(proof_gen_mode m, char const * trace_file, bool is_form if (!is_format_manager) m_format_manager = alloc(ast_manager, PGM_DISABLED, m_trace_stream, true); - else + else m_format_manager = 0; init(); } @@ -1335,7 +1335,7 @@ ast_manager::ast_manager(proof_gen_mode m, std::fstream * trace_stream, bool is_ if (!is_format_manager) m_format_manager = alloc(ast_manager, PGM_DISABLED, trace_stream, true); - else + else m_format_manager = 0; init(); } @@ -1406,7 +1406,7 @@ ast_manager::~ast_manager() { dealloc(*it); } DEBUG_CODE({ - if (!m_ast_table.empty()) + if (!m_ast_table.empty()) std::cout << "ast_manager LEAKED: " << m_ast_table.size() << std::endl; }); #if 1 @@ -1440,14 +1440,14 @@ void ast_manager::compact_memory() { m_alloc.consolidate(); unsigned capacity = m_ast_table.capacity(); if (capacity > 4*m_ast_table.size()) { - ast_table new_ast_table; + ast_table new_ast_table; ast_table::iterator it = m_ast_table.begin(); ast_table::iterator end = m_ast_table.end(); for (; it != end; ++it) { new_ast_table.insert(*it); } m_ast_table.swap(new_ast_table); - IF_VERBOSE(10, verbose_stream() << "(ast-table :prev-capacity " << capacity + IF_VERBOSE(10, verbose_stream() << "(ast-table :prev-capacity " << capacity << " :capacity " << m_ast_table.capacity() << " :size " << m_ast_table.size() << ")\n";); } else { @@ -1490,24 +1490,24 @@ void ast_manager::copy_families_plugins(ast_manager const & from) { SASSERT(from.is_builtin_family_id(fid) == is_builtin_family_id(fid)); SASSERT(!from.is_builtin_family_id(fid) || m_family_manager.has_family(fid)); symbol fid_name = from.get_family_name(fid); - TRACE("copy_families_plugins", tout << "copying: " << fid_name << ", src fid: " << fid + TRACE("copy_families_plugins", tout << "copying: " << fid_name << ", src fid: " << fid << ", target has_family: " << m_family_manager.has_family(fid) << "\n"; if (m_family_manager.has_family(fid)) tout << get_family_id(fid_name) << "\n";); if (!m_family_manager.has_family(fid)) { - family_id new_fid = mk_family_id(fid_name); + family_id new_fid = mk_family_id(fid_name); TRACE("copy_families_plugins", tout << "new target fid created: " << new_fid << " fid_name: " << fid_name << "\n";); } TRACE("copy_families_plugins", tout << "target fid: " << get_family_id(fid_name) << "\n";); SASSERT(fid == get_family_id(fid_name)); if (from.has_plugin(fid) && !has_plugin(fid)) { - decl_plugin * new_p = from.get_plugin(fid)->mk_fresh(); + decl_plugin * new_p = from.get_plugin(fid)->mk_fresh(); register_plugin(fid, new_p); SASSERT(new_p->get_family_id() == fid); SASSERT(has_plugin(fid)); } SASSERT(from.m_family_manager.has_family(fid) == m_family_manager.has_family(fid)); SASSERT(from.get_family_id(fid_name) == get_family_id(fid_name)); - SASSERT(!from.has_plugin(fid) || has_plugin(fid)); + SASSERT(!from.has_plugin(fid) || has_plugin(fid)); } } @@ -1524,7 +1524,7 @@ void ast_manager::set_next_expr_id(unsigned id) { if (it == end) return; // id is in use, move to the next one. - id++; + id++; } } @@ -1604,7 +1604,7 @@ bool ast_manager::slow_not_contains(ast const * n) { for (; it != end; ++it) { ast * curr = *it; if (compare_nodes(curr, n)) { - TRACE("nondet_bug", + TRACE("nondet_bug", tout << "id1: " << curr->get_id() << ", id2: " << n->get_id() << "\n"; tout << "hash1: " << get_node_hash(curr) << ", hash2: " << get_node_hash(n) << "\n";); return false; @@ -1621,7 +1621,7 @@ bool ast_manager::slow_not_contains(ast const * n) { #endif ast * ast_manager::register_node_core(ast * n) { - unsigned h = get_node_hash(n); + unsigned h = get_node_hash(n); n->m_hash = h; #ifdef Z3DEBUG bool contains = m_ast_table.contains(n); @@ -1662,7 +1662,7 @@ ast * ast_manager::register_node_core(ast * n) { n->m_id = is_decl(n) ? m_decl_id_gen.mk() : m_expr_id_gen.mk(); - + TRACE("ast", tout << "Object " << n->m_id << " was created.\n";); TRACE("mk_var_bug", tout << "mk_ast: " << n->m_id << "\n";); // increment reference counters @@ -1722,7 +1722,7 @@ ast * ast_manager::register_node_core(ast * n) { default: UNREACHABLE(); } - if (arg_depth > depth) + if (arg_depth > depth) depth = arg_depth; } depth++; @@ -1756,7 +1756,7 @@ void ast_manager::delete_node(ast * n) { while (!worklist.empty()) { n = worklist.back(); worklist.pop_back(); - + TRACE("ast", tout << "Deleting object " << n->m_id << " " << n << "\n";); CTRACE("del_quantifier", is_quantifier(n), tout << "deleting quantifier " << n->m_id << " " << n << "\n";); TRACE("mk_var_bug", tout << "del_ast: " << n->m_id << "\n";); @@ -1771,20 +1771,20 @@ void ast_manager::delete_node(ast * n) { if (!m_debug_ref_count) { if (is_decl(n)) m_decl_id_gen.recycle(n->m_id); - else + else m_expr_id_gen.recycle(n->m_id); } #endif switch (n->get_kind()) { case AST_SORT: - if (to_sort(n)->m_info != 0 && !m_debug_ref_count) { + if (to_sort(n)->m_info != 0 && !m_debug_ref_count) { sort_info * info = to_sort(n)->get_info(); info->del_eh(*this); - dealloc(info); + dealloc(info); } break; case AST_FUNC_DECL: - if (to_func_decl(n)->m_info != 0 && !m_debug_ref_count) { + if (to_func_decl(n)->m_info != 0 && !m_debug_ref_count) { func_decl_info * info = to_func_decl(n)->get_info(); info->del_eh(*this); dealloc(info); @@ -1821,7 +1821,7 @@ sort * ast_manager::mk_sort(family_id fid, decl_kind k, unsigned num_parameters, return p->mk_sort(k, num_parameters, parameters); return 0; } - + func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned arity, sort * const * domain, sort * range) { decl_plugin * p = get_plugin(fid); @@ -1830,13 +1830,13 @@ func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_p return 0; } -func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, +func_decl * ast_manager::mk_func_decl(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { decl_plugin * p = get_plugin(fid); if (p) return p->mk_func_decl(k, num_parameters, parameters, num_args, args, range); return 0; -} +} app * ast_manager::mk_app(family_id fid, decl_kind k, unsigned num_parameters, parameter const * parameters, unsigned num_args, expr * const * args, sort * range) { @@ -1867,7 +1867,7 @@ app * ast_manager::mk_app(family_id fid, decl_kind k, expr * arg1, expr * arg2, sort * ast_manager::mk_sort(symbol const & name, sort_info * info) { unsigned sz = sort::get_obj_size(); void * mem = allocate_node(sz); - sort * new_node = new (mem) sort(name, info); + sort * new_node = new (mem) sort(name, info); return register_node(new_node); } @@ -1877,7 +1877,7 @@ sort * ast_manager::mk_uninterpreted_sort(symbol const & name, unsigned num_para return plugin->mk_sort(kind, num_parameters, parameters); } -func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, +func_decl * ast_manager::mk_func_decl(symbol const & name, unsigned arity, sort * const * domain, sort * range, bool assoc, bool comm, bool inj) { func_decl_info info(null_family_id, null_decl_kind); info.set_associative(assoc); @@ -1931,8 +1931,8 @@ void ast_manager::check_sort(func_decl const * decl, unsigned num_args, expr * c } /** - \brief Shallow sort checker. - Return true if success. + \brief Shallow sort checker. + Return true if success. If n == 0, then fail. If n is an application, checks whether the arguments of n match the expected types. */ @@ -1940,18 +1940,18 @@ void ast_manager::check_sorts_core(ast const * n) const { if (!n) { throw ast_exception("expression is null"); } - if (n->get_kind() != AST_APP) + if (n->get_kind() != AST_APP) return; // nothing else to check... app const * a = to_app(n); func_decl* d = a->get_decl(); check_sort(d, a->get_num_args(), a->get_args()); if (a->get_num_args() == 2 && - !d->is_flat_associative() && + !d->is_flat_associative() && d->is_right_associative()) { check_sorts_core(a->get_arg(1)); } if (a->get_num_args() == 2 && - !d->is_flat_associative() && + !d->is_flat_associative() && d->is_left_associative()) { check_sorts_core(a->get_arg(0)); } @@ -1991,7 +1991,7 @@ bool ast_manager::coercion_needed(func_decl * decl, unsigned num_args, expr * co if (decl->get_arity() != num_args) { // Invalid input: unexpected number of arguments for non-associative operator. // So, there is no point in coercing the input arguments. - return false; + return false; } for (unsigned i = 0; i < num_args; i++) { sort * d = decl->get_domain(i); @@ -2008,7 +2008,7 @@ app * ast_manager::mk_app_core(func_decl * decl, unsigned num_args, expr * const unsigned sz = app::get_obj_size(num_args); void * mem = allocate_node(sz); - try { + try { if (m_int_real_coercions && coercion_needed(decl, num_args, args)) { expr_ref_buffer new_args(*this); if (decl->is_associative()) { @@ -2082,11 +2082,11 @@ void ast_manager::check_args(func_decl* f, unsigned n, expr* const* es) { 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 " + 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()); + throw ast_exception(buffer.str().c_str()); } } } @@ -2099,17 +2099,17 @@ inline app * ast_manager::mk_app_core(func_decl * decl, expr * arg1, expr * arg2 app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * args) { - bool type_error = - decl->get_arity() != num_args && !decl->is_right_associative() && + bool type_error = + decl->get_arity() != num_args && !decl->is_right_associative() && !decl->is_left_associative() && !decl->is_chainable(); - type_error |= (decl->get_arity() != num_args && num_args < 2 && + type_error |= (decl->get_arity() != num_args && num_args < 2 && decl->get_family_id() == m_basic_family_id && !decl->is_associative()); if (type_error) { std::ostringstream buffer; - buffer << "Wrong number of arguments (" << num_args - << ") passed to function " << mk_pp(decl, *this); + buffer << "Wrong number of arguments (" << num_args + << ") passed to function " << mk_pp(decl, *this); throw ast_exception(buffer.str().c_str()); } app * r = 0; @@ -2148,7 +2148,7 @@ app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * ar -func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity, +func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity, sort * const * domain, sort * range) { func_decl_info info(null_family_id, null_decl_kind); info.m_skolem = true; @@ -2200,7 +2200,7 @@ app * ast_manager::mk_label(bool pos, unsigned num_names, symbol const * names, SASSERT(get_sort(n) == m_bool_sort); buffer p; p.push_back(parameter(static_cast(pos))); - for (unsigned i = 0; i < num_names; i++) + for (unsigned i = 0; i < num_names; i++) p.push_back(parameter(names[i])); return mk_app(m_label_family_id, OP_LABEL, p.size(), p.c_ptr(), 1, &n); } @@ -2215,7 +2215,7 @@ bool ast_manager::is_label(expr const * n, bool & pos, buffer & names) c } func_decl const * decl = to_app(n)->get_decl(); pos = decl->get_parameter(0).get_int() != 0; - for (unsigned i = 1; i < decl->get_num_parameters(); i++) + for (unsigned i = 1; i < decl->get_num_parameters(); i++) names.push_back(decl->get_parameter(i).get_symbol()); return true; } @@ -2223,7 +2223,7 @@ bool ast_manager::is_label(expr const * n, bool & pos, buffer & names) c app * ast_manager::mk_label_lit(unsigned num_names, symbol const * names) { SASSERT(num_names > 0); buffer p; - for (unsigned i = 0; i < num_names; i++) + for (unsigned i = 0; i < num_names; i++) p.push_back(parameter(names[i])); return mk_app(m_label_family_id, OP_LABEL_LIT, p.size(), p.c_ptr(), 0, 0); } @@ -2237,7 +2237,7 @@ bool ast_manager::is_label_lit(expr const * n, buffer & names) const { return false; } func_decl const * decl = to_app(n)->get_decl(); - for (unsigned i = 0; i < decl->get_num_parameters(); i++) + for (unsigned i = 0; i < decl->get_num_parameters(); i++) names.push_back(decl->get_parameter(i).get_symbol()); return true; } @@ -2262,9 +2262,9 @@ bool ast_manager::is_pattern(expr const * n) const { return true; } -quantifier * ast_manager::mk_quantifier(bool forall, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, +quantifier * ast_manager::mk_quantifier(bool forall, unsigned num_decls, sort * const * decl_sorts, symbol const * decl_names, expr * body, int weight , symbol const & qid, symbol const & skid, - unsigned num_patterns, expr * const * patterns, + unsigned num_patterns, expr * const * patterns, unsigned num_no_patterns, expr * const * no_patterns) { SASSERT(body); SASSERT(num_patterns == 0 || num_no_patterns == 0); @@ -2279,14 +2279,14 @@ quantifier * ast_manager::mk_quantifier(bool forall, unsigned num_decls, sort * weight, qid, skid, num_patterns, patterns, num_no_patterns, no_patterns); quantifier * r = register_node(new_node); - + if (m_trace_stream && r == new_node) { *m_trace_stream << "[mk-quant] #" << r->get_id() << " " << qid; for (unsigned i = 0; i < num_patterns; ++i) { *m_trace_stream << " #" << patterns[i]->get_id(); } *m_trace_stream << " #" << body->get_id() << "\n"; - + } return r; @@ -2447,7 +2447,7 @@ expr_dependency * ast_manager::mk_leaf(expr * t) { if (t == 0) return 0; else - return m_expr_dependency_manager.mk_leaf(t); + return m_expr_dependency_manager.mk_leaf(t); } expr_dependency * ast_manager::mk_join(unsigned n, expr * const * ts) { @@ -2514,7 +2514,7 @@ bool ast_manager::is_fully_interp(sort const * s) const { // ----------------------------------- proof * ast_manager::mk_proof(family_id fid, decl_kind k, unsigned num_args, expr * const * args) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(fid, k, num_args, args); } @@ -2538,23 +2538,23 @@ proof * ast_manager::mk_true_proof() { return mk_proof(m_basic_family_id, PR_TRUE, f); } -proof * ast_manager::mk_asserted(expr * f) { +proof * ast_manager::mk_asserted(expr * f) { CTRACE("mk_asserted_bug", !is_bool(f), tout << mk_ismt2_pp(f, *this) << "\nsort: " << mk_ismt2_pp(get_sort(f), *this) << "\n";); SASSERT(is_bool(f)); - return mk_proof(m_basic_family_id, PR_ASSERTED, f); + return mk_proof(m_basic_family_id, PR_ASSERTED, f); } -proof * ast_manager::mk_goal(expr * f) { +proof * ast_manager::mk_goal(expr * f) { SASSERT(is_bool(f)); - return mk_proof(m_basic_family_id, PR_GOAL, f); + return mk_proof(m_basic_family_id, PR_GOAL, f); } proof * ast_manager::mk_modus_ponens(proof * p1, proof * p2) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(p1)); SASSERT(has_fact(p2)); - CTRACE("mk_modus_ponens", !(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2))), + CTRACE("mk_modus_ponens", !(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2))), tout << mk_ll_pp(p1, *this) << "\n"; tout << mk_ll_pp(p2, *this) << "\n";); SASSERT(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2))); @@ -2564,21 +2564,21 @@ proof * ast_manager::mk_modus_ponens(proof * p1, proof * p2) { if (is_reflexivity(p2)) return p1; expr * f = to_app(get_fact(p2))->get_arg(1); - if (is_oeq(get_fact(p2))) + if (is_oeq(get_fact(p2))) return mk_app(m_basic_family_id, PR_MODUS_PONENS_OEQ, p1, p2, f); return mk_app(m_basic_family_id, PR_MODUS_PONENS, p1, p2, f); } proof * ast_manager::mk_reflexivity(expr * e) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; - return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_eq(e, e)); + return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_eq(e, e)); } -proof * ast_manager::mk_oeq_reflexivity(expr * e) { - if (m_proof_mode == PGM_DISABLED) +proof * ast_manager::mk_oeq_reflexivity(expr * e) { + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; - return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_oeq(e, e)); + return mk_app(m_basic_family_id, PR_REFLEXIVITY, mk_oeq(e, e)); } proof * ast_manager::mk_commutativity(app * f) { @@ -2591,7 +2591,7 @@ proof * ast_manager::mk_commutativity(app * f) { \brief Given a proof of p, return a proof of (p <=> true) */ proof * ast_manager::mk_iff_true(proof * pr) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(pr)); SASSERT(is_bool(get_fact(pr))); @@ -2602,7 +2602,7 @@ proof * ast_manager::mk_iff_true(proof * pr) { \brief Given a proof of (not p), return a proof of (p <=> false) */ proof * ast_manager::mk_iff_false(proof * pr) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(pr)); SASSERT(is_not(get_fact(pr))); @@ -2611,7 +2611,7 @@ proof * ast_manager::mk_iff_false(proof * pr) { } proof * ast_manager::mk_symmetry(proof * p) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; if (!p) return p; @@ -2622,12 +2622,12 @@ proof * ast_manager::mk_symmetry(proof * p) { SASSERT(has_fact(p)); SASSERT(is_app(get_fact(p))); SASSERT(to_app(get_fact(p))->get_num_args() == 2); - return mk_app(m_basic_family_id, PR_SYMMETRY, p, + return mk_app(m_basic_family_id, PR_SYMMETRY, p, mk_app(to_app(get_fact(p))->get_decl(), to_app(get_fact(p))->get_arg(1), to_app(get_fact(p))->get_arg(0))); } proof * ast_manager::mk_transitivity(proof * p1, proof * p2) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; if (!p1) return p2; @@ -2644,11 +2644,11 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2) { tout << mk_pp(to_app(get_fact(p1))->get_decl(), *this) << "\n"; tout << mk_pp(to_app(get_fact(p2))->get_decl(), *this) << "\n";); SASSERT(to_app(get_fact(p1))->get_decl() == to_app(get_fact(p2))->get_decl() || - ((is_iff(get_fact(p1)) || is_eq(get_fact(p1))) && + ((is_iff(get_fact(p1)) || is_eq(get_fact(p1))) && (is_iff(get_fact(p2)) || is_eq(get_fact(p2)))) || ( (is_eq(get_fact(p1)) || is_oeq(get_fact(p1))) && (is_eq(get_fact(p2)) || is_oeq(get_fact(p2))))); - CTRACE("mk_transitivity", to_app(get_fact(p1))->get_arg(1) != to_app(get_fact(p2))->get_arg(0), + CTRACE("mk_transitivity", to_app(get_fact(p1))->get_arg(1) != to_app(get_fact(p2))->get_arg(0), tout << mk_pp(get_fact(p1), *this) << "\n\n" << mk_pp(get_fact(p2), *this) << "\n"; tout << mk_bounded_pp(p1, *this, 5) << "\n\n"; tout << mk_bounded_pp(p2, *this, 5) << "\n\n"; @@ -2660,7 +2660,7 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2) { return p1; // OEQ is compatible with EQ for transitivity. func_decl* f = to_app(get_fact(p1))->get_decl(); - if (is_oeq(get_fact(p2))) f = to_app(get_fact(p2))->get_decl(); + if (is_oeq(get_fact(p2))) f = to_app(get_fact(p2))->get_decl(); return mk_app(m_basic_family_id, PR_TRANSITIVITY, p1, p2, mk_app(f, to_app(get_fact(p1))->get_arg(0), to_app(get_fact(p2))->get_arg(1))); } @@ -2673,19 +2673,19 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2, proof * p3, proof * } proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(num_proofs > 0); proof * r = proofs[0]; - for (unsigned i = 1; i < num_proofs; i++) + for (unsigned i = 1; i < num_proofs; i++) r = mk_transitivity(r, proofs[i]); return r; } proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs, expr * n1, expr * n2) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; - if (fine_grain_proofs()) + if (fine_grain_proofs()) return mk_transitivity(num_proofs, proofs); SASSERT(num_proofs > 0); if (num_proofs == 1) @@ -2703,7 +2703,7 @@ proof * ast_manager::mk_transitivity(unsigned num_proofs, proof * const * proofs } proof * ast_manager::mk_monotonicity(func_decl * R, app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(f1->get_num_args() == f2->get_num_args()); SASSERT(f1->get_decl() == f2->get_decl()); @@ -2714,7 +2714,7 @@ proof * ast_manager::mk_monotonicity(func_decl * R, app * f1, app * f2, unsigned } proof * ast_manager::mk_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(get_sort(f1) == get_sort(f2)); sort * s = get_sort(f1); @@ -2723,7 +2723,7 @@ proof * ast_manager::mk_congruence(app * f1, app * f2, unsigned num_proofs, proo } proof * ast_manager::mk_oeq_congruence(app * f1, app * f2, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(get_sort(f1) == get_sort(f2)); sort * s = get_sort(f1); @@ -2732,11 +2732,11 @@ proof * ast_manager::mk_oeq_congruence(app * f1, app * f2, unsigned num_proofs, } proof * ast_manager::mk_quant_intro(quantifier * q1, quantifier * q2, proof * p) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; if (!p) { return 0; - } + } SASSERT(q1->get_num_decls() == q2->get_num_decls()); SASSERT(has_fact(p)); SASSERT(is_iff(get_fact(p))); @@ -2744,7 +2744,7 @@ proof * ast_manager::mk_quant_intro(quantifier * q1, quantifier * q2, proof * p) } proof * ast_manager::mk_oeq_quant_intro(quantifier * q1, quantifier * q2, proof * p) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(q1->get_num_decls() == q2->get_num_decls()); SASSERT(has_fact(p)); @@ -2753,25 +2753,25 @@ proof * ast_manager::mk_oeq_quant_intro(quantifier * q1, quantifier * q2, proof } proof * ast_manager::mk_distributivity(expr * s, expr * r) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_DISTRIBUTIVITY, mk_eq(s, r)); } proof * ast_manager::mk_rewrite(expr * s, expr * t) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_REWRITE, mk_eq(s, t)); } proof * ast_manager::mk_oeq_rewrite(expr * s, expr * t) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_REWRITE, mk_oeq(s, t)); } proof * ast_manager::mk_rewrite_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); @@ -2780,37 +2780,37 @@ proof * ast_manager::mk_rewrite_star(expr * s, expr * t, unsigned num_proofs, pr } proof * ast_manager::mk_pull_quant(expr * e, quantifier * q) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_PULL_QUANT, mk_iff(e, q)); } proof * ast_manager::mk_pull_quant_star(expr * e, quantifier * q) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_PULL_QUANT_STAR, mk_iff(e, q)); } proof * ast_manager::mk_push_quant(quantifier * q, expr * e) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_PUSH_QUANT, mk_iff(q, e)); } proof * ast_manager::mk_elim_unused_vars(quantifier * q, expr * e) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_ELIM_UNUSED_VARS, mk_iff(q, e)); } proof * ast_manager::mk_der(quantifier * q, expr * e) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_DER, mk_iff(q, e)); } proof * ast_manager::mk_quant_inst(expr * not_q_or_i, unsigned num_bind, expr* const* binding) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; vector params; for (unsigned i = 0; i < num_bind; ++i) { @@ -2845,7 +2845,7 @@ bool ast_manager::is_rewrite(expr const* e, expr*& r1, expr*& r2) const { } proof * ast_manager::mk_def_axiom(expr * ax) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_DEF_AXIOM, ax); } @@ -2879,7 +2879,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro expr const * _fact = get_fact(proofs[j]); if (is_complement(lit, _fact)) { found_complement = true; - DEBUG_CODE(found.setx(j, true, false); continue;); + DEBUG_CODE(found.setx(j, true, false); continue;); break; } } @@ -2888,9 +2888,9 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro } DEBUG_CODE({ for (unsigned i = 1; m_proof_mode == PGM_FINE && i < num_proofs; i++) { - CTRACE("mk_unit_resolution_bug", !found.get(i, false), + CTRACE("mk_unit_resolution_bug", !found.get(i, false), for (unsigned j = 0; j < num_proofs; j++) { - if (j == i) tout << "Index " << i << " was not found:\n"; + if (j == i) tout << "Index " << i << " was not found:\n"; tout << mk_ll_pp(get_fact(proofs[j]), *this); }); SASSERT(found.get(i, false)); @@ -2915,7 +2915,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro } proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * proofs, expr * new_fact) { - TRACE("unit_bug", + TRACE("unit_bug", for (unsigned i = 0; i < num_proofs; i++) tout << mk_pp(get_fact(proofs[i]), *this) << "\n"; tout << "===>\n"; tout << mk_pp(new_fact, *this) << "\n";); @@ -2956,20 +2956,20 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro SASSERT(num_matches == cls_sz || num_matches == cls_sz - 1); SASSERT(num_matches != cls_sz || is_false(new_fact)); } -#endif +#endif proof * pr = mk_app(m_basic_family_id, PR_UNIT_RESOLUTION, args.size(), args.c_ptr()); TRACE("unit_resolution", tout << "unit_resolution using fact\n" << mk_ll_pp(pr, *this);); return pr; } proof * ast_manager::mk_hypothesis(expr * h) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; return mk_app(m_basic_family_id, PR_HYPOTHESIS, h); } proof * ast_manager::mk_lemma(proof * p, expr * lemma) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(p)); CTRACE("mk_lemma", !is_false(get_fact(p)), tout << mk_ll_pp(p, *this) << "\n";); @@ -2979,11 +2979,11 @@ proof * ast_manager::mk_lemma(proof * p, expr * lemma) { proof * ast_manager::mk_def_intro(expr * new_def) { SASSERT(is_bool(new_def)); - return mk_proof(m_basic_family_id, PR_DEF_INTRO, new_def); + return mk_proof(m_basic_family_id, PR_DEF_INTRO, new_def); } proof * ast_manager::mk_apply_defs(expr * n, expr * def, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); @@ -2992,11 +2992,11 @@ proof * ast_manager::mk_apply_defs(expr * n, expr * def, unsigned num_proofs, pr } proof * ast_manager::mk_iff_oeq(proof * p) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; if (!p) return p; - + SASSERT(has_fact(p)); SASSERT(is_iff(get_fact(p)) || is_oeq(get_fact(p))); if (is_oeq(get_fact(p))) @@ -3019,7 +3019,7 @@ bool ast_manager::check_nnf_proof_parents(unsigned num_proofs, proof * const * p } proof * ast_manager::mk_nnf_pos(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; check_nnf_proof_parents(num_proofs, proofs); ptr_buffer args; @@ -3029,7 +3029,7 @@ proof * ast_manager::mk_nnf_pos(expr * s, expr * t, unsigned num_proofs, proof * } proof * ast_manager::mk_nnf_neg(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; check_nnf_proof_parents(num_proofs, proofs); ptr_buffer args; @@ -3039,7 +3039,7 @@ proof * ast_manager::mk_nnf_neg(expr * s, expr * t, unsigned num_proofs, proof * } proof * ast_manager::mk_nnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); @@ -3048,7 +3048,7 @@ proof * ast_manager::mk_nnf_star(expr * s, expr * t, unsigned num_proofs, proof } proof * ast_manager::mk_skolemization(expr * q, expr * e) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(is_bool(q)); SASSERT(is_bool(e)); @@ -3056,7 +3056,7 @@ proof * ast_manager::mk_skolemization(expr * q, expr * e) { } proof * ast_manager::mk_cnf_star(expr * s, expr * t, unsigned num_proofs, proof * const * proofs) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; ptr_buffer args; args.append(num_proofs, (expr**) proofs); @@ -3065,7 +3065,7 @@ proof * ast_manager::mk_cnf_star(expr * s, expr * t, unsigned num_proofs, proof } proof * ast_manager::mk_and_elim(proof * p, unsigned i) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(p)); SASSERT(is_and(get_fact(p))); @@ -3076,7 +3076,7 @@ proof * ast_manager::mk_and_elim(proof * p, unsigned i) { } proof * ast_manager::mk_not_or_elim(proof * p, unsigned i) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; SASSERT(has_fact(p)); SASSERT(is_not(get_fact(p))); @@ -3094,14 +3094,14 @@ proof * ast_manager::mk_not_or_elim(proof * p, unsigned i) { proof * ast_manager::mk_th_lemma( - family_id tid, + family_id tid, expr * fact, unsigned num_proofs, proof * const * proofs, unsigned num_params, parameter const* params - ) + ) { - if (m_proof_mode == PGM_DISABLED) + if (m_proof_mode == PGM_DISABLED) return m_undef_proof; - + ptr_buffer args; vector parameters; parameters.push_back(parameter(get_family_name(tid))); @@ -3121,7 +3121,7 @@ proof* ast_manager::mk_hyper_resolve(unsigned num_premises, proof* const* premis for (unsigned i = 0; i < num_premises; ++i) { TRACE("hyper_res", tout << mk_pp(premises[i], *this) << "\n";); fmls.push_back(get_fact(premises[i])); - } + } SASSERT(is_bool(concl)); vector params; for (unsigned i = 0; i < substs.size(); ++i) { @@ -3134,7 +3134,7 @@ proof* ast_manager::mk_hyper_resolve(unsigned num_premises, proof* const* premis params.push_back(parameter(positions[i].second)); } } - TRACE("hyper_res", + TRACE("hyper_res", for (unsigned i = 0; i < params.size(); ++i) { params[i].display(tout); tout << "\n"; }); @@ -3153,7 +3153,7 @@ proof* ast_manager::mk_hyper_resolve(unsigned num_premises, proof* const* premis } bool ast_manager::is_hyper_resolve( - proof* p, + proof* p, proof_ref_vector& premises, expr_ref& conclusion, svector > & positions, @@ -3170,7 +3170,7 @@ bool ast_manager::is_hyper_resolve( func_decl* d = p->get_decl(); unsigned num_p = d->get_num_parameters(); parameter const* params = d->get_parameters(); - + substs.push_back(expr_ref_vector(*this)); for (unsigned i = 0; i < num_p; ++i) { if (params[i].is_int()) { @@ -3186,10 +3186,10 @@ bool ast_manager::is_hyper_resolve( SASSERT(params[i].is_ast()); ast* a = params[i].get_ast(); SASSERT(is_expr(a)); - substs.back().push_back(to_expr(a)); + substs.back().push_back(to_expr(a)); } } - + return true; } @@ -3201,7 +3201,7 @@ bool ast_manager::is_hyper_resolve( // ----------------------------------- bool ast_mark::is_marked(ast * n) const { - if (is_decl(n)) + if (is_decl(n)) return m_decl_marks.is_marked(to_decl(n)); else return m_expr_marks.is_marked(to_expr(n)); @@ -3242,7 +3242,7 @@ void scoped_mark::reset() { m_stack.reset(); m_lim.reset(); } - + void scoped_mark::push_scope() { m_lim.push_back(m_stack.size()); } @@ -3251,7 +3251,7 @@ void scoped_mark::pop_scope() { unsigned new_size = m_stack.size(); unsigned old_size = m_lim.back(); for (unsigned i = old_size; i < new_size; ++i) { - ast_mark::mark(m_stack[i].get(), false); + ast_mark::mark(m_stack[i].get(), false); } m_lim.pop_back(); m_stack.resize(old_size); @@ -3262,7 +3262,7 @@ void scoped_mark::pop_scope(unsigned num_scopes) { pop_scope(); } } - + // Added by KLM for use in GDB // show an expr_ref on stdout diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index 51d7fedb0..fbabd469e 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -22,7 +22,7 @@ Revision History: #include"for_each_expr.h" #include"well_sorted.h" -goal::precision goal::mk_union(precision p1, precision p2) { +goal::precision goal::mk_union(precision p1, precision p2) { if (p1 == PRECISE) return p2; if (p2 == PRECISE) return p1; if (p1 != p2) return UNDER_OVER; @@ -40,24 +40,24 @@ std::ostream & operator<<(std::ostream & out, goal::precision p) { } goal::goal(ast_manager & m, bool models_enabled, bool core_enabled): - m_manager(m), + m_manager(m), m_ref_count(0), - m_depth(0), + m_depth(0), m_models_enabled(models_enabled), - m_proofs_enabled(m.proofs_enabled()), - m_core_enabled(core_enabled), - m_inconsistent(false), + m_proofs_enabled(m.proofs_enabled()), + m_core_enabled(core_enabled), + m_inconsistent(false), m_precision(PRECISE) { } - + goal::goal(ast_manager & m, bool proofs_enabled, bool models_enabled, bool core_enabled): - m_manager(m), + m_manager(m), m_ref_count(0), - m_depth(0), + m_depth(0), m_models_enabled(models_enabled), - m_proofs_enabled(proofs_enabled), - m_core_enabled(core_enabled), - m_inconsistent(false), + m_proofs_enabled(proofs_enabled), + m_core_enabled(core_enabled), + m_inconsistent(false), m_precision(PRECISE) { SASSERT(!proofs_enabled || m.proofs_enabled()); } @@ -65,11 +65,11 @@ goal::goal(ast_manager & m, bool proofs_enabled, bool models_enabled, bool core_ goal::goal(goal const & src): m_manager(src.m()), m_ref_count(0), - m_depth(0), + m_depth(0), m_models_enabled(src.models_enabled()), - m_proofs_enabled(src.proofs_enabled()), - m_core_enabled(src.unsat_core_enabled()), - m_inconsistent(false), + m_proofs_enabled(src.proofs_enabled()), + m_core_enabled(src.unsat_core_enabled()), + m_inconsistent(false), m_precision(PRECISE) { copy_from(src); } @@ -79,16 +79,16 @@ goal::goal(goal const & src): goal::goal(goal const & src, bool): m_manager(src.m()), m_ref_count(0), - m_depth(src.m_depth), + m_depth(src.m_depth), m_models_enabled(src.models_enabled()), - m_proofs_enabled(src.proofs_enabled()), - m_core_enabled(src.unsat_core_enabled()), - m_inconsistent(false), + m_proofs_enabled(src.proofs_enabled()), + m_core_enabled(src.unsat_core_enabled()), + m_inconsistent(false), m_precision(src.m_precision) { } - -goal::~goal() { - reset_core(); + +goal::~goal() { + reset_core(); } void goal::copy_to(goal & target) const { @@ -141,7 +141,7 @@ void goal::quick_process(bool save_first, expr * & f, expr_dependency * d) { if (!save_first) { push_back(f, 0, d); } - return; + return; } typedef std::pair expr_pol; sbuffer todo; @@ -173,7 +173,7 @@ void goal::quick_process(bool save_first, expr * & f, expr_dependency * d) { todo.push_back(expr_pol(to_app(curr)->get_arg(0), !pol)); } else { - if (!pol) + if (!pol) curr = m().mk_not(curr); if (save_first) { f = curr; @@ -214,9 +214,9 @@ void goal::process_not_or(bool save_first, app * f, proof * pr, expr_dependency } void goal::slow_process(bool save_first, expr * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr) { - if (m().is_and(f)) + if (m().is_and(f)) process_and(save_first, to_app(f), pr, d, out_f, out_pr); - else if (m().is_not(f) && m().is_or(to_app(f)->get_arg(0))) + else if (m().is_not(f) && m().is_or(to_app(f)->get_arg(0))) process_not_or(save_first, to_app(to_app(f)->get_arg(0)), pr, d, out_f, out_pr); else if (save_first) { out_f = f; @@ -255,7 +255,7 @@ void goal::get_formulas(ptr_vector & result) { } void goal::update(unsigned i, expr * f, proof * pr, expr_dependency * d) { - // KLM: don't know why this assertion is no longer true + // KLM: don't know why this assertion is no longer true // SASSERT(proofs_enabled() == (pr != 0 && !m().is_undef_proof(pr))); if (m_inconsistent) return; @@ -270,7 +270,7 @@ void goal::update(unsigned i, expr * f, proof * pr, expr_dependency * d) { else { m().set(m_forms, i, out_f); m().set(m_proofs, i, out_pr); - if (unsat_core_enabled()) + if (unsat_core_enabled()) m().set(m_dependencies, i, d); } } @@ -283,7 +283,7 @@ void goal::update(unsigned i, expr * f, proof * pr, expr_dependency * d) { } else { m().set(m_forms, i, f); - if (unsat_core_enabled()) + if (unsat_core_enabled()) m().set(m_dependencies, i, d); } } @@ -303,9 +303,9 @@ void goal::reset_all() { m_precision = PRECISE; } -void goal::reset() { - reset_core(); - m_inconsistent = false; +void goal::reset() { + reset_core(); + m_inconsistent = false; } void goal::display(ast_printer & prn, std::ostream & out) const { @@ -573,7 +573,7 @@ void goal::elim_redundancies() { expr_dependency_ref d(m()); if (unsat_core_enabled()) d = m().mk_join(dep(get_idx(atom)), dep(i)); - push_back(m().mk_false(), p, d); + push_back(m().mk_false(), p, d); return; } neg_lits.mark(atom); @@ -627,10 +627,10 @@ goal * goal::translate(ast_translation & translator) const { ast_manager & m_to = translator.to(); goal * res = alloc(goal, m_to, m_to.proofs_enabled() && proofs_enabled(), models_enabled(), unsat_core_enabled()); - + unsigned sz = m().size(m_forms); for (unsigned i = 0; i < sz; i++) { - res->m().push_back(res->m_forms, translator(m().get(m_forms, i))); + res->m().push_back(res->m_forms, translator(m().get(m_forms, i))); if (res->proofs_enabled()) res->m().push_back(res->m_proofs, translator(m().get(m_proofs, i))); if (res->unsat_core_enabled()) @@ -645,15 +645,15 @@ goal * goal::translate(ast_translation & translator) const { } -bool goal::sat_preserved() const { - return prec() == PRECISE || prec() == UNDER; +bool goal::sat_preserved() const { + return prec() == PRECISE || prec() == UNDER; } bool goal::unsat_preserved() const { return prec() == PRECISE || prec() == OVER; } -bool goal::is_decided_sat() const { +bool goal::is_decided_sat() const { return size() == 0 && sat_preserved(); } @@ -661,7 +661,7 @@ bool goal::is_decided_unsat() const { return inconsistent() && unsat_preserved(); } -bool goal::is_decided() const { +bool goal::is_decided() const { return is_decided_sat() || is_decided_unsat(); } From 6db0a15d29702258cb0e56d1b1087a8b30ea5d0c Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 18 Apr 2016 17:17:31 +0100 Subject: [PATCH 29/66] Fixed potential memory leakage issues in fpa2bv_converfter --- src/ast/fpa/fpa2bv_converter.cpp | 376 ++++++++++++++++--------------- 1 file changed, 200 insertions(+), 176 deletions(-) diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index c958fb67f..d41b1809f 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -3,15 +3,15 @@ Copyright (c) 2012 Microsoft Corporation Module Name: - fpa2bv_converter.cpp +fpa2bv_converter.cpp Abstract: - Conversion routines for Floating Point -> Bit-Vector +Conversion routines for Floating Point -> Bit-Vector Author: - Christoph (cwinter) 2012-02-09 +Christoph (cwinter) 2012-02-09 Notes: @@ -50,7 +50,7 @@ void fpa2bv_converter::mk_eq(expr * a, expr * b, expr_ref & result) { SASSERT(is_app_of(b, m_plugin->get_family_id(), OP_FPA_FP)); TRACE("fpa2bv", tout << "mk_eq a=" << mk_ismt2_pp(a, m) << std::endl; - tout << "mk_eq b=" << mk_ismt2_pp(b, m) << std::endl;); + tout << "mk_eq b=" << mk_ismt2_pp(b, m) << std::endl;); expr_ref eq_sgn(m), eq_exp(m), eq_sig(m); m_simp.mk_eq(to_app(a)->get_arg(0), to_app(b)->get_arg(0), eq_sgn); @@ -81,7 +81,7 @@ void fpa2bv_converter::mk_eq(expr * a, expr * b, expr_ref & result) { SASSERT(is_app_of(b, m_plugin->get_family_id(), OP_FPA_INTERNAL_RM)); TRACE("fpa2bv", tout << "mk_eq_rm a=" << mk_ismt2_pp(a, m) << std::endl; - tout << "mk_eq_rm b=" << mk_ismt2_pp(b, m) << std::endl;); + tout << "mk_eq_rm b=" << mk_ismt2_pp(b, m) << std::endl;); m_simp.mk_eq(to_app(a)->get_arg(0), to_app(b)->get_arg(0), result); } @@ -146,7 +146,7 @@ void fpa2bv_converter::mk_numeral(func_decl * f, unsigned num, expr * const * ar } else { expr_ref bv_sgn(m), bv_sig(m), e(m), biased_exp(m); - bv_sgn = m_bv_util.mk_numeral( (sign) ? 1 : 0, 1); + bv_sgn = m_bv_util.mk_numeral((sign) ? 1 : 0, 1); bv_sig = m_bv_util.mk_numeral(rational(sig), sbits-1); e = m_bv_util.mk_numeral(exp, ebits); @@ -154,7 +154,7 @@ void fpa2bv_converter::mk_numeral(func_decl * f, unsigned num, expr * const * ar mk_fp(bv_sgn, biased_exp, bv_sig, result); TRACE("fpa2bv_dbg", tout << "value of [" << sign << " " << m_mpz_manager.to_string(sig) << " " << exp << "] is " - << mk_ismt2_pp(result, m) << std::endl;); + << mk_ismt2_pp(result, m) << std::endl;); } } @@ -183,8 +183,8 @@ void fpa2bv_converter::mk_const(func_decl * f, expr_ref & result) { std::string name = f->get_name().str(); sgn = mk_fresh_const((p + "_sgn_" + name).c_str(), 1); - s = mk_fresh_const((p + "_sig_" + name).c_str(), sbits - 1); e = mk_fresh_const((p + "_exp_" + name).c_str(), ebits); + s = mk_fresh_const((p + "_sig_" + name).c_str(), sbits-1); #else app_ref bv(m); unsigned bv_sz = 1 + ebits + (sbits - 1); @@ -215,8 +215,8 @@ void fpa2bv_converter::mk_var(unsigned base_inx, sort * srt, expr_ref & result) expr_ref sgn(m), s(m), e(m); sgn = m.mk_var(base_inx, m_bv_util.mk_sort(1)); - s = m.mk_var(base_inx + 1, m_bv_util.mk_sort(sbits-1)); - e = m.mk_var(base_inx + 2, m_bv_util.mk_sort(ebits)); + s = m.mk_var(base_inx+1, m_bv_util.mk_sort(sbits-1)); + e = m.mk_var(base_inx+2, m_bv_util.mk_sort(ebits)); mk_fp(sgn, e, s, result); } @@ -230,9 +230,9 @@ void fpa2bv_converter::mk_uninterpreted_output(sort * rng, func_decl * fbv, expr app_ref na(m); na = m.mk_app(fbv, new_args.size(), new_args.c_ptr()); mk_fp(m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, na), - m_bv_util.mk_extract(bv_sz - 2, sbits - 1, na), - m_bv_util.mk_extract(sbits - 2, 0, na), - result); + m_bv_util.mk_extract(bv_sz - 2, sbits - 1, na), + m_bv_util.mk_extract(sbits - 2, 0, na), + result); } else if (m_util.is_rm(rng)) { app_ref na(m); @@ -316,11 +316,11 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { expr_ref bv3(m); bv3 = m.mk_fresh_const( - #ifdef Z3DEBUG +#ifdef Z3DEBUG "fpa2bv_rm" - #else +#else 0 - #endif +#endif , m_bv_util.mk_sort(3)); mk_rm(bv3, result); @@ -342,9 +342,9 @@ void fpa2bv_converter::mk_pinf(func_decl * f, expr_ref & result) { expr_ref top_exp(m); mk_top_exp(ebits, top_exp); mk_fp(m_bv_util.mk_numeral(0, 1), - top_exp, - m_bv_util.mk_numeral(0, sbits-1), - result); + top_exp, + m_bv_util.mk_numeral(0, sbits-1), + result); } void fpa2bv_converter::mk_ninf(func_decl * f, expr_ref & result) { @@ -355,9 +355,9 @@ void fpa2bv_converter::mk_ninf(func_decl * f, expr_ref & result) { expr_ref top_exp(m); mk_top_exp(ebits, top_exp); mk_fp(m_bv_util.mk_numeral(1, 1), - top_exp, - m_bv_util.mk_numeral(0, sbits-1), - result); + top_exp, + m_bv_util.mk_numeral(0, sbits-1), + result); } void fpa2bv_converter::mk_nan(func_decl * f, expr_ref & result) { @@ -368,9 +368,9 @@ void fpa2bv_converter::mk_nan(func_decl * f, expr_ref & result) { expr_ref top_exp(m); mk_top_exp(ebits, top_exp); mk_fp(m_bv_util.mk_numeral(0, 1), - top_exp, - m_bv_util.mk_numeral(1, sbits-1), - result); + top_exp, + m_bv_util.mk_numeral(1, sbits-1), + result); } void fpa2bv_converter::mk_nzero(func_decl *f, expr_ref & result) { @@ -381,9 +381,9 @@ void fpa2bv_converter::mk_nzero(func_decl *f, expr_ref & result) { expr_ref bot_exp(m); mk_bot_exp(ebits, bot_exp); mk_fp(m_bv_util.mk_numeral(1, 1), - bot_exp, - m_bv_util.mk_numeral(0, sbits - 1), - result); + bot_exp, + m_bv_util.mk_numeral(0, sbits - 1), + result); } void fpa2bv_converter::mk_pzero(func_decl *f, expr_ref & result) { @@ -394,9 +394,9 @@ void fpa2bv_converter::mk_pzero(func_decl *f, expr_ref & result) { expr_ref bot_exp(m); mk_bot_exp(ebits, bot_exp); mk_fp(m_bv_util.mk_numeral(0, 1), - bot_exp, - m_bv_util.mk_numeral(0, sbits-1), - result); + bot_exp, + m_bv_util.mk_numeral(0, sbits-1), + result); } void fpa2bv_converter::mk_one(func_decl *f, expr_ref sign, expr_ref & result) { @@ -405,9 +405,9 @@ void fpa2bv_converter::mk_one(func_decl *f, expr_ref sign, expr_ref & result) { unsigned sbits = m_util.get_sbits(srt); unsigned ebits = m_util.get_ebits(srt); mk_fp(sign, - m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits), - m_bv_util.mk_numeral(0, sbits-1), - result); + m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits), + m_bv_util.mk_numeral(0, sbits-1), + result); } void fpa2bv_converter::add_core(unsigned sbits, unsigned ebits, @@ -623,8 +623,8 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, expr_ref res_sgn(m), res_sig(m), res_exp(m); add_core(sbits, ebits, - c_sgn, c_sig, c_exp, d_sgn, d_sig, d_exp, - res_sgn, res_sig, res_exp); + c_sgn, c_sig, c_exp, d_sgn, d_sig, d_exp, + res_sgn, res_sig, res_exp); expr_ref is_zero_sig(m), nil_sbit4(m); nil_sbit4 = m_bv_util.mk_numeral(0, sbits+4); @@ -781,8 +781,8 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, dbg_decouple("fpa2bv_mul_res_sgn", res_sgn); res_exp = m_bv_util.mk_bv_add( - m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), - m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); + m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), + m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); expr_ref product(m); product = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); @@ -925,8 +925,8 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); res_exp = m_bv_util.mk_bv_sub( - m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), - m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); + m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), + m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); expr_ref quotient(m); // b_sig_ext can't be 0 here, so it's safe to use OP_BUDIV_I @@ -981,7 +981,7 @@ void fpa2bv_converter::mk_rem(func_decl * f, unsigned num, expr * const * args, y = args[1]; TRACE("fpa2bv_rem", tout << "X = " << mk_ismt2_pp(x, m) << std::endl; - tout << "Y = " << mk_ismt2_pp(y, m) << std::endl;); + tout << "Y = " << mk_ismt2_pp(y, m) << std::endl;); expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); mk_nan(f, nan); @@ -1089,7 +1089,7 @@ void fpa2bv_converter::mk_rem(func_decl * f, unsigned num, expr * const * args, expr_ref res_sgn(m), res_sig(m), res_exp(m); res_sgn = a_sgn; res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits, 0, huge_rem), - m_bv_util.mk_numeral(0, 3)); + m_bv_util.mk_numeral(0, 3)); res_exp = m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext); @@ -1136,7 +1136,9 @@ void fpa2bv_converter::mk_min(func_decl * f, unsigned num, expr * const * args, v = m.mk_app(m_util.get_family_id(), OP_FPA_INTERNAL_MIN_UNSPECIFIED, x, y); // Note: This requires BR_REWRITE_FULL afterwards. - result = m.mk_ite(c, v, m.mk_app(m_util.get_family_id(), OP_FPA_INTERNAL_MIN_I, x, y)); + expr_ref min_i(m); + min_i = m.mk_app(m_util.get_family_id(), OP_FPA_INTERNAL_MIN_I, x, y); + m_simp.mk_ite(c, v, min_i, result); } void fpa2bv_converter::mk_min_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { @@ -1163,8 +1165,7 @@ void fpa2bv_converter::mk_min_i(func_decl * f, unsigned num, expr * const * args expr_ref lt(m); mk_float_lt(f, num, args, lt); - result = y; - mk_ite(lt, x, result, result); + mk_ite(lt, x, y, result); mk_ite(both_zero, y, result, result); mk_ite(y_is_nan, x, result, result); mk_ite(x_is_nan, y, result, result); @@ -1192,13 +1193,13 @@ expr_ref fpa2bv_converter::mk_min_unspecified(func_decl * f, expr * x, expr * y) expr_ref pn(m), np(m); mk_fp(decls.first, - m_bv_util.mk_numeral(0, ebits), - m_bv_util.mk_numeral(0, sbits - 1), - pn); + m_bv_util.mk_numeral(0, ebits), + m_bv_util.mk_numeral(0, sbits - 1), + pn); mk_fp(decls.second, - m_bv_util.mk_numeral(0, ebits), - m_bv_util.mk_numeral(0, sbits - 1), - np); + m_bv_util.mk_numeral(0, ebits), + m_bv_util.mk_numeral(0, sbits - 1), + np); expr_ref x_is_pzero(m), x_is_nzero(m), xyzero(m); mk_is_pzero(x, x_is_pzero); @@ -1221,7 +1222,9 @@ void fpa2bv_converter::mk_max(func_decl * f, unsigned num, expr * const * args, v = m.mk_app(m_util.get_family_id(), OP_FPA_INTERNAL_MAX_UNSPECIFIED, x, y); // Note: This requires BR_REWRITE_FULL afterwards. - result = m.mk_ite(c, v, m.mk_app(m_util.get_family_id(), OP_FPA_INTERNAL_MAX_I, x, y)); + expr_ref max_i(m); + max_i = m.mk_app(m_util.get_family_id(), OP_FPA_INTERNAL_MAX_I, x, y); + m_simp.mk_ite(c, v, max_i, result); } void fpa2bv_converter::mk_max_i(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { @@ -1242,14 +1245,14 @@ void fpa2bv_converter::mk_max_i(func_decl * f, unsigned num, expr * const * args mk_is_nan(y, y_is_nan); mk_pzero(f, pzero); - expr_ref sgn_diff(m); - sgn_diff = m.mk_not(m.mk_eq(x_sgn, y_sgn)); + expr_ref sgn_diff(m), sgn_eq(m); + sgn_eq = m.mk_eq(x_sgn, y_sgn); + sgn_diff = m.mk_not(sgn_eq); expr_ref gt(m); mk_float_gt(f, num, args, gt); - result = y; - mk_ite(gt, x, result, result); + mk_ite(gt, x, y, result); mk_ite(both_zero, y, result, result); mk_ite(y_is_nan, x, result, result); mk_ite(x_is_nan, y, result, result); @@ -1277,13 +1280,13 @@ expr_ref fpa2bv_converter::mk_max_unspecified(func_decl * f, expr * x, expr * y) expr_ref pn(m), np(m); mk_fp(decls.first, - m_bv_util.mk_numeral(0, ebits), - m_bv_util.mk_numeral(0, sbits - 1), - pn); + m_bv_util.mk_numeral(0, ebits), + m_bv_util.mk_numeral(0, sbits - 1), + pn); mk_fp(decls.second, - m_bv_util.mk_numeral(0, ebits), - m_bv_util.mk_numeral(0, sbits - 1), - np); + m_bv_util.mk_numeral(0, ebits), + m_bv_util.mk_numeral(0, sbits - 1), + np); expr_ref x_is_pzero(m), x_is_nzero(m), xyzero(m); mk_is_pzero(x, x_is_pzero); @@ -1440,7 +1443,7 @@ void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, dbg_decouple("fpa2bv_fma_mul_sgn", mul_sgn); mul_exp = m_bv_util.mk_bv_add(m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), - m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); + m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); dbg_decouple("fpa2bv_fma_mul_exp", mul_exp); mul_sig = m_bv_util.mk_bv_mul(a_sig_ext, b_sig_ext); @@ -1501,8 +1504,8 @@ void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, // Alignment shift with sticky bit computation. expr_ref shifted_big(m), shifted_f_sig(m), sticky_raw(m); shifted_big = m_bv_util.mk_bv_lshr( - m_bv_util.mk_concat(f_sig, m_bv_util.mk_numeral(0, sbits)), - m_bv_util.mk_zero_extend((3*sbits)-(ebits+2), exp_delta)); + m_bv_util.mk_concat(f_sig, m_bv_util.mk_numeral(0, sbits)), + m_bv_util.mk_zero_extend((3*sbits)-(ebits+2), exp_delta)); shifted_f_sig = m_bv_util.mk_extract(3*sbits-1, sbits, shifted_big); sticky_raw = m_bv_util.mk_extract(sbits-1, 0, shifted_big); SASSERT(m_bv_util.get_bv_size(shifted_f_sig) == 2 * sbits); @@ -1535,7 +1538,7 @@ void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, expr_ref sum(m), e_plus_f(m), e_minus_f(m); e_plus_f = m_bv_util.mk_bv_add(e_sig, shifted_f_sig); e_minus_f = m_bv_util.mk_bv_sub(e_sig, shifted_f_sig), - m_simp.mk_ite(eq_sgn, e_plus_f, e_minus_f, sum); + m_simp.mk_ite(eq_sgn, e_plus_f, e_minus_f, sum); SASSERT(is_well_sorted(m, sum)); @@ -1721,7 +1724,7 @@ void fpa2bv_converter::mk_sqrt(func_decl * f, unsigned num, expr * const * args, expr * or_args[2] = { Q, S }; expr_ref Q_or_S(m), R_shftd(m), T_lsds4(m); - Q_or_S = m_bv_util.mk_bv_or(2, or_args); + Q_or_S = m_bv_util.mk_bv_or(2, or_args); m_simp.mk_ite(t_lt_0, Q, Q_or_S, Q); R_shftd = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits + 3, 0, R), zero1); T_lsds4 = m_bv_util.mk_extract(sbits + 4, 0, T); @@ -1818,8 +1821,9 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * dbg_decouple("fpa2bv_r2i_unpacked_sig", a_sig); dbg_decouple("fpa2bv_r2i_unpacked_exp", a_exp); - expr_ref xzero(m); - mk_ite(m.mk_eq(a_sgn, one_1), nzero, pzero, xzero); + expr_ref xzero(m), sgn_eq_1(m); + sgn_eq_1 = m.mk_eq(a_sgn, one_1); + mk_ite(sgn_eq_1, nzero, pzero, xzero); // exponent < 0 -> 0/1 expr_ref exp_lt_zero(m), exp_h(m); @@ -1831,7 +1835,7 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * expr_ref pone(m), none(m), xone(m), c421(m), c422(m), c423(m), t1(m), t2(m), tie(m), v42(m), exp_lt_m1(m); mk_one(f, zero_1, pone); mk_one(f, one_1, none); - mk_ite(m.mk_eq(a_sgn, one_1), none, pone, xone); + mk_ite(sgn_eq_1, none, pone, xone); expr_ref pow_2_sbitsm1(m), m1(m); pow_2_sbitsm1 = m_bv_util.mk_numeral(fu().fm().m_powers2(sbits - 1), sbits); @@ -1880,7 +1884,7 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * expr_ref shift(m), rshift(m), div(m), rem(m); shift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits - 1, sbits + 1), - m_bv_util.mk_sign_extend(sbits - ebits + 1, a_exp)); + m_bv_util.mk_sign_extend(sbits - ebits + 1, a_exp)); rshift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits, sbits + 1), shift); div = m_bv_util.mk_bv_lshr(m_bv_util.mk_zero_extend(1, a_sig), shift); rem = m_bv_util.mk_bv_lshr(m_bv_util.mk_bv_shl(m_bv_util.mk_zero_extend(1, a_sig), rshift), rshift); @@ -1901,15 +1905,15 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * expr_ref tie2(m), tie2_c(m), div_last(m), v51(m), rem_shl(m); rem_shl = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits - 1, 0, rem), zero_1); m_simp.mk_eq(rem_shl, - m_bv_util.mk_bv_shl(m_bv_util.mk_numeral(1, sbits+1), shift), - tie2); + m_bv_util.mk_bv_shl(m_bv_util.mk_numeral(1, sbits+1), shift), + tie2); div_last = m_bv_util.mk_extract(0, 0, div); tie2_c = m.mk_or(m.mk_and(tie2, - m.mk_or(m.mk_and(rm_is_rte, m.mk_eq(div_last, one_1)), - m.mk_and(rm_is_rta, m.mk_eq(div_last, zero_1)))), - m.mk_xor(m.mk_eq(a_sgn, one_1), - m_bv_util.mk_sle(m_bv_util.mk_bv_shl(m_bv_util.mk_numeral(1, sbits + 1), shift), - rem_shl))); + m.mk_or(m.mk_and(rm_is_rte, m.mk_eq(div_last, one_1)), + m.mk_and(rm_is_rta, m.mk_eq(div_last, zero_1)))), + m.mk_xor(m.mk_eq(a_sgn, one_1), + m_bv_util.mk_sle(m_bv_util.mk_bv_shl(m_bv_util.mk_numeral(1, sbits + 1), shift), + rem_shl))); m_simp.mk_ite(tie2_c, div_p1, div, v51); dbg_decouple("fpa2bv_r2i_v51", v51); @@ -1948,7 +1952,7 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * expr_ref e_shift(m); e_shift = (ebits + 2 <= sbits + 1) ? m_bv_util.mk_extract(ebits + 1, 0, shift) : - m_bv_util.mk_sign_extend((ebits + 2) - (sbits + 1), shift); + m_bv_util.mk_sign_extend((ebits + 2) - (sbits + 1), shift); SASSERT(m_bv_util.get_bv_size(e_shift) == ebits + 2); res_exp = m_bv_util.mk_bv_add(m_bv_util.mk_zero_extend(2, res_exp), e_shift); @@ -1977,7 +1981,7 @@ void fpa2bv_converter::mk_float_eq(func_decl * f, unsigned num, expr * const * a expr * x = args[0], * y = args[1]; TRACE("fpa2bv_float_eq", tout << "X = " << mk_ismt2_pp(x, m) << std::endl; - tout << "Y = " << mk_ismt2_pp(y, m) << std::endl;); + tout << "Y = " << mk_ismt2_pp(y, m) << std::endl;); expr_ref c1(m), c2(m), x_is_nan(m), y_is_nan(m), x_is_zero(m), y_is_zero(m); mk_is_nan(x, x_is_nan); @@ -2004,7 +2008,6 @@ void fpa2bv_converter::mk_float_eq(func_decl * f, unsigned num, expr * const * a expr_ref c3t4(m), c2else(m); m_simp.mk_ite(c3, m.mk_false(), t4, c3t4); m_simp.mk_ite(c2, m.mk_true(), c3t4, c2else); - m_simp.mk_ite(c1, m.mk_false(), c2else, result); TRACE("fpa2bv_float_eq", tout << "FLOAT_EQ = " << mk_ismt2_pp(result, m) << std::endl; ); @@ -2154,7 +2157,7 @@ void fpa2bv_converter::mk_is_positive(func_decl * f, unsigned num, expr * const void fpa2bv_converter::mk_to_fp(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_fp", for (unsigned i=0; i < num; i++) - tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl; ); + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl; ); if (num == 1 && m_bv_util.is_bv(args[0])) { @@ -2167,33 +2170,33 @@ void fpa2bv_converter::mk_to_fp(func_decl * f, unsigned num, expr * const * args SASSERT((unsigned)sz == to_sbits + to_ebits); mk_fp(m_bv_util.mk_extract(sz - 1, sz - 1, bv), - m_bv_util.mk_extract(sz - 2, sz - to_ebits - 1, bv), - m_bv_util.mk_extract(sz - to_ebits - 2, 0, bv), - result); + m_bv_util.mk_extract(sz - 2, sz - to_ebits - 1, bv), + m_bv_util.mk_extract(sz - to_ebits - 2, 0, bv), + result); } else if (num == 2 && - m_util.is_rm(args[0]) && - m_util.is_float(m.get_sort(args[1]))) { + m_util.is_rm(args[0]) && + m_util.is_float(m.get_sort(args[1]))) { // rm + float -> float mk_to_fp_float(f, f->get_range(), args[0], args[1], result); } else if (num == 2 && - m_util.is_rm(args[0]) && - (m_arith_util.is_int(args[1]) || - m_arith_util.is_real(args[1]))) { + m_util.is_rm(args[0]) && + (m_arith_util.is_int(args[1]) || + m_arith_util.is_real(args[1]))) { // rm + real -> float mk_to_fp_real(f, f->get_range(), args[0], args[1], result); } else if (num == 2 && - m_util.is_rm(args[0]) && - m_bv_util.is_bv(args[1])) { + m_util.is_rm(args[0]) && + m_bv_util.is_bv(args[1])) { // rm + signed bv -> float mk_to_fp_signed(f, num, args, result); } else if (num == 3 && - m_bv_util.is_bv(args[0]) && - m_bv_util.is_bv(args[1]) && - m_bv_util.is_bv(args[2])) { + m_bv_util.is_bv(args[0]) && + m_bv_util.is_bv(args[1]) && + m_bv_util.is_bv(args[2])) { // 3 BV -> float SASSERT(m_bv_util.get_bv_size(args[0]) == 1); SASSERT(m_util.get_ebits(f->get_range()) == m_bv_util.get_bv_size(args[1])); @@ -2201,9 +2204,9 @@ void fpa2bv_converter::mk_to_fp(func_decl * f, unsigned num, expr * const * args mk_fp(args[0], args[1], args[2], result); } else if (num == 3 && - m_util.is_rm(args[0]) && - m_arith_util.is_numeral(args[1]) && - m_arith_util.is_numeral(args[2])) + m_util.is_rm(args[0]) && + m_arith_util.is_numeral(args[1]) && + m_arith_util.is_numeral(args[2])) { // rm + real + int -> float mk_to_fp_real_int(f, num, args, result); @@ -2309,15 +2312,15 @@ void fpa2bv_converter::mk_to_fp_float(func_decl * f, sort * s, expr * rm, expr * res_exp = m_bv_util.mk_bv_sub(res_exp, lz_ext); } else if (from_ebits > (to_ebits + 2)) { - unsigned ebits_diff = from_ebits - (to_ebits + 2); + unsigned ebits_diff = from_ebits - (to_ebits + 2); // subtract lz for subnormal numbers. expr_ref exp_sub_lz(m); exp_sub_lz = m_bv_util.mk_bv_sub(m_bv_util.mk_sign_extend(2, exp), m_bv_util.mk_sign_extend(2, lz)); dbg_decouple("fpa2bv_to_float_exp_sub_lz", exp_sub_lz); - // check whether exponent is within roundable (to_ebits+2) range. - expr_ref max_exp(m), min_exp(m), exp_in_range(m); + // check whether exponent is within roundable (to_ebits+2) range. + expr_ref max_exp(m), min_exp(m), exp_in_range(m); const mpz & z = m_mpf_manager.m_powers2(to_ebits + 1, true); max_exp = m_bv_util.mk_concat( m_bv_util.mk_numeral(m_mpf_manager.m_powers2.m1(to_ebits, false), to_ebits + 1), @@ -2330,23 +2333,23 @@ void fpa2bv_converter::mk_to_fp_float(func_decl * f, sort * s, expr * rm, expr * const mpz & ovft = m_mpf_manager.m_powers2.m1(to_ebits+1, false); first_ovf_exp = m_bv_util.mk_numeral(ovft, from_ebits+2); first_udf_exp = m_bv_util.mk_concat( - m_bv_util.mk_numeral(-1, ebits_diff + 3), - m_bv_util.mk_numeral(1, to_ebits + 1)); + m_bv_util.mk_numeral(-1, ebits_diff + 3), + m_bv_util.mk_numeral(1, to_ebits + 1)); dbg_decouple("fpa2bv_to_float_first_ovf_exp", first_ovf_exp); dbg_decouple("fpa2bv_to_float_first_udf_exp", first_udf_exp); - exp_in_range = m_bv_util.mk_extract(to_ebits + 1, 0, exp_sub_lz); - SASSERT(m_bv_util.get_bv_size(exp_in_range) == to_ebits + 2); + exp_in_range = m_bv_util.mk_extract(to_ebits + 1, 0, exp_sub_lz); + SASSERT(m_bv_util.get_bv_size(exp_in_range) == to_ebits + 2); - expr_ref ovf_cond(m), udf_cond(m); - ovf_cond = m_bv_util.mk_sle(first_ovf_exp, exp_sub_lz); - udf_cond = m_bv_util.mk_sle(exp_sub_lz, first_udf_exp); + expr_ref ovf_cond(m), udf_cond(m); + ovf_cond = m_bv_util.mk_sle(first_ovf_exp, exp_sub_lz); + udf_cond = m_bv_util.mk_sle(exp_sub_lz, first_udf_exp); dbg_decouple("fpa2bv_to_float_exp_ovf", ovf_cond); dbg_decouple("fpa2bv_to_float_exp_udf", udf_cond); - res_exp = exp_in_range; - res_exp = m.mk_ite(ovf_cond, max_exp, res_exp); - res_exp = m.mk_ite(udf_cond, min_exp, res_exp); + res_exp = exp_in_range; + res_exp = m.mk_ite(ovf_cond, max_exp, res_exp); + res_exp = m.mk_ite(udf_cond, min_exp, res_exp); } else { // from_ebits == (to_ebits + 2) res_exp = m_bv_util.mk_bv_sub(exp, lz); @@ -2381,7 +2384,7 @@ void fpa2bv_converter::mk_to_fp_float(func_decl * f, sort * s, expr * rm, expr * void fpa2bv_converter::mk_to_fp_real(func_decl * f, sort * s, expr * rm, expr * x, expr_ref & result) { TRACE("fpa2bv_to_fp_real", tout << "rm: " << mk_ismt2_pp(rm, m) << std::endl << - "x: " << mk_ismt2_pp(x, m) << std::endl;); + "x: " << mk_ismt2_pp(x, m) << std::endl;); SASSERT(m_util.is_float(s)); SASSERT(au().is_real(x) || au().is_int(x)); SASSERT(is_app_of(rm, m_util.get_family_id(), OP_FPA_INTERNAL_RM)); @@ -2572,7 +2575,7 @@ void fpa2bv_converter::mk_to_fp_real_int(func_decl * f, unsigned num, expr * con void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_real", for (unsigned i = 0; i < num; i++) - tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); SASSERT(num == 1); SASSERT(f->get_num_parameters() == 0); SASSERT(is_app_of(args[0], m_plugin->get_family_id(), OP_FPA_FP)); @@ -2596,7 +2599,7 @@ void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * ar SASSERT(m_bv_util.get_bv_size(sig) == sbits); SASSERT(m_bv_util.get_bv_size(exp) == ebits); - expr_ref rsig(m), bit(m), zero(m), one(m), two(m), bv0(m), bv1(m); + expr_ref rsig(m), bit(m), bit_eq_1(m), rsig_mul_2(m), zero(m), one(m), two(m), bv0(m), bv1(m); zero = m_arith_util.mk_numeral(rational(0), rs); one = m_arith_util.mk_numeral(rational(1), rs); two = m_arith_util.mk_numeral(rational(2), rs); @@ -2605,8 +2608,10 @@ void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * ar rsig = one; for (unsigned i = sbits - 2; i != (unsigned)-1; i--) { bit = m_bv_util.mk_extract(i, i, sig); - rsig = m_arith_util.mk_add(m_arith_util.mk_mul(rsig, two), - m.mk_ite(m.mk_eq(bit, bv1), one, zero)); + bit_eq_1 = m.mk_eq(bit, bv1); + rsig_mul_2 = m_arith_util.mk_mul(rsig, two); + rsig = m_arith_util.mk_add(rsig_mul_2, + m.mk_ite(bit_eq_1, one, zero)); } const mpz & p2 = fu().fm().m_powers2(sbits - 1); @@ -2625,37 +2630,44 @@ void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * ar dbg_decouple("fpa2bv_to_real_exp_abs", exp); SASSERT(m_bv_util.get_bv_size(exp_abs) == ebits + 1); - expr_ref exp2(m), prev_bit(m); + expr_ref exp2(m), exp2_mul_2(m), prev_bit(m); exp2 = zero; for (unsigned i = ebits; i != (unsigned)-1; i--) { bit = m_bv_util.mk_extract(i, i, exp_abs); - exp2 = m_arith_util.mk_add(m_arith_util.mk_mul(exp2, two), - m.mk_ite(m.mk_eq(bit, bv1), one, zero)); + bit_eq_1 = m.mk_eq(bit, bv1); + exp2_mul_2 = m_arith_util.mk_mul(exp2, two); + exp2 = m_arith_util.mk_add(exp2_mul_2, + m.mk_ite(bit_eq_1, one, zero)); prev_bit = bit; } - exp2 = m.mk_ite(exp_is_neg, m_arith_util.mk_div(one, exp2), exp2); + expr_ref one_div_exp2(m); + one_div_exp2 = m_arith_util.mk_div(one, exp2); + exp2 = m.mk_ite(exp_is_neg, one_div_exp2, exp2); dbg_decouple("fpa2bv_to_real_exp2", exp2); - expr_ref res(m), two_exp2(m); + expr_ref res(m), two_exp2(m), minus_res(m); two_exp2 = m_arith_util.mk_power(two, exp2); res = m_arith_util.mk_mul(rsig, two_exp2); - res = m.mk_ite(m.mk_eq(sgn, bv1), m_arith_util.mk_uminus(res), res); + minus_res = m_arith_util.mk_uminus(res); + res = m.mk_ite(m.mk_eq(sgn, bv1), minus_res, res); dbg_decouple("fpa2bv_to_real_sig_times_exp2", res); TRACE("fpa2bv_to_real", tout << "rsig = " << mk_ismt2_pp(rsig, m) << std::endl; tout << "exp2 = " << mk_ismt2_pp(exp2, m) << std::endl;); + expr_ref unspec(m); + unspec = mk_to_real_unspecified(ebits, sbits); result = m.mk_ite(x_is_zero, zero, res); - result = m.mk_ite(x_is_inf, mk_to_real_unspecified(ebits, sbits), result); - result = m.mk_ite(x_is_nan, mk_to_real_unspecified(ebits, sbits), result); + result = m.mk_ite(x_is_inf, unspec, result); + result = m.mk_ite(x_is_nan, unspec, result); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_fp_signed", for (unsigned i = 0; i < num; i++) - tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); // This is a conversion from signed bitvector to float: // ; from signed machine integer, represented as a 2's complement bit vector @@ -2702,10 +2714,11 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const // Special case: x != 0 expr_ref is_neg_bit(m), exp_too_large(m), sig_4(m), exp_2(m); - expr_ref is_neg(m), x_abs(m); + expr_ref is_neg(m), x_abs(m), neg_x(m); is_neg_bit = m_bv_util.mk_extract(bv_sz - 1, bv_sz - 1, x); is_neg = m.mk_eq(is_neg_bit, bv1_1); - x_abs = m.mk_ite(is_neg, m_bv_util.mk_bv_neg(x), x); + neg_x = m_bv_util.mk_bv_neg(x); + x_abs = m.mk_ite(is_neg, neg_x, x); dbg_decouple("fpa2bv_to_fp_signed_is_neg", is_neg); // x_abs has an extra bit in the front. // x_abs is [bv_sz-1, bv_sz-2] . [bv_sz-3 ... 0] * 2^(bv_sz-2) @@ -2736,7 +2749,7 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const extra_zeros = m_bv_util.mk_numeral(0, extra_bits); sig_4 = m_bv_util.mk_concat(shifted_sig, extra_zeros); lz = m_bv_util.mk_bv_add(m_bv_util.mk_concat(extra_zeros, lz), - m_bv_util.mk_numeral(extra_bits, sig_sz)); + m_bv_util.mk_numeral(extra_bits, sig_sz)); bv_sz = bv_sz + extra_bits; SASSERT(is_well_sorted(m, lz)); } @@ -2762,15 +2775,16 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const // exp_sz < exp_worst_case_sz and exp >= 0. // Take the maximum legal exponent; this // allows us to keep the most precision. - expr_ref max_exp(m), max_exp_bvsz(m); + expr_ref max_exp(m), max_exp_bvsz(m), zero_sig_sz(m); mk_max_exp(exp_sz, max_exp); max_exp_bvsz = m_bv_util.mk_zero_extend(bv_sz - exp_sz, max_exp); exp_too_large = m_bv_util.mk_ule(m_bv_util.mk_bv_add( - max_exp_bvsz, - m_bv_util.mk_numeral(1, bv_sz)), - s_exp); - sig_4 = m.mk_ite(exp_too_large, m_bv_util.mk_numeral(0, sig_sz), sig_4); + max_exp_bvsz, + m_bv_util.mk_numeral(1, bv_sz)), + s_exp); + zero_sig_sz = m_bv_util.mk_numeral(0, sig_sz); + sig_4 = m.mk_ite(exp_too_large, zero_sig_sz, sig_4); exp_2 = m.mk_ite(exp_too_large, max_exp, exp_2); } dbg_decouple("fpa2bv_to_fp_signed_exp_too_large", exp_too_large); @@ -2795,7 +2809,7 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_fp_unsigned", for (unsigned i = 0; i < num; i++) - tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); // This is a conversion from unsigned bitvector to float: // ((_ to_fp_unsigned eb sb) RoundingMode (_ BitVec m) (_ FloatingPoint eb sb)) @@ -2870,7 +2884,7 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con extra_zeros = m_bv_util.mk_numeral(0, extra_bits); sig_4 = m_bv_util.mk_concat(shifted_sig, extra_zeros); lz = m_bv_util.mk_bv_add(m_bv_util.mk_concat(extra_zeros, lz), - m_bv_util.mk_numeral(extra_bits, sig_sz)); + m_bv_util.mk_numeral(extra_bits, sig_sz)); bv_sz = bv_sz + extra_bits; SASSERT(is_well_sorted(m, lz)); } @@ -2886,15 +2900,15 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con // the remaining bits are 0 if ebits is large enough. exp_too_large = m.mk_false(); // This is always in range. - // The exponent is at most bv_sz, i.e., we need ld(bv_sz)+1 ebits. - // exp < bv_sz (+sign bit which is [0]) + // The exponent is at most bv_sz, i.e., we need ld(bv_sz)+1 ebits. + // exp < bv_sz (+sign bit which is [0]) unsigned exp_worst_case_sz = (unsigned)((log((double)bv_sz) / log((double)2)) + 1.0); if (exp_sz < exp_worst_case_sz) { // exp_sz < exp_worst_case_sz and exp >= 0. // Take the maximum legal exponent; this // allows us to keep the most precision. - expr_ref max_exp(m), max_exp_bvsz(m); + expr_ref max_exp(m), max_exp_bvsz(m), zero_sig_sz(m); mk_max_exp(exp_sz, max_exp); max_exp_bvsz = m_bv_util.mk_zero_extend(bv_sz - exp_sz, max_exp); @@ -2902,7 +2916,8 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con max_exp_bvsz, m_bv_util.mk_numeral(1, bv_sz)), s_exp); - sig_4 = m.mk_ite(exp_too_large, m_bv_util.mk_numeral(0, sig_sz), sig_4); + zero_sig_sz = m_bv_util.mk_numeral(0, sig_sz); + sig_4 = m.mk_ite(exp_too_large, zero_sig_sz, sig_4); exp_2 = m.mk_ite(exp_too_large, max_exp, exp_2); } dbg_decouple("fpa2bv_to_fp_unsigned_exp_too_large", exp_too_large); @@ -2941,20 +2956,22 @@ void fpa2bv_converter::mk_to_ieee_bv(func_decl * f, unsigned num, expr * const * if (m_hi_fp_unspecified) // The "hardware interpretation" is 01...10...01. nanv = m_bv_util.mk_concat(m_bv_util.mk_numeral(0, 1), - m_bv_util.mk_concat(m_bv_util.mk_numeral(-1, ebits), - m_bv_util.mk_concat(m_bv_util.mk_numeral(0, sbits - 2), - m_bv_util.mk_numeral(1, 1)))); + m_bv_util.mk_concat(m_bv_util.mk_numeral(-1, ebits), + m_bv_util.mk_concat(m_bv_util.mk_numeral(0, sbits - 2), + m_bv_util.mk_numeral(1, 1)))); else nanv = mk_to_ieee_bv_unspecified(ebits, sbits); - result = m.mk_ite(x_is_nan, nanv, m_bv_util.mk_concat(m_bv_util.mk_concat(sgn, e), s)); + expr_ref sgn_e_s(m); + sgn_e_s = m_bv_util.mk_concat(m_bv_util.mk_concat(sgn, e), s); + m_simp.mk_ite(x_is_nan, nanv, sgn_e_s, result); SASSERT(is_well_sorted(m, result)); } void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result) { TRACE("fpa2bv_to_bv", for (unsigned i = 0; i < num; i++) - tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); SASSERT(num == 2); SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); @@ -3016,14 +3033,15 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args sig_sz = m_bv_util.get_bv_size(sig); SASSERT(sig_sz >= (bv_sz + 3)); - expr_ref exp_m_lz(m), e_m_lz_m_bv_sz(m), shift(m), bv0_e2(m), shift_abs(m); + expr_ref exp_m_lz(m), e_m_lz_m_bv_sz(m), shift(m), bv0_e2(m), shift_abs(m), shift_le_0(m); exp_m_lz = m_bv_util.mk_bv_sub(m_bv_util.mk_sign_extend(2, exp), - m_bv_util.mk_zero_extend(2, lz)); + m_bv_util.mk_zero_extend(2, lz)); e_m_lz_m_bv_sz = m_bv_util.mk_bv_sub(exp_m_lz, - m_bv_util.mk_numeral(bv_sz - 1, ebits + 2)); + m_bv_util.mk_numeral(bv_sz - 1, ebits + 2)); shift = m_bv_util.mk_bv_neg(e_m_lz_m_bv_sz); bv0_e2 = m_bv_util.mk_numeral(0, ebits + 2); - shift_abs = m.mk_ite(m_bv_util.mk_sle(shift, bv0_e2), e_m_lz_m_bv_sz, shift); + shift_le_0 = m_bv_util.mk_sle(shift, bv0_e2); + shift_abs = m.mk_ite(shift_le_0, e_m_lz_m_bv_sz, shift); SASSERT(m_bv_util.get_bv_size(shift) == ebits + 2); SASSERT(m_bv_util.get_bv_size(shift_abs) == ebits + 2); dbg_decouple("fpa2bv_to_bv_shift", shift); @@ -3041,15 +3059,15 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args c_in_limits = m_bv_util.mk_sle(bv0_e2, shift); else c_in_limits = m.mk_or(m_bv_util.mk_sle(m_bv_util.mk_numeral(1, ebits + 2), shift), - m.mk_and(m.mk_eq(m_bv_util.mk_numeral(0, ebits + 2), shift), - m.mk_eq(sig, m_bv_util.mk_concat(bv1, m_bv_util.mk_numeral(0, sig_sz-1))))); + m.mk_and(m.mk_eq(m_bv_util.mk_numeral(0, ebits + 2), shift), + m.mk_eq(sig, m_bv_util.mk_concat(bv1, m_bv_util.mk_numeral(0, sig_sz-1))))); dbg_decouple("fpa2bv_to_bv_in_limits", c_in_limits); expr_ref r_shifted_sig(m), l_shifted_sig(m); r_shifted_sig = m_bv_util.mk_bv_lshr(sig, shift_abs); l_shifted_sig = m_bv_util.mk_bv_shl(sig, m_bv_util.mk_bv_sub( - m_bv_util.mk_numeral(m_bv_util.get_bv_size(sig), m_bv_util.get_bv_size(sig)), - shift_abs)); + m_bv_util.mk_numeral(m_bv_util.get_bv_size(sig), m_bv_util.get_bv_size(sig)), + shift_abs)); dbg_decouple("fpa2bv_to_bv_r_shifted_sig", r_shifted_sig); dbg_decouple("fpa2bv_to_bv_l_shifted_sig", l_shifted_sig); @@ -3079,13 +3097,19 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args rnd_has_overflown = m.mk_eq(rnd_overflow, bv1); dbg_decouple("fpa2bv_to_bv_rnd_has_overflown", rnd_has_overflown); - if (is_signed) - rnd = m.mk_ite(m.mk_eq(sgn, bv1), m_bv_util.mk_bv_neg(rnd), rnd); + if (is_signed) { + expr_ref sgn_eq_1(m), neg_rnd(m); + sgn_eq_1 = m.mk_eq(sgn, bv1); + neg_rnd = m_bv_util.mk_bv_neg(rnd); + m_simp.mk_ite(sgn_eq_1, neg_rnd, rnd, rnd); + } dbg_decouple("fpa2bv_to_bv_rnd", rnd); - result = m.mk_ite(rnd_has_overflown, mk_to_ubv_unspecified(ebits, sbits, bv_sz), rnd); - result = m.mk_ite(c_in_limits, result, mk_to_ubv_unspecified(ebits, sbits, bv_sz)); + expr_ref unspec(m); + unspec = mk_to_ubv_unspecified(ebits, sbits, bv_sz); + result = m.mk_ite(rnd_has_overflown, unspec, rnd); + result = m.mk_ite(c_in_limits, result, unspec); result = m.mk_ite(c2, v2, result); result = m.mk_ite(c1, v1, result); @@ -3094,13 +3118,13 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args void fpa2bv_converter::mk_to_ubv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_ubv", for (unsigned i = 0; i < num; i++) - tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); mk_to_bv(f, num, args, false, result); } void fpa2bv_converter::mk_to_sbv(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv_to_sbv", for (unsigned i = 0; i < num; i++) - tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); + tout << "arg" << i << " = " << mk_ismt2_pp(args[i], m) << std::endl;); mk_to_bv(f, num, args, true, result); } @@ -3571,14 +3595,14 @@ void fpa2bv_converter::mk_rounding_mode(func_decl * f, expr_ref & result) } void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { - #ifdef Z3DEBUG +#ifdef Z3DEBUG return; // CMW: This works only for quantifier-free formulas. expr_ref new_e(m); new_e = m.mk_fresh_const(prefix, m.get_sort(e)); m_extra_assertions.push_back(m.mk_eq(new_e, e)); e = new_e; - #endif +#endif } expr_ref fpa2bv_converter::mk_rounding_decision(expr * bv_rm, expr * sgn, expr * last, expr * round, expr * sticky) { @@ -3598,7 +3622,7 @@ expr_ref fpa2bv_converter::mk_rounding_decision(expr * bv_rm, expr * sgn, expr * expr * round_sticky[2] = { round, sticky }; last_or_sticky = m_bv_util.mk_bv_or(2, last_sticky); round_or_sticky = m_bv_util.mk_bv_or(2, round_sticky); - not_last= m_bv_util.mk_bv_not(last); + not_last = m_bv_util.mk_bv_not(last); not_round = m_bv_util.mk_bv_not(round); not_sticky = m_bv_util.mk_bv_not(sticky); not_lors = m_bv_util.mk_bv_not(last_or_sticky); @@ -3613,7 +3637,7 @@ expr_ref fpa2bv_converter::mk_rounding_decision(expr * bv_rm, expr * sgn, expr * expr_ref inc_teven(m), inc_taway(m), inc_pos(m), inc_neg(m); inc_teven = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, nround_lors)); expr *taway_args[2] = { m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, nl_r)), - m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(3, nl_nr_sn)) }; + m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(3, nl_nr_sn)) }; inc_taway = m_bv_util.mk_bv_or(2, taway_args); inc_pos = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, pos_args)); inc_neg = m_bv_util.mk_bv_not(m_bv_util.mk_bv_or(2, neg_args)); @@ -3649,11 +3673,11 @@ void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_re SASSERT(is_well_sorted(m, exp)); TRACE("fpa2bv_dbg", tout << "RND: " << std::endl << - "ebits = " << ebits << std::endl << - "sbits = " << sbits << std::endl << - "sgn = " << mk_ismt2_pp(sgn, m) << std::endl << - "sig = " << mk_ismt2_pp(sig, m) << std::endl << - "exp = " << mk_ismt2_pp(exp, m) << std::endl; ); + "ebits = " << ebits << std::endl << + "sbits = " << sbits << std::endl << + "sgn = " << mk_ismt2_pp(sgn, m) << std::endl << + "sig = " << mk_ismt2_pp(sig, m) << std::endl << + "exp = " << mk_ismt2_pp(exp, m) << std::endl; ); // Assumptions: sig is of the form f[-1:0] . f[1:sbits-1] [guard,round,sticky], // i.e., it has 2 + (sbits-1) + 3 = sbits + 4 bits, where the first one is in sgn. @@ -3675,7 +3699,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_re mk_max_exp(ebits, e_max); TRACE("fpa2bv_dbg", tout << "e_min = " << mk_ismt2_pp(e_min, m) << std::endl << - "e_max = " << mk_ismt2_pp(e_max, m) << std::endl;); + "e_max = " << mk_ismt2_pp(e_max, m) << std::endl;); expr_ref OVF1(m), e_top_three(m), sigm1(m), e_eq_emax_and_sigm1(m), e_eq_emax(m); expr_ref e3(m), ne3(m), e2(m), e1(m), e21(m), one_1(m), h_exp(m), sh_exp(m), th_exp(m); @@ -3724,7 +3748,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_re expr_ref beta(m); beta = m_bv_util.mk_bv_add(m_bv_util.mk_bv_sub(exp, lz), m_bv_util.mk_numeral(1, ebits+2)); - TRACE("fpa2bv_dbg", tout << "beta = " << mk_ismt2_pp(beta, m)<< std::endl; ); + TRACE("fpa2bv_dbg", tout << "beta = " << mk_ismt2_pp(beta, m) << std::endl; ); SASSERT(is_well_sorted(m, beta)); dbg_decouple("fpa2bv_rnd_beta", beta); @@ -3745,12 +3769,12 @@ void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_re dbg_decouple("fpa2bv_rnd_sig_before_shift", sig); unsigned sig_size = m_bv_util.get_bv_size(sig); - SASSERT(sig_size == sbits+4); + SASSERT(sig_size == sbits + 4); SASSERT(m_bv_util.get_bv_size(sigma) == ebits+2); - unsigned sigma_size = ebits+2; + unsigned sigma_size = ebits + 2; expr_ref sigma_neg(m), sigma_cap(m), sigma_neg_capped(m), sigma_lt_zero(m), sig_ext(m), - rs_sig(m), ls_sig(m), big_sh_sig(m), sigma_le_cap(m); + rs_sig(m), ls_sig(m), big_sh_sig(m), sigma_le_cap(m); sigma_neg = m_bv_util.mk_bv_neg(sigma); sigma_cap = m_bv_util.mk_numeral(sbits+2, sigma_size); sigma_le_cap = m_bv_util.mk_ule(sigma_neg, sigma_cap); @@ -3815,7 +3839,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_re dbg_decouple("fpa2bv_rnd_inc", inc); sig = m_bv_util.mk_bv_add(m_bv_util.mk_zero_extend(1, sig), - m_bv_util.mk_zero_extend(sbits, inc)); + m_bv_util.mk_zero_extend(sbits, inc)); SASSERT(is_well_sorted(m, sig)); dbg_decouple("fpa2bv_rnd_sig_plus_inc", sig); @@ -3900,7 +3924,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_re expr_ref max_sig(m), max_exp(m), inf_sig(m), inf_exp(m); max_sig = m_bv_util.mk_numeral(fu().fm().m_powers2.m1(sbits-1, false), sbits-1); max_exp = m_bv_util.mk_concat(m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1, false), ebits-1), - m_bv_util.mk_numeral(0, 1)); + m_bv_util.mk_numeral(0, 1)); inf_sig = m_bv_util.mk_numeral(0, sbits-1); inf_exp = top_exp; @@ -3956,8 +3980,8 @@ void fpa2bv_converter::reset(void) { dec_ref_map_key_values(m, m_rm_const2bv); dec_ref_map_key_values(m, m_uf2bvuf); for (obj_map >::iterator it = m_specials.begin(); - it != m_specials.end(); - it++) { + it != m_specials.end(); + it++) { m.dec_ref(it->m_key); m.dec_ref(it->m_value.first); m.dec_ref(it->m_value.second); From 5d0db6d2569831ab48e03370602de0a1ff4c20a2 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 18 Apr 2016 17:18:16 +0100 Subject: [PATCH 30/66] Fixed memory leak in goal::update. Fixes #567 --- src/tactic/goal.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index fbabd469e..9b2863769 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -145,13 +145,14 @@ void goal::quick_process(bool save_first, expr * & f, expr_dependency * d) { } typedef std::pair expr_pol; sbuffer todo; + expr_ref_vector tmp_exprs(m()); todo.push_back(expr_pol(f, true)); while (!todo.empty()) { if (m_inconsistent) return; - expr_pol p = todo.back(); + expr_pol p = todo.back(); expr * curr = p.first; - bool pol = p.second; + bool pol = p.second; todo.pop_back(); if (pol && m().is_and(curr)) { app * t = to_app(curr); @@ -173,10 +174,12 @@ void goal::quick_process(bool save_first, expr * & f, expr_dependency * d) { todo.push_back(expr_pol(to_app(curr)->get_arg(0), !pol)); } else { - if (!pol) + if (!pol) { curr = m().mk_not(curr); + tmp_exprs.push_back(curr); + } if (save_first) { - f = curr; + f = curr; save_first = false; } else { From 4761f4f191d81129a832916a81e6bc1ebe6dc146 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Apr 2016 11:14:40 -0700 Subject: [PATCH 31/66] add handling for int.to.str Signed-off-by: Nikolaj Bjorner --- src/ast/seq_decl_plugin.h | 2 ++ src/smt/theory_seq.cpp | 66 +++++++++++++++++++++++++++++++++++++++ src/smt/theory_seq.h | 11 +++++++ 3 files changed, 79 insertions(+) diff --git a/src/ast/seq_decl_plugin.h b/src/ast/seq_decl_plugin.h index 22156afb8..78672182a 100644 --- a/src/ast/seq_decl_plugin.h +++ b/src/ast/seq_decl_plugin.h @@ -240,6 +240,8 @@ public: app* mk_index(expr* a, expr* b, expr* i) { expr* es[3] = { a, b, i}; return m.mk_app(m_fid, OP_SEQ_INDEX, 3, es); } app* mk_unit(expr* u) { return m.mk_app(m_fid, OP_SEQ_UNIT, 1, &u); } app* mk_char(zstring const& s, unsigned idx); + app* mk_itos(expr* i) { return m.mk_app(m_fid, OP_STRING_ITOS, 1, &i); } + app* mk_stoi(expr* s) { return m.mk_app(m_fid, OP_STRING_STOI, 1, &s); } bool is_string(expr const * n) const { return is_app_of(n, m_fid, OP_STRING_CONST); } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 7703f70aa..22cd6482f 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -202,6 +202,7 @@ theory_seq::theory_seq(ast_manager& m): m_exclude(m), m_axioms(m), m_axioms_head(0), + m_int_string(m), m_mg(0), m_rewrite(m), m_seq_rewrite(m), @@ -257,6 +258,11 @@ final_check_status theory_seq::final_check_eh() { TRACE("seq", tout << ">>fixed_length\n";); return FC_CONTINUE; } + if (check_int_string()) { + ++m_stats.m_int_string; + TRACE("seq", tout << ">>int_string\n";); + return FC_CONTINUE; + } if (reduce_length_eq() || branch_unit_variable() || branch_binary_variable() || branch_variable_mb() || branch_variable()) { ++m_stats.m_branch_variable; TRACE("seq", tout << ">>branch_variable\n";); @@ -2160,6 +2166,7 @@ void theory_seq::add_length(expr* e) { m_trail_stack.push(insert_obj_trail(m_length, e)); } + /* ensure that all elements in equivalence class occur under an applicatin of 'length' */ @@ -2177,6 +2184,48 @@ void theory_seq::enforce_length(enode* n) { while (n1 != n); } + +void theory_seq::add_int_string(expr* e) { + m_int_string.push_back(e); + m_trail_stack.push(push_back_vector(m_int_string)); +} + +bool theory_seq::check_int_string() { + bool change = false; + for (unsigned i = 0; i < m_int_string.size(); ++i) { + expr* e = m_int_string[i].get(), *n; + if (add_itos_axiom(e)) { + change = true; + } + else if (m_util.str.is_stoi(e, n)) { + // not (yet) handled. + // we would check that in the current proto-model + // the string at 'n', when denoting integer would map to the + // proper integer. + } + } + return change; +} + +bool theory_seq::add_itos_axiom(expr* e) { + context& ctx = get_context(); + rational val; + expr* n; + if (m_util.str.is_itos(e, n) && get_value(n, val)) { + app_ref e1(m_util.str.mk_string(symbol(val.to_string().c_str())), m); + if (!m_itos_axioms.contains(val)) { + m_itos_axioms.insert(val); + + expr_ref n1(arith_util(m).mk_numeral(val, true), m); + add_axiom(mk_eq(m_util.str.mk_itos(n1), e1, false)); + m_trail_stack.push(insert_map(m_itos_axioms, val)); + m_trail_stack.push(push_replay(alloc(replay_axiom, m, e))); + return true; + } + } + return false; +} + void theory_seq::apply_sort_cnstr(enode* n, sort* s) { mk_var(n); } @@ -2317,6 +2366,7 @@ void theory_seq::collect_statistics(::statistics & st) const { st.update("seq add axiom", m_stats.m_add_axiom); st.update("seq extensionality", m_stats.m_extensionality); st.update("seq fixed length", m_stats.m_fixed_length); + st.update("seq int.to.str", m_stats.m_int_string); } void theory_seq::init_model(expr_ref_vector const& es) { @@ -2627,6 +2677,9 @@ void theory_seq::deque_axiom(expr* n) { else if (m_util.str.is_string(n)) { add_elim_string_axiom(n); } + else if (m_util.str.is_itos(n)) { + add_itos_axiom(n); + } } @@ -2890,6 +2943,14 @@ static theory_mi_arith* get_th_arith(context& ctx, theory_id afid, expr* e) { } } +bool theory_seq::get_value(expr* e, rational& val) const { + context& ctx = get_context(); + theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); + expr_ref _val(m); + if (!tha || !tha->get_value(ctx.get_enode(e), _val)) return false; + return m_autil.is_numeral(_val, val) && val.is_int(); +} + bool theory_seq::lower_bound(expr* _e, rational& lo) const { context& ctx = get_context(); expr_ref e(m_util.str.mk_length(_e), m); @@ -3525,6 +3586,11 @@ void theory_seq::relevant_eh(app* n) { enque_axiom(n); } + if (m_util.str.is_itos(n) || + m_util.str.is_stoi(n)) { + add_int_string(n); + } + expr* arg; if (m_util.str.is_length(n, arg) && !has_length(arg)) { enforce_length(get_context().get_enode(arg)); diff --git a/src/smt/theory_seq.h b/src/smt/theory_seq.h index fc37a8f06..4107f3d05 100644 --- a/src/smt/theory_seq.h +++ b/src/smt/theory_seq.h @@ -287,7 +287,10 @@ namespace smt { unsigned m_extensionality; unsigned m_fixed_length; unsigned m_propagate_contains; + unsigned m_int_string; }; + typedef hashtable rational_set; + ast_manager& m; dependency_manager m_dm; solution_map m_rep; // unification representative. @@ -303,6 +306,8 @@ namespace smt { obj_hashtable m_axiom_set; unsigned m_axioms_head; // index of first axiom to add. bool m_incomplete; // is the solver (clearly) incomplete for the fragment. + expr_ref_vector m_int_string; + rational_set m_itos_axioms; obj_hashtable m_length; // is length applied scoped_ptr_vector m_replay; // set of actions to replay model_generator* m_mg; @@ -481,9 +486,14 @@ namespace smt { bool enforce_length(expr_ref_vector const& es, vector& len); void enforce_length_coherence(enode* n1, enode* n2); + // model-check the functions that convert integers to strings and the other way. + void add_int_string(expr* e); + bool check_int_string(); + void add_elim_string_axiom(expr* n); void add_at_axiom(expr* n); void add_in_re_axiom(expr* n); + bool add_itos_axiom(expr* n); literal mk_literal(expr* n); literal mk_eq_empty(expr* n, bool phase = true); literal mk_seq_eq(expr* a, expr* b); @@ -496,6 +506,7 @@ namespace smt { // arithmetic integration + bool get_value(expr* s, rational& val) const; bool lower_bound(expr* s, rational& lo) const; bool upper_bound(expr* s, rational& hi) const; bool get_length(expr* s, rational& val) const; From 81232808ba9e2f6fb4d9c02648b2ef0e64bbea6a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Apr 2016 11:17:33 -0700 Subject: [PATCH 32/66] add handling for int.to.str Signed-off-by: Nikolaj Bjorner --- src/smt/theory_seq.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 22cd6482f..58b58e9fe 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -2194,7 +2194,7 @@ bool theory_seq::check_int_string() { bool change = false; for (unsigned i = 0; i < m_int_string.size(); ++i) { expr* e = m_int_string[i].get(), *n; - if (add_itos_axiom(e)) { + if (m_util.str.is_itos(e) && add_itos_axiom(e)) { change = true; } else if (m_util.str.is_stoi(e, n)) { @@ -2211,11 +2211,12 @@ bool theory_seq::add_itos_axiom(expr* e) { context& ctx = get_context(); rational val; expr* n; - if (m_util.str.is_itos(e, n) && get_value(n, val)) { - app_ref e1(m_util.str.mk_string(symbol(val.to_string().c_str())), m); + VERIFY(m_util.str.is_itos(e, n)); + if (get_value(n, val)) { if (!m_itos_axioms.contains(val)) { m_itos_axioms.insert(val); - + + app_ref e1(m_util.str.mk_string(symbol(val.to_string().c_str())), m); expr_ref n1(arith_util(m).mk_numeral(val, true), m); add_axiom(mk_eq(m_util.str.mk_itos(n1), e1, false)); m_trail_stack.push(insert_map(m_itos_axioms, val)); From c3f4124a9f70c9ec7e9304c190d7f5a847b80b16 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Apr 2016 14:50:10 -0700 Subject: [PATCH 33/66] trace down recent exposed regression in goal2sat, incorporate Scott's suggestion on making vector --- src/model/func_interp.cpp | 9 +++------ src/qe/qe_arith.cpp | 1 - src/qe/qe_datatypes.cpp | 1 - src/sat/tactic/goal2sat.cpp | 8 ++++---- src/smt/theory_seq.cpp | 3 --- src/util/vector.h | 10 ++++++++++ 6 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index 5c53e64ba..96201d900 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -107,13 +107,10 @@ void func_interp::reset_interp_cache() { bool func_interp::is_fi_entry_expr(expr * e, ptr_vector & args) { args.reset(); - if (!is_app(e) || !m().is_ite(to_app(e))) + expr* c, *t, *f; + if (!m().is_ite(e, c, t, f)) { return false; - - app * a = to_app(e); - expr * c = a->get_arg(0); - expr * t = a->get_arg(1); - expr * f = a->get_arg(2); + } if ((m_arity == 0) || (m_arity == 1 && (!m().is_eq(c) || to_app(c)->get_num_args() != 2)) || diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index e80adb40e..a3fcda6de 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -32,7 +32,6 @@ namespace qe { bool is_divides(arith_util& a, expr* e1, expr* e2, rational& k, expr_ref& p) { expr* t1, *t2; - ast_manager& m = a.get_manager(); if (a.is_mod(e2, t1, t2) && a.is_numeral(e1, k) && k.is_zero() && diff --git a/src/qe/qe_datatypes.cpp b/src/qe/qe_datatypes.cpp index 81001dea5..aa67d28a3 100644 --- a/src/qe/qe_datatypes.cpp +++ b/src/qe/qe_datatypes.cpp @@ -87,7 +87,6 @@ namespace qe { } void project_rec(model& model, app_ref_vector& vars, expr_ref_vector& lits) { - func_decl* f = m_val->get_decl(); expr_ref rhs(m); expr_ref_vector eqs(m); for (unsigned i = 0; i < lits.size(); ++i) { diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 62c491b62..4d5542866 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -324,6 +324,7 @@ struct goal2sat::imp { } void process(expr * n) { + //SASSERT(m_result_stack.empty()); TRACE("goal2sat", tout << "converting: " << mk_ismt2_pp(n, m) << "\n";); if (visit(n, true, false)) { SASSERT(m_result_stack.empty()); @@ -342,8 +343,7 @@ struct goal2sat::imp { 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";); + tout << m_result_stack << "\n";); if (fr.m_idx == 0 && process_cached(t, root, sign)) { m_frame_stack.pop_back(); continue; @@ -362,11 +362,11 @@ struct goal2sat::imp { } 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";); + tout << m_result_stack << "\n";); convert(t, root, sign); m_frame_stack.pop_back(); } + CTRACE("goal2sat", !m_result_stack.empty(), tout << m_result_stack << "\n";); SASSERT(m_result_stack.empty()); } diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 58b58e9fe..a8811b179 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -298,7 +298,6 @@ final_check_status theory_seq::final_check_eh() { bool theory_seq::reduce_length_eq() { context& ctx = get_context(); - unsigned sz = m_eqs.size(); int start = ctx.get_random_value(); for (unsigned i = 0; !ctx.inconsistent() && i < m_eqs.size(); ++i) { @@ -457,7 +456,6 @@ void theory_seq::branch_unit_variable(dependency* dep, expr* X, expr_ref_vector } bool theory_seq::branch_variable_mb() { - context& ctx = get_context(); bool change = false; for (unsigned i = 0; i < m_eqs.size(); ++i) { eq const& e = m_eqs[i]; @@ -2208,7 +2206,6 @@ bool theory_seq::check_int_string() { } bool theory_seq::add_itos_axiom(expr* e) { - context& ctx = get_context(); rational val; expr* n; VERIFY(m_util.str.is_itos(e, n)); diff --git a/src/util/vector.h b/src/util/vector.h index faab3b86c..03c1d6019 100644 --- a/src/util/vector.h +++ b/src/util/vector.h @@ -457,5 +457,15 @@ template struct svector_hash : public vector_hash_tpl > {}; +// Specialize vector to be inaccessible. +// This will catch any regression of issue #564 and #420. +// Use std::vector instead. +template <> +class vector { +private: + vector(); +}; + + #endif /* VECTOR_H_ */ From 4cb57cd4dad67168cb13f69329355aaa55058fb7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Apr 2016 17:22:47 -0700 Subject: [PATCH 34/66] fix regression introduced by using ref-vectors on non-ref'ed output parameters Signed-off-by: Nikolaj Bjorner --- src/tactic/goal.cpp | 17 ++++++++++------- src/tactic/goal.h | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/tactic/goal.cpp b/src/tactic/goal.cpp index 9b2863769..8897a83f8 100644 --- a/src/tactic/goal.cpp +++ b/src/tactic/goal.cpp @@ -136,7 +136,7 @@ void goal::push_back(expr * f, proof * pr, expr_dependency * d) { } } -void goal::quick_process(bool save_first, expr * & f, expr_dependency * d) { +void goal::quick_process(bool save_first, expr_ref& f, expr_dependency * d) { if (!m().is_and(f) && !(m().is_not(f) && m().is_or(to_app(f)->get_arg(0)))) { if (!save_first) { push_back(f, 0, d); @@ -175,7 +175,7 @@ void goal::quick_process(bool save_first, expr * & f, expr_dependency * d) { } else { if (!pol) { - curr = m().mk_not(curr); + curr = m().mk_not(curr); tmp_exprs.push_back(curr); } if (save_first) { @@ -242,8 +242,10 @@ void goal::assert_expr(expr * f, proof * pr, expr_dependency * d) { return; if (proofs_enabled()) slow_process(f, pr, d); - else - quick_process(false, f, d); + else { + expr_ref fr(f, m()); + quick_process(false, fr, d); + } } void goal::assert_expr(expr * f, expr_dependency * d) { @@ -279,13 +281,14 @@ void goal::update(unsigned i, expr * f, proof * pr, expr_dependency * d) { } } else { - quick_process(true, f, d); + expr_ref fr(f, m()); + quick_process(true, fr, d); if (!m_inconsistent) { - if (m().is_false(f)) { + if (m().is_false(fr)) { push_back(f, 0, d); } else { - m().set(m_forms, i, f); + m().set(m_forms, i, fr); if (unsat_core_enabled()) m().set(m_dependencies, i, d); } diff --git a/src/tactic/goal.h b/src/tactic/goal.h index 147c5b2e9..ea02dfa17 100644 --- a/src/tactic/goal.h +++ b/src/tactic/goal.h @@ -62,7 +62,7 @@ protected: unsigned m_precision:2; // PRECISE, UNDER, OVER. void push_back(expr * f, proof * pr, expr_dependency * d); - void quick_process(bool save_first, expr * & f, expr_dependency * d); + void quick_process(bool save_first, expr_ref & f, expr_dependency * d); void process_and(bool save_first, app * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr); void process_not_or(bool save_first, app * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr); void slow_process(bool save_first, expr * f, proof * pr, expr_dependency * d, expr_ref & out_f, proof_ref & out_pr); From 3a6218ac21532a7af1621db895458c5606defa47 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Apr 2016 17:30:52 -0700 Subject: [PATCH 35/66] update func_interp code Signed-off-by: Nikolaj Bjorner --- src/model/func_interp.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index 96201d900..4dd9d599f 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -107,25 +107,23 @@ void func_interp::reset_interp_cache() { bool func_interp::is_fi_entry_expr(expr * e, ptr_vector & args) { args.reset(); - expr* c, *t, *f; + expr* c, *t, *f, *a0, *a1; if (!m().is_ite(e, c, t, f)) { return false; } if ((m_arity == 0) || - (m_arity == 1 && (!m().is_eq(c) || to_app(c)->get_num_args() != 2)) || + (m_arity == 1 && !m().is_eq(c, a1, a2)) || (m_arity > 1 && (!m().is_and(c) || to_app(c)->get_num_args() != m_arity))) return false; args.resize(m_arity, 0); for (unsigned i = 0; i < m_arity; i++) { - expr * ci = (m_arity == 1 && i == 0) ? to_app(c) : to_app(c)->get_arg(i); + expr * ci = (m_arity == 1 && i == 0) ? c : to_app(c)->get_arg(i); - if (!m().is_eq(ci) || to_app(ci)->get_num_args() != 2) + if (!m().is_eq(ci, a0, a1)) return false; - expr * a0 = to_app(ci)->get_arg(0); - expr * a1 = to_app(ci)->get_arg(1); if (is_var(a0) && to_var(a0)->get_idx() == i) args[i] = a1; else if (is_var(a1) && to_var(a1)->get_idx() == i) From b512212d4129c0c0a6dfcba37275ec73f6c45f9a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 18 Apr 2016 17:31:36 -0700 Subject: [PATCH 36/66] update func_interp code Signed-off-by: Nikolaj Bjorner --- src/model/func_interp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/model/func_interp.cpp b/src/model/func_interp.cpp index 4dd9d599f..01e160eb0 100644 --- a/src/model/func_interp.cpp +++ b/src/model/func_interp.cpp @@ -113,7 +113,7 @@ bool func_interp::is_fi_entry_expr(expr * e, ptr_vector & args) { } if ((m_arity == 0) || - (m_arity == 1 && !m().is_eq(c, a1, a2)) || + (m_arity == 1 && !m().is_eq(c, a0, a1)) || (m_arity > 1 && (!m().is_and(c) || to_app(c)->get_num_args() != m_arity))) return false; From 417c80edbccb54842b62ecf644d9cd35f31c70d0 Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Tue, 19 Apr 2016 02:17:12 -0700 Subject: [PATCH 37/66] fix mem leak in quantifier_info::insert_qinfo on timeout --- src/smt/smt_model_finder.cpp | 4 ++-- src/util/util.h | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/smt/smt_model_finder.cpp b/src/smt/smt_model_finder.cpp index f166125ba..1f8f6dabe 100644 --- a/src/smt/smt_model_finder.cpp +++ b/src/smt/smt_model_finder.cpp @@ -1756,16 +1756,16 @@ namespace smt { void insert_qinfo(qinfo * qi) { // I'm assuming the number of qinfo's per quantifier is small. So, the linear search is not a big deal. + scoped_ptr q(qi); ptr_vector::iterator it = m_qinfo_vect.begin(); ptr_vector::iterator end = m_qinfo_vect.end(); for (; it != end; ++it) { checkpoint(); if (qi->is_equal(*it)) { - dealloc(qi); return; } } - m_qinfo_vect.push_back(qi); + m_qinfo_vect.push_back(q.detach()); TRACE("model_finder", tout << "new quantifier qinfo: "; qi->display(tout); tout << "\n";); } diff --git a/src/util/util.h b/src/util/util.h index 0dfa8ac8c..a040a79ae 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -226,9 +226,7 @@ public: } ~scoped_ptr() { - if (m_ptr) { - dealloc(m_ptr); - } + dealloc(m_ptr); } T * operator->() const { @@ -253,9 +251,7 @@ public: scoped_ptr & operator=(T * n) { if (m_ptr != n) { - if (m_ptr) { - dealloc(m_ptr); - } + dealloc(m_ptr); m_ptr = n; } return *this; From d0175b96b84e2785e3fcd9c0786cdc5d10a1fe50 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 20 Apr 2016 14:07:45 -0700 Subject: [PATCH 38/66] guarding against null symbols creeping in. Issue #571 Signed-off-by: Nikolaj Bjorner --- src/api/api_context.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/api_context.cpp b/src/api/api_context.cpp index 47a25f313..bc48874a7 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -131,7 +131,7 @@ namespace api { } char * context::mk_external_string(char const * str) { - m_string_buffer = str; + m_string_buffer = str?str:""; return const_cast(m_string_buffer.c_str()); } From 20a6b41c5c89a61a61a5762c13efb9b6b8e444b5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 21 Apr 2016 10:49:16 -0700 Subject: [PATCH 39/66] coalescing is-int check for python 2.x, issue #572 Signed-off-by: Nikolaj Bjorner --- src/api/python/z3.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/api/python/z3.py b/src/api/python/z3.py index 211b6777e..848883ba3 100644 --- a/src/api/python/z3.py +++ b/src/api/python/z3.py @@ -52,7 +52,7 @@ import math if sys.version < '3': def _is_int(v): - return isinstance(v, int) or isinstance(v, long) + return isinstance(v, (int, long)) else: def _is_int(v): return isinstance(v, int) @@ -95,7 +95,7 @@ def append_log(s): def to_symbol(s, ctx=None): """Convert an integer or string into a Z3 symbol.""" - if isinstance(s, int): + if _is_int(s): return Z3_mk_int_symbol(_get_ctx(ctx).ref(), s) else: return Z3_mk_string_symbol(_get_ctx(ctx).ref(), s) @@ -3627,7 +3627,7 @@ def Extract(high, low, a): return SeqRef(Z3_mk_seq_extract(s.ctx_ref(), s.as_ast(), offset.as_ast(), length.as_ast()), s.ctx) if __debug__: _z3_assert(low <= high, "First argument must be greater than or equal to second argument") - _z3_assert(isinstance(high, int) and high >= 0 and isinstance(low, int) and low >= 0, "First and second arguments must be non negative integers") + _z3_assert(_is_int(high) and high >= 0 and _is_int(low) and low >= 0, "First and second arguments must be non negative integers") _z3_assert(is_bv(a), "Third argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_extract(a.ctx_ref(), high, low, a.as_ast()), a.ctx) @@ -3849,7 +3849,7 @@ def SignExt(n, a): fe """ if __debug__: - _z3_assert(isinstance(n, int), "First argument must be an integer") + _z3_assert(_is_int(n), "First argument must be an integer") _z3_assert(is_bv(a), "Second argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_sign_ext(a.ctx_ref(), n, a.as_ast()), a.ctx) @@ -3876,7 +3876,7 @@ def ZeroExt(n, a): 8 """ if __debug__: - _z3_assert(isinstance(n, int), "First argument must be an integer") + _z3_assert(_is_int(n), "First argument must be an integer") _z3_assert(is_bv(a), "Second argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_zero_ext(a.ctx_ref(), n, a.as_ast()), a.ctx) @@ -3899,7 +3899,7 @@ def RepeatBitVec(n, a): aaaa """ if __debug__: - _z3_assert(isinstance(n, int), "First argument must be an integer") + _z3_assert(_is_int(n), "First argument must be an integer") _z3_assert(is_bv(a), "Second argument must be a Z3 Bitvector expression") return BitVecRef(Z3_mk_repeat(a.ctx_ref(), n, a.as_ast()), a.ctx) @@ -4580,7 +4580,7 @@ class ParamsRef: name_sym = to_symbol(name, self.ctx) if isinstance(val, bool): Z3_params_set_bool(self.ctx.ref(), self.params, name_sym, val) - elif isinstance(val, int): + elif _is_int(val): Z3_params_set_uint(self.ctx.ref(), self.params, name_sym, val) elif isinstance(val, float): Z3_params_set_double(self.ctx.ref(), self.params, name_sym, val) @@ -5627,7 +5627,7 @@ class ModelRef(Z3PPObject): x -> 1 f -> [1 -> 0, else -> 0] """ - if isinstance(idx, int): + if _is_int(idx): if idx >= len(self): raise IndexError num_consts = Z3_model_get_num_consts(self.ctx.ref(), self.model) @@ -9310,7 +9310,7 @@ def IndexOf(s, substr, offset): ctx = _get_ctx2(s, substr, ctx) s = _coerce_seq(s, ctx) substr = _coerce_seq(substr, ctx) - if isinstance(offset, int): + if _is_int(offset): offset = IntVal(offset, ctx) return SeqRef(Z3_mk_seq_index(s.ctx_ref(), s.as_ast(), substr.as_ast(), offset.as_ast()), s.ctx) From e4b7ac37f38f1a75da362bb7a2afa87c98da54f1 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 22 Apr 2016 13:58:02 -0700 Subject: [PATCH 40/66] add overloading for arithmetical expressions in C# to handle common cases Signed-off-by: Nikolaj Bjorner --- src/api/dotnet/ArithExpr.cs | 162 ++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) diff --git a/src/api/dotnet/ArithExpr.cs b/src/api/dotnet/ArithExpr.cs index 7858ff3e1..56aa48f26 100644 --- a/src/api/dotnet/ArithExpr.cs +++ b/src/api/dotnet/ArithExpr.cs @@ -38,5 +38,167 @@ namespace Microsoft.Z3 Contract.Requires(ctx != null); } #endregion + + /// Operator overloading for arithmetical operator + public static ArithExpr operator -(ArithExpr a, ArithExpr b) + { + return a.Context.MkSub(a, b); + } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator +(ArithExpr a, ArithExpr b) + { + return a.Context.MkAdd(a, b); + } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator *(ArithExpr a, ArithExpr b) + { + return a.Context.MkMul(a, b); + } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator *(ArithExpr a, int b) + { + return a.Context.MkMul(a, (ArithExpr)a.Context.MkNumeral(b, a.Context.MkIntSort())); + } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator *(ArithExpr a, double b) + { + return a.Context.MkMul(a, (ArithExpr)a.Context.MkNumeral(b.ToString(), a.Context.MkRealSort())); + } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator *(int a, ArithExpr b) + { + return b.Context.MkMul((ArithExpr)b.Context.MkNumeral(a, b.Context.MkIntSort()), b); + } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator *(double a, ArithExpr b) + { + return b.Context.MkMul((ArithExpr)b.Context.MkNumeral(a.ToString(), b.Context.MkRealSort()), b); + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <=(ArithExpr a, ArithExpr b) + { + return a.Context.MkLe(a, b); + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <=(ArithExpr a, int b) + { + return a <= (ArithExpr)a.Context.MkNumeral(b, a.Context.MkIntSort()); + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <=(ArithExpr a, double b) + { + return a <= (ArithExpr)a.Context.MkNumeral(b.ToString(), a.Context.MkRealSort()); + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <=(int a, ArithExpr b) + { + return (ArithExpr)b.Context.MkNumeral(a, b.Context.MkIntSort()) <= b; + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <=(double a, ArithExpr b) + { + return (ArithExpr)b.Context.MkNumeral(a.ToString(), b.Context.MkRealSort()) <= b; + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <(ArithExpr a, ArithExpr b) + { + return a.Context.MkLt(a, b); + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <(ArithExpr a, int b) + { + return a < (ArithExpr)a.Context.MkNumeral(b, a.Context.MkIntSort()); + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <(ArithExpr a, double b) + { + return a < (ArithExpr)a.Context.MkNumeral(b.ToString(), a.Context.MkRealSort()); + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <(int a, ArithExpr b) + { + return (ArithExpr)b.Context.MkNumeral(a, b.Context.MkIntSort()) < b; + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator <(double a, ArithExpr b) + { + return (ArithExpr)b.Context.MkNumeral(a.ToString(), b.Context.MkRealSort()) < b; + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >=(ArithExpr a, ArithExpr b) + { + return a.Context.MkGe(a, b); + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >=(ArithExpr a, int b) + { + return a >= (ArithExpr)a.Context.MkNumeral(b, a.Context.MkIntSort()); + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >=(ArithExpr a, double b) + { + return a >= (ArithExpr)a.Context.MkNumeral(b.ToString(), a.Context.MkRealSort()); + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >=(int a, ArithExpr b) + { + return (ArithExpr)b.Context.MkNumeral(a, b.Context.MkIntSort()) >= b; + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >=(double a, ArithExpr b) + { + return (ArithExpr)b.Context.MkNumeral(a.ToString(), b.Context.MkRealSort()) >= b; + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >(ArithExpr a, ArithExpr b) + { + return a.Context.MkGt(a, b); + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >(ArithExpr a, int b) + { + return a > (ArithExpr)a.Context.MkNumeral(b, a.Context.MkIntSort()); + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >(ArithExpr a, double b) + { + return a > (ArithExpr)a.Context.MkNumeral(b.ToString(), a.Context.MkRealSort()); + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >(int a, ArithExpr b) + { + return (ArithExpr)b.Context.MkNumeral(a, b.Context.MkIntSort()) > b; + } + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >(double a, ArithExpr b) + { + return (ArithExpr)b.Context.MkNumeral(a.ToString(), b.Context.MkRealSort()) > b; + } } } From 662e43d2646f06ef2c6393a9929d8568163644df Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 23 Apr 2016 15:50:30 -0700 Subject: [PATCH 41/66] overloading support for C# expressions Signed-off-by: Nikolaj Bjorner --- src/api/dotnet/ArithExpr.cs | 107 +++++++++++++++++++++++++++++------- src/api/dotnet/BoolExpr.cs | 19 +++++++ src/api/dotnet/Context.cs | 68 +++++++++++++++++++++++ src/api/dotnet/Optimize.cs | 41 +++++++++++--- src/api/dotnet/Z3Object.cs | 19 +++++++ 5 files changed, 225 insertions(+), 29 deletions(-) diff --git a/src/api/dotnet/ArithExpr.cs b/src/api/dotnet/ArithExpr.cs index 56aa48f26..ac1e9200e 100644 --- a/src/api/dotnet/ArithExpr.cs +++ b/src/api/dotnet/ArithExpr.cs @@ -39,18 +39,84 @@ namespace Microsoft.Z3 } #endregion + #region Operators + + private static ArithExpr MkNum(ArithExpr e, int i) + { + return (ArithExpr)e.Context.MkNumeral(i, e.Context.MkIntSort()); + } + + private static ArithExpr MkNum(ArithExpr e, double d) + { + return (ArithExpr)e.Context.MkNumeral(d.ToString(), e.Context.MkRealSort()); + } + + /// Operator overloading for arithmetical divsion operator (over reals) + public static ArithExpr operator /(ArithExpr a, ArithExpr b) + { + return a.Context.MkDiv(a, b); + } + /// Operator overloading for arithmetical operator public static ArithExpr operator -(ArithExpr a, ArithExpr b) { return a.Context.MkSub(a, b); } + /// Operator overloading for arithmetical operator + public static ArithExpr operator -(ArithExpr a, int b) + { + return a.Context.MkSub(a, MkNum(a, b)); + } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator -(ArithExpr a, double b) + { + return a.Context.MkSub(a, MkNum(a, b)); + } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator -(int a, ArithExpr b) + { + return b.Context.MkSub(MkNum(b, a), b); + } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator -(double a, ArithExpr b) + { + return b.Context.MkSub(MkNum(b, a), b); + } + /// Operator overloading for arithmetical operator public static ArithExpr operator +(ArithExpr a, ArithExpr b) { return a.Context.MkAdd(a, b); } + /// Operator overloading for arithmetical operator + public static ArithExpr operator +(ArithExpr a, int b) + { + return a.Context.MkAdd(a, MkNum(a, b)); + } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator +(ArithExpr a, double b) + { + return a.Context.MkAdd(a, MkNum(a, b)); + } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator +(int a, ArithExpr b) + { + return b.Context.MkAdd(MkNum(b, a), b); + } + + /// Operator overloading for arithmetical operator + public static ArithExpr operator +(double a, ArithExpr b) + { + return b.Context.MkAdd(MkNum(b, a), b); + } + /// Operator overloading for arithmetical operator public static ArithExpr operator *(ArithExpr a, ArithExpr b) { @@ -60,25 +126,25 @@ namespace Microsoft.Z3 /// Operator overloading for arithmetical operator public static ArithExpr operator *(ArithExpr a, int b) { - return a.Context.MkMul(a, (ArithExpr)a.Context.MkNumeral(b, a.Context.MkIntSort())); + return a.Context.MkMul(a, MkNum(a, b)); } /// Operator overloading for arithmetical operator public static ArithExpr operator *(ArithExpr a, double b) { - return a.Context.MkMul(a, (ArithExpr)a.Context.MkNumeral(b.ToString(), a.Context.MkRealSort())); + return a.Context.MkMul(a, MkNum(a, b)); } /// Operator overloading for arithmetical operator public static ArithExpr operator *(int a, ArithExpr b) { - return b.Context.MkMul((ArithExpr)b.Context.MkNumeral(a, b.Context.MkIntSort()), b); + return b.Context.MkMul(MkNum(b, a), b); } /// Operator overloading for arithmetical operator public static ArithExpr operator *(double a, ArithExpr b) { - return b.Context.MkMul((ArithExpr)b.Context.MkNumeral(a.ToString(), b.Context.MkRealSort()), b); + return b.Context.MkMul(MkNum(b, a), b); } /// Operator overloading for arithmetical operator @@ -90,25 +156,25 @@ namespace Microsoft.Z3 /// Operator overloading for arithmetical operator public static BoolExpr operator <=(ArithExpr a, int b) { - return a <= (ArithExpr)a.Context.MkNumeral(b, a.Context.MkIntSort()); + return a <= MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator <=(ArithExpr a, double b) { - return a <= (ArithExpr)a.Context.MkNumeral(b.ToString(), a.Context.MkRealSort()); + return a <= MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator <=(int a, ArithExpr b) { - return (ArithExpr)b.Context.MkNumeral(a, b.Context.MkIntSort()) <= b; + return MkNum(b, a) <= b; } /// Operator overloading for arithmetical operator public static BoolExpr operator <=(double a, ArithExpr b) { - return (ArithExpr)b.Context.MkNumeral(a.ToString(), b.Context.MkRealSort()) <= b; + return MkNum(b, a) <= b; } /// Operator overloading for arithmetical operator @@ -120,25 +186,25 @@ namespace Microsoft.Z3 /// Operator overloading for arithmetical operator public static BoolExpr operator <(ArithExpr a, int b) { - return a < (ArithExpr)a.Context.MkNumeral(b, a.Context.MkIntSort()); + return a < MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator <(ArithExpr a, double b) { - return a < (ArithExpr)a.Context.MkNumeral(b.ToString(), a.Context.MkRealSort()); + return a < MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator <(int a, ArithExpr b) { - return (ArithExpr)b.Context.MkNumeral(a, b.Context.MkIntSort()) < b; + return MkNum(b, a) < b; } /// Operator overloading for arithmetical operator public static BoolExpr operator <(double a, ArithExpr b) { - return (ArithExpr)b.Context.MkNumeral(a.ToString(), b.Context.MkRealSort()) < b; + return MkNum(b, a) < b; } /// Operator overloading for arithmetical operator @@ -150,25 +216,25 @@ namespace Microsoft.Z3 /// Operator overloading for arithmetical operator public static BoolExpr operator >=(ArithExpr a, int b) { - return a >= (ArithExpr)a.Context.MkNumeral(b, a.Context.MkIntSort()); + return a >= MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator >=(ArithExpr a, double b) { - return a >= (ArithExpr)a.Context.MkNumeral(b.ToString(), a.Context.MkRealSort()); + return a >= MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator >=(int a, ArithExpr b) { - return (ArithExpr)b.Context.MkNumeral(a, b.Context.MkIntSort()) >= b; + return MkNum(b, a) >= b; } /// Operator overloading for arithmetical operator public static BoolExpr operator >=(double a, ArithExpr b) { - return (ArithExpr)b.Context.MkNumeral(a.ToString(), b.Context.MkRealSort()) >= b; + return MkNum(b, a) >= b; } /// Operator overloading for arithmetical operator @@ -180,25 +246,26 @@ namespace Microsoft.Z3 /// Operator overloading for arithmetical operator public static BoolExpr operator >(ArithExpr a, int b) { - return a > (ArithExpr)a.Context.MkNumeral(b, a.Context.MkIntSort()); + return a > MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator >(ArithExpr a, double b) { - return a > (ArithExpr)a.Context.MkNumeral(b.ToString(), a.Context.MkRealSort()); + return a > MkNum(a, b); } /// Operator overloading for arithmetical operator public static BoolExpr operator >(int a, ArithExpr b) { - return (ArithExpr)b.Context.MkNumeral(a, b.Context.MkIntSort()) > b; + return MkNum(b, a) > b; } /// Operator overloading for arithmetical operator public static BoolExpr operator >(double a, ArithExpr b) { - return (ArithExpr)b.Context.MkNumeral(a.ToString(), b.Context.MkRealSort()) > b; + return MkNum(b, a) > b; } + #endregion } } diff --git a/src/api/dotnet/BoolExpr.cs b/src/api/dotnet/BoolExpr.cs index a9a15e4dc..4d5a43074 100644 --- a/src/api/dotnet/BoolExpr.cs +++ b/src/api/dotnet/BoolExpr.cs @@ -34,5 +34,24 @@ namespace Microsoft.Z3 /// Constructor for BoolExpr internal BoolExpr(Context ctx, IntPtr obj) : base(ctx, obj) { Contract.Requires(ctx != null); } #endregion + + #region Operators + /// + /// Disjunction of Boolean expressions + /// + public static BoolExpr operator|(BoolExpr a, BoolExpr b) + { + return a.Context.MkOr(a, b); + } + + /// + /// Conjunction of Boolean expressions + /// + public static BoolExpr operator &(BoolExpr a, BoolExpr b) + { + return a.Context.MkAnd(a, b); + } + + #endregion } } diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs index a97036897..5bff8960a 100644 --- a/src/api/dotnet/Context.cs +++ b/src/api/dotnet/Context.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; using System.Diagnostics.Contracts; +using System.Linq; namespace Microsoft.Z3 { @@ -814,6 +815,20 @@ namespace Microsoft.Z3 return Expr.Create(this, f, args); } + /// + /// Create a new function application. + /// + public Expr MkApp(FuncDecl f, IEnumerable args) + { + Contract.Requires(f != null); + Contract.Requires(args == null || Contract.ForAll(args, a => a != null)); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(f); + CheckContextMatch(args); + return Expr.Create(this, f, args.ToArray()); + } + #region Propositional /// /// The true Term. @@ -959,6 +974,18 @@ namespace Microsoft.Z3 return new BoolExpr(this, Native.Z3_mk_and(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } + /// + /// Create an expression representing t[0] and t[1] and .... + /// + public BoolExpr MkAnd(IEnumerable t) + { + Contract.Requires(t != null); + Contract.Requires(Contract.ForAll(t, a => a != null)); + Contract.Ensures(Contract.Result() != null); + CheckContextMatch(t); + return new BoolExpr(this, Native.Z3_mk_and(nCtx, (uint)t.Count(), AST.EnumToNative(t))); + } + /// /// Create an expression representing t[0] or t[1] or .... /// @@ -973,6 +1000,19 @@ namespace Microsoft.Z3 } + /// + /// Create an expression representing t[0] or t[1] or .... + /// + public BoolExpr MkOr(IEnumerable t) + { + Contract.Requires(t != null); + Contract.Requires(Contract.ForAll(t, a => a != null)); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(t); + return new BoolExpr(this, Native.Z3_mk_or(nCtx, (uint)t.Count(), AST.EnumToNative(t))); + } + #endregion #region Arithmetic @@ -989,6 +1029,19 @@ namespace Microsoft.Z3 return (ArithExpr)Expr.Create(this, Native.Z3_mk_add(nCtx, (uint)t.Length, AST.ArrayToNative(t))); } + /// + /// Create an expression representing t[0] + t[1] + .... + /// + public ArithExpr MkAdd(IEnumerable t) + { + Contract.Requires(t != null); + Contract.Requires(Contract.ForAll(t, a => a != null)); + Contract.Ensures(Contract.Result() != null); + + CheckContextMatch(t); + return (ArithExpr)Expr.Create(this, Native.Z3_mk_add(nCtx, (uint)t.Count(), AST.EnumToNative(t))); + } + /// /// Create an expression representing t[0] * t[1] * .... /// @@ -4743,6 +4796,21 @@ namespace Microsoft.Z3 } } + [Pure] + internal void CheckContextMatch(IEnumerable arr) + { + Contract.Requires(arr == null || Contract.ForAll(arr, a => a != null)); + + if (arr != null) + { + foreach (Z3Object a in arr) + { + Contract.Assert(a != null); // It was an assume, now we added the precondition, and we made it into an assert + CheckContextMatch(a); + } + } + } + [ContractInvariantMethod] private void ObjectInvariant() { diff --git a/src/api/dotnet/Optimize.cs b/src/api/dotnet/Optimize.cs index 501603668..304e3e86f 100644 --- a/src/api/dotnet/Optimize.cs +++ b/src/api/dotnet/Optimize.cs @@ -66,6 +66,38 @@ namespace Microsoft.Z3 /// Assert a constraint (or multiple) into the optimize solver. /// public void Assert(params BoolExpr[] constraints) + { + AddConstraints(constraints); + } + + /// + /// Assert a constraint (or multiple) into the optimize solver. + /// + public void Assert(IEnumerable constraints) + { + AddConstraints(constraints); + } + + /// + /// Alias for Assert. + /// + public void Add(params BoolExpr[] constraints) + { + AddConstraints(constraints); + } + + /// + /// Alias for Assert. + /// + public void Add(IEnumerable constraints) + { + AddConstraints(constraints); + } + + /// + /// Assert a constraint (or multiple) into the optimize solver. + /// + private void AddConstraints(IEnumerable constraints) { Contract.Requires(constraints != null); Contract.Requires(Contract.ForAll(constraints, c => c != null)); @@ -76,15 +108,6 @@ namespace Microsoft.Z3 Native.Z3_optimize_assert(Context.nCtx, NativeObject, a.NativeObject); } } - - /// - /// Alias for Assert. - /// - public void Add(params BoolExpr[] constraints) - { - Assert(constraints); - } - /// /// Handle to objectives returned by objective functions. /// diff --git a/src/api/dotnet/Z3Object.cs b/src/api/dotnet/Z3Object.cs index 8e474041a..cd6803341 100644 --- a/src/api/dotnet/Z3Object.cs +++ b/src/api/dotnet/Z3Object.cs @@ -20,6 +20,8 @@ Notes: using System; using System.Diagnostics.Contracts; using System.Threading; +using System.Collections.Generic; +using System.Linq; namespace Microsoft.Z3 { @@ -135,6 +137,23 @@ namespace Microsoft.Z3 return an; } + [Pure] + internal static IntPtr[] EnumToNative(IEnumerable a) + { + Contract.Ensures(a == null || Contract.Result() != null); + Contract.Ensures(a == null || Contract.Result().Length == a.Count()); + + if (a == null) return null; + IntPtr[] an = new IntPtr[a.Count()]; + int i = 0; + foreach (var ai in a) + { + if (ai != null) an[i] = ai.NativeObject; + ++i; + } + return an; + } + [Pure] internal static uint ArrayLength(Z3Object[] a) { From 643a87cb5b309b30ff1037d2f4cb6e186a38f903 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 23 Apr 2016 22:03:27 -0700 Subject: [PATCH 42/66] overloading support for C# expressions Signed-off-by: Nikolaj Bjorner --- src/api/dotnet/ArithExpr.cs | 203 +++++++++--------------------------- src/api/dotnet/BoolExpr.cs | 20 ++-- 2 files changed, 60 insertions(+), 163 deletions(-) diff --git a/src/api/dotnet/ArithExpr.cs b/src/api/dotnet/ArithExpr.cs index ac1e9200e..58d160900 100644 --- a/src/api/dotnet/ArithExpr.cs +++ b/src/api/dotnet/ArithExpr.cs @@ -41,231 +41,130 @@ namespace Microsoft.Z3 #region Operators - private static ArithExpr MkNum(ArithExpr e, int i) - { - return (ArithExpr)e.Context.MkNumeral(i, e.Context.MkIntSort()); - } + private static ArithExpr MkNum(ArithExpr e, int i) => (ArithExpr)e.Context.MkNumeral(i, e.Context.MkIntSort()); - private static ArithExpr MkNum(ArithExpr e, double d) - { - return (ArithExpr)e.Context.MkNumeral(d.ToString(), e.Context.MkRealSort()); - } + private static ArithExpr MkNum(ArithExpr e, double d) => (ArithExpr)e.Context.MkNumeral(d.ToString(), e.Context.MkRealSort()); /// Operator overloading for arithmetical divsion operator (over reals) - public static ArithExpr operator /(ArithExpr a, ArithExpr b) - { - return a.Context.MkDiv(a, b); - } + public static ArithExpr operator /(ArithExpr a, ArithExpr b) => a.Context.MkDiv(a, b); /// Operator overloading for arithmetical operator - public static ArithExpr operator -(ArithExpr a, ArithExpr b) - { - return a.Context.MkSub(a, b); - } + public static ArithExpr operator /(ArithExpr a, int b) => a / MkNum(a, b); /// Operator overloading for arithmetical operator - public static ArithExpr operator -(ArithExpr a, int b) - { - return a.Context.MkSub(a, MkNum(a, b)); - } + public static ArithExpr operator /(ArithExpr a, double b) => a / MkNum(a, b); /// Operator overloading for arithmetical operator - public static ArithExpr operator -(ArithExpr a, double b) - { - return a.Context.MkSub(a, MkNum(a, b)); - } + public static ArithExpr operator /(int a, ArithExpr b) => MkNum(b, a) / b; /// Operator overloading for arithmetical operator - public static ArithExpr operator -(int a, ArithExpr b) - { - return b.Context.MkSub(MkNum(b, a), b); - } + public static ArithExpr operator /(double a, ArithExpr b) => MkNum(b, a) / b; /// Operator overloading for arithmetical operator - public static ArithExpr operator -(double a, ArithExpr b) - { - return b.Context.MkSub(MkNum(b, a), b); - } + public static ArithExpr operator -(ArithExpr a, ArithExpr b) => a.Context.MkSub(a, b); /// Operator overloading for arithmetical operator - public static ArithExpr operator +(ArithExpr a, ArithExpr b) - { - return a.Context.MkAdd(a, b); - } + public static ArithExpr operator -(ArithExpr a, int b) => a - MkNum(a, b); /// Operator overloading for arithmetical operator - public static ArithExpr operator +(ArithExpr a, int b) - { - return a.Context.MkAdd(a, MkNum(a, b)); - } + public static ArithExpr operator -(ArithExpr a, double b) => a - MkNum(a, b); /// Operator overloading for arithmetical operator - public static ArithExpr operator +(ArithExpr a, double b) - { - return a.Context.MkAdd(a, MkNum(a, b)); - } + public static ArithExpr operator -(int a, ArithExpr b) => MkNum(b, a) - b; /// Operator overloading for arithmetical operator - public static ArithExpr operator +(int a, ArithExpr b) - { - return b.Context.MkAdd(MkNum(b, a), b); - } + public static ArithExpr operator -(double a, ArithExpr b) => MkNum(b, a) - b; /// Operator overloading for arithmetical operator - public static ArithExpr operator +(double a, ArithExpr b) - { - return b.Context.MkAdd(MkNum(b, a), b); - } + public static ArithExpr operator +(ArithExpr a, ArithExpr b) => a.Context.MkAdd(a, b); /// Operator overloading for arithmetical operator - public static ArithExpr operator *(ArithExpr a, ArithExpr b) - { - return a.Context.MkMul(a, b); - } + public static ArithExpr operator +(ArithExpr a, int b) => a + MkNum(a, b); /// Operator overloading for arithmetical operator - public static ArithExpr operator *(ArithExpr a, int b) - { - return a.Context.MkMul(a, MkNum(a, b)); - } + public static ArithExpr operator +(ArithExpr a, double b) => a + MkNum(a, b); + + /// Operator overloading for arithmetical operator + public static ArithExpr operator +(int a, ArithExpr b) => MkNum(b, a) + b; /// Operator overloading for arithmetical operator - public static ArithExpr operator *(ArithExpr a, double b) - { - return a.Context.MkMul(a, MkNum(a, b)); - } + public static ArithExpr operator +(double a, ArithExpr b) => MkNum(b, a) + b; /// Operator overloading for arithmetical operator - public static ArithExpr operator *(int a, ArithExpr b) - { - return b.Context.MkMul(MkNum(b, a), b); - } + public static ArithExpr operator *(ArithExpr a, ArithExpr b) => a.Context.MkMul(a, b); /// Operator overloading for arithmetical operator - public static ArithExpr operator *(double a, ArithExpr b) - { - return b.Context.MkMul(MkNum(b, a), b); - } + public static ArithExpr operator *(ArithExpr a, int b) => a * MkNum(a, b); /// Operator overloading for arithmetical operator - public static BoolExpr operator <=(ArithExpr a, ArithExpr b) - { - return a.Context.MkLe(a, b); - } + public static ArithExpr operator *(ArithExpr a, double b) => a * MkNum(a, b); /// Operator overloading for arithmetical operator - public static BoolExpr operator <=(ArithExpr a, int b) - { - return a <= MkNum(a, b); - } + public static ArithExpr operator *(int a, ArithExpr b) => MkNum(b, a) * b; /// Operator overloading for arithmetical operator - public static BoolExpr operator <=(ArithExpr a, double b) - { - return a <= MkNum(a, b); - } + public static ArithExpr operator *(double a, ArithExpr b) => MkNum(b, a) * b; /// Operator overloading for arithmetical operator - public static BoolExpr operator <=(int a, ArithExpr b) - { - return MkNum(b, a) <= b; - } + public static BoolExpr operator <=(ArithExpr a, ArithExpr b) => a.Context.MkLe(a, b); /// Operator overloading for arithmetical operator - public static BoolExpr operator <=(double a, ArithExpr b) - { - return MkNum(b, a) <= b; - } + public static BoolExpr operator <=(ArithExpr a, int b) => a <= MkNum(a, b); /// Operator overloading for arithmetical operator - public static BoolExpr operator <(ArithExpr a, ArithExpr b) - { - return a.Context.MkLt(a, b); - } + public static BoolExpr operator <=(ArithExpr a, double b) => a <= MkNum(a, b); /// Operator overloading for arithmetical operator - public static BoolExpr operator <(ArithExpr a, int b) - { - return a < MkNum(a, b); - } + public static BoolExpr operator <=(int a, ArithExpr b) => MkNum(b, a) <= b; /// Operator overloading for arithmetical operator - public static BoolExpr operator <(ArithExpr a, double b) - { - return a < MkNum(a, b); - } + public static BoolExpr operator <=(double a, ArithExpr b) => MkNum(b, a) <= b; /// Operator overloading for arithmetical operator - public static BoolExpr operator <(int a, ArithExpr b) - { - return MkNum(b, a) < b; - } + public static BoolExpr operator <(ArithExpr a, ArithExpr b) => a.Context.MkLt(a, b); /// Operator overloading for arithmetical operator - public static BoolExpr operator <(double a, ArithExpr b) - { - return MkNum(b, a) < b; - } + public static BoolExpr operator <(ArithExpr a, int b) => a < MkNum(a, b); /// Operator overloading for arithmetical operator - public static BoolExpr operator >=(ArithExpr a, ArithExpr b) - { - return a.Context.MkGe(a, b); - } + public static BoolExpr operator <(ArithExpr a, double b) => a < MkNum(a, b); /// Operator overloading for arithmetical operator - public static BoolExpr operator >=(ArithExpr a, int b) - { - return a >= MkNum(a, b); - } + public static BoolExpr operator <(int a, ArithExpr b) => MkNum(b, a) < b; /// Operator overloading for arithmetical operator - public static BoolExpr operator >=(ArithExpr a, double b) - { - return a >= MkNum(a, b); - } + public static BoolExpr operator <(double a, ArithExpr b) => MkNum(b, a) < b; /// Operator overloading for arithmetical operator - public static BoolExpr operator >=(int a, ArithExpr b) - { - return MkNum(b, a) >= b; - } + public static BoolExpr operator >(ArithExpr a, ArithExpr b) => a.Context.MkGt(a, b); /// Operator overloading for arithmetical operator - public static BoolExpr operator >=(double a, ArithExpr b) - { - return MkNum(b, a) >= b; - } + public static BoolExpr operator >(ArithExpr a, int b) => a > MkNum(a, b); /// Operator overloading for arithmetical operator - public static BoolExpr operator >(ArithExpr a, ArithExpr b) - { - return a.Context.MkGt(a, b); - } + public static BoolExpr operator >(ArithExpr a, double b) => a > MkNum(a, b); /// Operator overloading for arithmetical operator - public static BoolExpr operator >(ArithExpr a, int b) - { - return a > MkNum(a, b); - } + public static BoolExpr operator >(int a, ArithExpr b) => MkNum(b, a) > b; /// Operator overloading for arithmetical operator - public static BoolExpr operator >(ArithExpr a, double b) - { - return a > MkNum(a, b); - } + public static BoolExpr operator >(double a, ArithExpr b) => MkNum(b, a) > b; /// Operator overloading for arithmetical operator - public static BoolExpr operator >(int a, ArithExpr b) - { - return MkNum(b, a) > b; - } + public static BoolExpr operator >=(ArithExpr a, ArithExpr b) => a.Context.MkGe(a, b); /// Operator overloading for arithmetical operator - public static BoolExpr operator >(double a, ArithExpr b) - { - return MkNum(b, a) > b; - } + public static BoolExpr operator >=(ArithExpr a, int b) => a >= MkNum(a, b); + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >=(ArithExpr a, double b) => a >= MkNum(a, b); + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >=(int a, ArithExpr b) => MkNum(b, a) >= b; + + /// Operator overloading for arithmetical operator + public static BoolExpr operator >=(double a, ArithExpr b) => MkNum(b, a) >= b; + #endregion } } diff --git a/src/api/dotnet/BoolExpr.cs b/src/api/dotnet/BoolExpr.cs index 4d5a43074..5bac65243 100644 --- a/src/api/dotnet/BoolExpr.cs +++ b/src/api/dotnet/BoolExpr.cs @@ -36,21 +36,19 @@ namespace Microsoft.Z3 #endregion #region Operators - /// - /// Disjunction of Boolean expressions - /// - public static BoolExpr operator|(BoolExpr a, BoolExpr b) - { - return a.Context.MkOr(a, b); - } + /// Disjunction of Boolean expressions + public static BoolExpr operator|(BoolExpr a, BoolExpr b) => a.Context.MkOr(a, b); /// /// Conjunction of Boolean expressions /// - public static BoolExpr operator &(BoolExpr a, BoolExpr b) - { - return a.Context.MkAnd(a, b); - } + public static BoolExpr operator &(BoolExpr a, BoolExpr b) => a.Context.MkAnd(a, b); + + /// Xor of Boolean expressions + public static BoolExpr operator ^(BoolExpr a, BoolExpr b) => a.Context.MkXor(a, b); + + /// Negation + public static BoolExpr operator !(BoolExpr a) => a.Context.MkNot(a); #endregion } From 3131f29816251d8335387e6813c604af508918f3 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Sun, 24 Apr 2016 15:11:03 +0100 Subject: [PATCH 43/66] whitespace --- src/util/hwf.cpp | 76 ++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index 74a03b620..8c184b8d2 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -38,13 +38,13 @@ Revision History: // Note: // Which FPU will be used is determined by compiler settings. On x64 it's always SSE2, -// on x86 we have to chose SSE2 by enabling /arch:SSE2 (otherwise the x87 FPU will be used). +// on x86 we have to chose SSE2 by enabling /arch:SSE2 (otherwise the x87 FPU will be used). // Christoph has decided that we don't want to use the x87; this makes everything a lot easier. // For SSE2, it is best to use compiler intrinsics because this makes it completely // clear to the compiler what instructions should be used. E.g., for sqrt(), the Windows compiler selects -// the x87 FPU, even when /arch:SSE2 is on. +// the x87 FPU, even when /arch:SSE2 is on. // Luckily, these are kind of standardized, at least for Windows/Linux/OSX. #ifdef __clang__ #undef USE_INTRINSICS @@ -56,19 +56,19 @@ Revision History: hwf_manager::hwf_manager() : m_mpz_manager(m_mpq_manager) -{ +{ #ifdef _WINDOWS #if defined(_AMD64_) || defined(_M_IA64) - // Precision control is not supported on x64. + // Precision control is not supported on x64. // See: http://msdn.microsoft.com/en-us/library/e9b52ceh(VS.110).aspx - // CMW: I think this is okay though, the compiler will chose the right instructions + // CMW: I think this is okay though, the compiler will chose the right instructions // (the x64/SSE2 FPU has separate instructions for different precisions). #else // Setting the precision should only be required on the x87, but it won't hurt to do it anyways. // _PC_53 means double precision (53 significand bits). For extended precision use _PC_64. #ifndef USE_INTRINSICS - __control87_2(_PC_53, _MCW_PC, &x86_state, &sse2_state); + __control87_2(_PC_53, _MCW_PC, &x86_state, &sse2_state); #endif #endif #else @@ -78,7 +78,7 @@ hwf_manager::hwf_manager() : // We only set the precision of the FPU here in the constructor. At the moment, there are no // other parts of the code that could overwrite this, and Windows takes care of context switches. - // CMW: I'm not sure what happens on CPUs with hyper-threading (since the FPU is shared). + // CMW: I'm not sure what happens on CPUs with hyper-threading (since the FPU is shared). // I have yet to discover whether Linux and OSX save the FPU state when switching context. // As long as we stick to using the SSE2 FPU though, there shouldn't be any problems with respect // to the precision (not sure about the rounding modes though). @@ -104,7 +104,7 @@ void hwf_manager::set(hwf & o, double value) { o.value = value; } -void hwf_manager::set(hwf & o, float value) { +void hwf_manager::set(hwf & o, float value) { o.value = (double)value; } @@ -113,7 +113,7 @@ void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & value) { o.value = m_mpq_manager.get_double(value); } -void hwf_manager::set(hwf & o, mpf_rounding_mode rm, char const * value) { +void hwf_manager::set(hwf & o, mpf_rounding_mode rm, char const * value) { // We expect [i].[f]P[e], where P means that the exponent is interpreted as 2^e instead of 10^e. std::string v(value); @@ -123,17 +123,17 @@ void hwf_manager::set(hwf & o, mpf_rounding_mode rm, char const * value) { std::string f, e; f = (e_pos != std::string::npos) ? v.substr(0, e_pos) : v; - e = (e_pos != std::string::npos) ? v.substr(e_pos+1) : "0"; + e = (e_pos != std::string::npos) ? v.substr(e_pos+1) : "0"; - TRACE("mpf_dbg", tout << " f = " << f << " e = " << e << std::endl;); + TRACE("mpf_dbg", tout << " f = " << f << " e = " << e << std::endl;); - mpq q; + mpq q; m_mpq_manager.set(q, f.c_str()); mpz ex; m_mpz_manager.set(ex, e.c_str()); - set(o, rm, q, ex); + set(o, rm, q, ex); TRACE("mpf_dbg", tout << "set: res = " << to_string(o) << std::endl;); } @@ -142,7 +142,7 @@ void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & significand, mp // Assumption: this represents significand * 2^exponent. set_rounding_mode(rm); - mpq sig; + mpq sig; m_mpq_manager.set(sig, significand); int64 exp = m_mpz_manager.get_int64(exponent); @@ -150,7 +150,7 @@ void hwf_manager::set(hwf & o, mpf_rounding_mode rm, mpq const & significand, mp o.value = 0.0; else { - while (m_mpq_manager.lt(sig, 1)) + while (m_mpq_manager.lt(sig, 1)) { m_mpq_manager.mul(sig, 2, sig); exp--; @@ -176,7 +176,7 @@ void hwf_manager::set(hwf & o, hwf const & x) { o.value = x.value; } -void hwf_manager::abs(hwf & o) { +void hwf_manager::abs(hwf & o) { o.value = fabs(o.value); } @@ -244,14 +244,14 @@ void hwf_manager::mul(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & // On the x86 FPU (x87), we use custom assembly routines because // the code generated for x*y and x/y suffers from the double // rounding on underflow problem. The scaling trick is described - // in Roger Golliver: `Efficiently producing default orthogonal IEEE + // in Roger Golliver: `Efficiently producing default orthogonal IEEE // double results using extended IEEE hardware', see // http://www.open-std.org/JTC1/SC22/JSG/docs/m3/docs/jsgn326.pdf // CMW: Tthis is not really needed if we use only the SSE2 FPU, // it shouldn't hurt the performance too much though. static const int const1 = -DBL_SCALE; - static const int const2 = +DBL_SCALE; + static const int const2 = +DBL_SCALE; double xv = x.value; double yv = y.value; double & ov = o.value; @@ -266,14 +266,14 @@ void hwf_manager::mul(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & fxch st(1); fscale; fstp ov; - } + } #endif } void hwf_manager::div(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & o) { set_rounding_mode(rm); #ifdef USE_INTRINSICS - _mm_store_sd(&o.value, _mm_div_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); + _mm_store_sd(&o.value, _mm_div_sd(_mm_set_sd(x.value), _mm_set_sd(y.value))); #else o.value = x.value / y.value; #endif @@ -306,18 +306,18 @@ void hwf_manager::div(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf & #endif void hwf_manager::fma(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf const &z, hwf & o) { - // CMW: fused_mul_add is not available on most CPUs. As of 2012, only Itanium, + // CMW: fused_mul_add is not available on most CPUs. As of 2012, only Itanium, // Intel Sandybridge and AMD Bulldozers support that (via AVX). set_rounding_mode(rm); #ifdef _M_IA64 - // IA64 (Itanium) will do it, if contractions are on. + // IA64 (Itanium) will do it, if contractions are on. o.value = x.value * y.value + z.value; #else -#if defined(_WINDOWS) +#if defined(_WINDOWS) #if _MSC_VER >= 1800 - o.value = ::fma(x.value, y.value, z.value); + o.value = ::fma(x.value, y.value, z.value); #else // Windows, older than VS 2013 #ifdef USE_INTRINSICS _mm_store_sd(&o.value, _mm_fmadd_sd(_mm_set_sd(x.value), _mm_set_sd(y.value), _mm_set_sd(z.value))); @@ -351,7 +351,7 @@ void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o // CMW: modf is not the right function here. // modf(x.value, &o.value); - // According to the Intel Architecture manual, the x87-instrunction FRNDINT is the + // According to the Intel Architecture manual, the x87-instrunction FRNDINT is the // same in 32-bit and 64-bit mode. The _mm_round_* intrinsics are SSE4 extensions. #ifdef _WINDOWS #ifdef USE_INTRINSICS @@ -389,7 +389,7 @@ void hwf_manager::rem(hwf const & x, hwf const & y, hwf & o) { o.value = x.value/y.value; // NaN else if (is_inf(y)) o.value = x.value; - else + else o.value = fmod(x.value, y.value); // Here is an x87 alternative if the above makes problems; this may also be faster. @@ -423,7 +423,7 @@ void hwf_manager::maximum(hwf const & x, hwf const & y, hwf & o) { o.value = x.value; else if (lt(x, y)) o.value = y.value; - else + else o.value = x.value; #endif } @@ -439,12 +439,12 @@ void hwf_manager::minimum(hwf const & x, hwf const & y, hwf & o) { o.value = x.value; else if (lt(x, y)) o.value = x.value; - else + else o.value = y.value; #endif } -std::string hwf_manager::to_string(hwf const & x) { +std::string hwf_manager::to_string(hwf const & x) { std::stringstream ss(""); ss << std::scientific << x.value; return ss.str(); @@ -488,9 +488,9 @@ void hwf_manager::to_rational(hwf const & x, unsynch_mpq_manager & qm, mpq & o) int e = exp(x); if (e >= 0) qm.mul2k(n, (unsigned)e); - else + else qm.mul2k(d, (unsigned)-e); - qm.set(o, n, d); + qm.set(o, n, d); } bool hwf_manager::is_zero(hwf const & x) { @@ -559,13 +559,13 @@ bool hwf_manager::is_denormal(hwf const & x) { (t & 0x000FFFFFFFFFFFFFull) != 0x0); } -bool hwf_manager::is_regular(hwf const & x) { +bool hwf_manager::is_regular(hwf const & x) { // Everything that doesn't have the top-exponent is considered regular. // Note that +-0.0 and denormal numbers have exponent==0; these are regular. - // All normal numbers are also regular. What remains is +-Inf and NaN, they are + // All normal numbers are also regular. What remains is +-Inf and NaN, they are // not regular and they are the only numbers that have exponent 7FF. uint64 e = RAW(x.value) & 0x7FF0000000000000ull; // the exponent - return (e != 0x7FF0000000000000ull); + return (e != 0x7FF0000000000000ull); } bool hwf_manager::is_int(hwf const & x) { @@ -596,8 +596,8 @@ void hwf_manager::mk_pzero(hwf & o) { } void hwf_manager::mk_zero(bool sign, hwf & o) { - if (sign) - mk_nzero(o); + if (sign) + mk_nzero(o); else mk_pzero(o); } @@ -627,7 +627,7 @@ void hwf_manager::mk_ninf(hwf & o) { #ifdef USE_INTRINSICS #define SETRM(RM) _MM_SET_ROUNDING_MODE(RM) #else -#define SETRM(RM) _controlfp_s(&sse2_state, RM, _MCW_RC); +#define SETRM(RM) _controlfp_s(&sse2_state, RM, _MCW_RC); #endif #else #ifdef USE_INTRINSICS @@ -691,7 +691,7 @@ void hwf_manager::set_rounding_mode(mpf_rounding_mode rm) #endif #else // OSX/Linux switch (rm) { - case MPF_ROUND_NEAREST_TEVEN: + case MPF_ROUND_NEAREST_TEVEN: SETRM(FE_TONEAREST); break; case MPF_ROUND_TOWARD_POSITIVE: From 952e3afb90281f6cc8f8a21c103fd6eae2f3c195 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Sun, 24 Apr 2016 15:11:24 +0100 Subject: [PATCH 44/66] bugfix for hwf_manager::rem --- src/util/hwf.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index 8c184b8d2..a990e6e6d 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -383,17 +383,10 @@ void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o } void hwf_manager::rem(hwf const & x, hwf const & y, hwf & o) { - // The built-in fmod() works, except for the special numbers. + o.value = remainder(x.value, y.value); - if (is_inf(x) && is_inf(y)) - o.value = x.value/y.value; // NaN - else if (is_inf(y)) - o.value = x.value; - else - o.value = fmod(x.value, y.value); - - // Here is an x87 alternative if the above makes problems; this may also be faster. #if 0 + // Here is an x87 alternative if the above makes problems; this may also be faster. double xv = x.value; double yv = y.value; double & ov = o.value; From be424d9cbbf1d9d0a212ca744c15199291945189 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Sun, 24 Apr 2016 15:14:16 +0100 Subject: [PATCH 45/66] Bugfixes for fp.roundToIntegral and fp.rem. Relates to #561 --- src/ast/fpa/fpa2bv_converter.cpp | 461 +++++++++++++++++-------------- src/ast/fpa/fpa2bv_converter.h | 19 +- src/util/mpf.cpp | 56 +--- 3 files changed, 282 insertions(+), 254 deletions(-) diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index d41b1809f..aefa6294a 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -335,10 +335,13 @@ void fpa2bv_converter::mk_rm_const(func_decl * f, expr_ref & result) { } void fpa2bv_converter::mk_pinf(func_decl * f, expr_ref & result) { - sort * srt = f->get_range(); - SASSERT(is_float(srt)); - unsigned sbits = m_util.get_sbits(srt); - unsigned ebits = m_util.get_ebits(srt); + mk_pinf(f->get_range(), result); +} + +void fpa2bv_converter::mk_pinf(sort * s, expr_ref & result) { + SASSERT(is_float(s)); + unsigned sbits = m_util.get_sbits(s); + unsigned ebits = m_util.get_ebits(s); expr_ref top_exp(m); mk_top_exp(ebits, top_exp); mk_fp(m_bv_util.mk_numeral(0, 1), @@ -348,10 +351,13 @@ void fpa2bv_converter::mk_pinf(func_decl * f, expr_ref & result) { } void fpa2bv_converter::mk_ninf(func_decl * f, expr_ref & result) { - sort * srt = f->get_range(); - SASSERT(is_float(srt)); - unsigned sbits = m_util.get_sbits(srt); - unsigned ebits = m_util.get_ebits(srt); + mk_ninf(f->get_range(), result); +} + +void fpa2bv_converter::mk_ninf(sort * s, expr_ref & result) { + SASSERT(is_float(s)); + unsigned sbits = m_util.get_sbits(s); + unsigned ebits = m_util.get_ebits(s); expr_ref top_exp(m); mk_top_exp(ebits, top_exp); mk_fp(m_bv_util.mk_numeral(1, 1), @@ -361,23 +367,29 @@ void fpa2bv_converter::mk_ninf(func_decl * f, expr_ref & result) { } void fpa2bv_converter::mk_nan(func_decl * f, expr_ref & result) { - sort * srt = f->get_range(); - SASSERT(is_float(srt)); - unsigned sbits = m_util.get_sbits(srt); - unsigned ebits = m_util.get_ebits(srt); + mk_nan(f->get_range(), result); +} + +void fpa2bv_converter::mk_nan(sort * s, expr_ref & result) { + SASSERT(is_float(s)); + unsigned sbits = m_util.get_sbits(s); + unsigned ebits = m_util.get_ebits(s); expr_ref top_exp(m); mk_top_exp(ebits, top_exp); mk_fp(m_bv_util.mk_numeral(0, 1), top_exp, - m_bv_util.mk_numeral(1, sbits-1), + m_bv_util.mk_numeral(1, sbits - 1), result); } -void fpa2bv_converter::mk_nzero(func_decl *f, expr_ref & result) { - sort * srt = f->get_range(); - SASSERT(is_float(srt)); - unsigned sbits = m_util.get_sbits(srt); - unsigned ebits = m_util.get_ebits(srt); +void fpa2bv_converter::mk_nzero(func_decl * f, expr_ref & result) { + mk_nzero(f->get_range(), result); +} + +void fpa2bv_converter::mk_nzero(sort * s, expr_ref & result) { + SASSERT(is_float(s)); + unsigned sbits = m_util.get_sbits(s); + unsigned ebits = m_util.get_ebits(s); expr_ref bot_exp(m); mk_bot_exp(ebits, bot_exp); mk_fp(m_bv_util.mk_numeral(1, 1), @@ -386,11 +398,14 @@ void fpa2bv_converter::mk_nzero(func_decl *f, expr_ref & result) { result); } -void fpa2bv_converter::mk_pzero(func_decl *f, expr_ref & result) { - sort * srt = f->get_range(); - SASSERT(is_float(srt)); - unsigned sbits = m_util.get_sbits(srt); - unsigned ebits = m_util.get_ebits(srt); +void fpa2bv_converter::mk_pzero(func_decl * f, expr_ref & result) { + mk_pzero(f->get_range(), result); +} + +void fpa2bv_converter::mk_pzero(sort * s, expr_ref & result) { + SASSERT(is_float(s)); + unsigned sbits = m_util.get_sbits(s); + unsigned ebits = m_util.get_ebits(s); expr_ref bot_exp(m); mk_bot_exp(ebits, bot_exp); mk_fp(m_bv_util.mk_numeral(0, 1), @@ -399,11 +414,22 @@ void fpa2bv_converter::mk_pzero(func_decl *f, expr_ref & result) { result); } -void fpa2bv_converter::mk_one(func_decl *f, expr_ref sign, expr_ref & result) { - sort * srt = f->get_range(); - SASSERT(is_float(srt)); - unsigned sbits = m_util.get_sbits(srt); - unsigned ebits = m_util.get_ebits(srt); +void fpa2bv_converter::mk_zero(sort * s, expr_ref & sgn, expr_ref & result) { + expr_ref is_pos(m), pzero(m), nzero(m); + is_pos = m.mk_eq(sgn, m_bv_util.mk_numeral(0, 1)); + mk_pzero(s, pzero); + mk_nzero(s, nzero); + mk_ite(is_pos, pzero, nzero, result); +} + +void fpa2bv_converter::mk_one(func_decl * f, expr_ref & sign, expr_ref & result) { + mk_one(f->get_range(), sign, result); +} + +void fpa2bv_converter::mk_one(sort * s, expr_ref & sign, expr_ref & result) { + SASSERT(is_float(s)); + unsigned sbits = m_util.get_sbits(s); + unsigned ebits = m_util.get_ebits(s); mk_fp(sign, m_bv_util.mk_numeral(fu().fm().m_powers2.m1(ebits-1), ebits), m_bv_util.mk_numeral(0, sbits-1), @@ -524,15 +550,18 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, SASSERT(num == 3); SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); - expr_ref bv_rm(m), x(m), y(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m), y(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; y = args[2]; + mk_add(f->get_range(), rm, x, y, result); +} +void fpa2bv_converter::mk_add(sort * s, expr_ref & rm, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref nan(m), nzero(m), pzero(m); - mk_nan(f, nan); - mk_nzero(f, nzero); - mk_pzero(f, pzero); + mk_nan(s, nan); + mk_nzero(s, nzero); + mk_pzero(s, pzero); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_neg(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_neg(m), y_is_inf(m); @@ -582,7 +611,7 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, m_simp.mk_and(x_is_zero, y_is_zero, c4); m_simp.mk_and(x_is_neg, y_is_neg, signs_and); m_simp.mk_xor(x_is_neg, y_is_neg, signs_xor); - mk_is_rm(bv_rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); m_simp.mk_and(rm_is_to_neg, signs_xor, rm_and_xor); m_simp.mk_or(signs_and, rm_and_xor, neg_cond); mk_ite(neg_cond, nzero, pzero, v4); @@ -595,9 +624,9 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, c6 = y_is_zero; v6 = x; - //// Actual addition. - unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); + // Actual addition. + unsigned ebits = m_util.get_ebits(s); + unsigned sbits = m_util.get_sbits(s); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, false); @@ -638,7 +667,7 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, mk_ite(rm_is_to_neg, nzero, pzero, zero_case); expr_ref rounded(m); - round(f->get_range(), bv_rm, res_sgn, res_sig, res_exp, rounded); + round(s, rm, res_sgn, res_sig, res_exp, rounded); mk_ite(is_zero_sig, zero_case, rounded, v7); @@ -656,39 +685,55 @@ void fpa2bv_converter::mk_add(func_decl * f, unsigned num, expr * const * args, void fpa2bv_converter::mk_sub(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); + expr_ref rm(m), x(m), y(m); + rm = args[0]; + x = args[1]; + y = args[2]; + mk_sub(f->get_range(), rm, x, y, result); +} + +void fpa2bv_converter::mk_sub(sort * s, expr_ref & rm, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref t(m); - mk_neg(f, 1, &args[2], t); - expr * nargs[3] = { args[0], args[1], t }; - mk_add(f, 3, nargs, result); + mk_neg(s, y, t); + mk_add(s, rm, x, t, result); } void fpa2bv_converter::mk_neg(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 1); - expr * sgn, * s, * e; - split_fp(args[0], sgn, e, s); + expr_ref x(m); + x = args[0]; + mk_neg(f->get_range(), x, result); +} + +void fpa2bv_converter::mk_neg(sort * srt, expr_ref & x, expr_ref & result) { + expr * sgn, *sig, *e; + split_fp(x, sgn, e, sig); expr_ref c(m), nsgn(m); - mk_is_nan(args[0], c); + mk_is_nan(x, c); nsgn = m_bv_util.mk_bv_not(sgn); expr_ref r_sgn(m); m_simp.mk_ite(c, sgn, nsgn, r_sgn); - mk_fp(r_sgn, e, s, result); + mk_fp(r_sgn, e, sig, result); } void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); - expr_ref bv_rm(m), x(m), y(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m), y(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; y = args[2]; + mk_mul(f->get_range(), rm, x, y, result); +} +void fpa2bv_converter::mk_mul(sort * s, expr_ref & rm, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); - mk_nan(f, nan); - mk_nzero(f, nzero); - mk_pzero(f, pzero); - mk_ninf(f, ninf); - mk_pinf(f, pinf); + mk_nan(s, nan); + mk_nzero(s, nzero); + mk_pzero(s, pzero); + mk_ninf(s, ninf); + mk_pinf(s, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); @@ -748,7 +793,7 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, mk_ite(sign_xor, nzero, pzero, v6); // else comes the actual multiplication. - unsigned sbits = m_util.get_sbits(f->get_range()); + unsigned sbits = m_util.get_sbits(s); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); @@ -806,7 +851,7 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, SASSERT(m_bv_util.get_bv_size(rbits) == 4); res_sig = m_bv_util.mk_concat(h_p, rbits); - round(f->get_range(), bv_rm, res_sgn, res_sig, res_exp, v7); + round(s, rm, res_sgn, res_sig, res_exp, v7); // And finally, we tie them together. mk_ite(c6, v6, v7, result); @@ -824,18 +869,20 @@ void fpa2bv_converter::mk_mul(func_decl * f, unsigned num, expr * const * args, void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 3); SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); - - expr_ref bv_rm(m), x(m), y(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m), y(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; y = args[2]; + mk_div(f->get_range(), rm, x, y, result); +} +void fpa2bv_converter::mk_div(sort * s, expr_ref & rm, expr_ref & x, expr_ref & y, expr_ref & result) { expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); - mk_nan(f, nan); - mk_nzero(f, nzero); - mk_pzero(f, pzero); - mk_ninf(f, ninf); - mk_pinf(f, pinf); + mk_nan(s, nan); + mk_nzero(s, nzero); + mk_pzero(s, pzero); + mk_ninf(s, ninf); + mk_pinf(s, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); @@ -899,8 +946,8 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, mk_ite(signs_xor, nzero, pzero, v7); // else comes the actual division. - unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); + unsigned ebits = m_util.get_ebits(s); + unsigned sbits = m_util.get_sbits(s); SASSERT(ebits <= sbits); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m), b_sgn(m), b_sig(m), b_exp(m), b_lz(m); @@ -956,7 +1003,7 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, m_simp.mk_ite(shift_cond, res_sig, res_sig_shifted, res_sig); m_simp.mk_ite(shift_cond, res_exp, res_exp_shifted, res_exp); - round(f->get_range(), bv_rm, res_sgn, res_sig, res_exp, v8); + round(s, rm, res_sgn, res_sig, res_exp, v8); // And finally, we tie them together. mk_ite(c7, v7, v8, result); @@ -974,21 +1021,22 @@ void fpa2bv_converter::mk_div(func_decl * f, unsigned num, expr * const * args, void fpa2bv_converter::mk_rem(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { SASSERT(num == 2); - - // Remainder is always exact, so there is no rounding mode. expr_ref x(m), y(m); x = args[0]; y = args[1]; + mk_rem(f->get_range(), x, y, result); +} +void fpa2bv_converter::mk_rem(sort * s, expr_ref & x, expr_ref & y, expr_ref & result) { TRACE("fpa2bv_rem", tout << "X = " << mk_ismt2_pp(x, m) << std::endl; - tout << "Y = " << mk_ismt2_pp(y, m) << std::endl;); + tout << "Y = " << mk_ismt2_pp(y, m) << std::endl;); expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); - mk_nan(f, nan); - mk_nzero(f, nzero); - mk_pzero(f, pzero); - mk_ninf(f, ninf); - mk_pinf(f, pinf); + mk_nan(s, nan); + mk_nzero(s, nzero); + mk_pzero(s, pzero); + mk_ninf(s, ninf); + mk_pinf(s, pinf); expr_ref x_is_nan(m), x_is_zero(m), x_is_pos(m), x_is_inf(m); expr_ref y_is_nan(m), y_is_zero(m), y_is_pos(m), y_is_inf(m); @@ -1033,71 +1081,32 @@ void fpa2bv_converter::mk_rem(func_decl * f, unsigned num, expr * const * args, c5 = x_is_zero; v5 = pzero; - // else the actual remainder. - unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); - - expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); - expr_ref b_sgn(m), b_sig(m), b_exp(m), b_lz(m); - unpack(x, a_sgn, a_sig, a_exp, a_lz, true); - unpack(y, b_sgn, b_sig, b_exp, b_lz, true); - - dbg_decouple("fpa2bv_rem_a_sgn", a_sgn); - dbg_decouple("fpa2bv_rem_a_sig", a_sig); - dbg_decouple("fpa2bv_rem_a_exp", a_exp); - dbg_decouple("fpa2bv_rem_a_lz", a_lz); - dbg_decouple("fpa2bv_rem_b_sgn", b_sgn); - dbg_decouple("fpa2bv_rem_b_sig", b_sig); - dbg_decouple("fpa2bv_rem_b_exp", b_exp); - dbg_decouple("fpa2bv_rem_b_lz", b_lz); - - BVSLT(a_exp, b_exp, c6); + // exp(x) < exp(y) -> x + expr * x_sgn, *x_sig, *x_exp; + expr * y_sgn, *y_sig, *y_exp; + split_fp(x, x_sgn, x_exp, x_sig); + split_fp(y, y_sgn, y_exp, y_sig); + BVSLT(x_exp, y_exp, c6); v6 = x; - // max. exponent difference is (2^ebits) - 3 - const mpz & two_to_ebits = fu().fm().m_powers2(ebits); - mpz max_exp_diff; - m_mpz_manager.sub(two_to_ebits, 3, max_exp_diff); - SASSERT(m_mpz_manager.is_int64(max_exp_diff)); - SASSERT(m_mpz_manager.get_uint64(max_exp_diff) <= UINT_MAX); + // else the actual remainder, r = x - y * n + expr_ref rne(m), nr(m), n(m), yn(m), r(m); + rne = m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3); + mk_div(s, rne, x, y, nr); + mk_round_to_integral(s, rne, nr, n); + mk_mul(s, rne, y, n, yn); + mk_sub(s, rne, x, yn, r); - unsigned int max_exp_diff_ui = (unsigned int)m_mpz_manager.get_uint64(max_exp_diff); - m_mpz_manager.del(max_exp_diff); + expr_ref r_is_zero(m), x_sgn_ref(x_sgn, m), x_sgn_zero(m); + mk_is_zero(r, r_is_zero); + mk_zero(s, x_sgn_ref, x_sgn_zero); + mk_ite(r_is_zero, x_sgn_zero, r, v7); - expr_ref a_exp_ext(m), b_exp_ext(m); - a_exp_ext = m_bv_util.mk_sign_extend(2, a_exp); - b_exp_ext = m_bv_util.mk_sign_extend(2, b_exp); - - expr_ref a_lz_ext(m), b_lz_ext(m); - a_lz_ext = m_bv_util.mk_zero_extend(2, a_lz); - b_lz_ext = m_bv_util.mk_zero_extend(2, b_lz); - - expr_ref exp_diff(m); - exp_diff = m_bv_util.mk_bv_sub( - m_bv_util.mk_bv_sub(a_exp_ext, a_lz_ext), - m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext)); - dbg_decouple("fpa2bv_rem_exp_diff", exp_diff); - - // CMW: This creates _huge_ bit-vectors, which is potentially sub-optimal, - // but calculating this via rem = x - y * nearest(x/y) creates huge circuits. - expr_ref huge_sig(m), shifted_sig(m), huge_rem(m); - huge_sig = m_bv_util.mk_zero_extend(max_exp_diff_ui, a_sig); - shifted_sig = m_bv_util.mk_bv_shl(huge_sig, m_bv_util.mk_zero_extend(max_exp_diff_ui + sbits - ebits - 2, exp_diff)); - huge_rem = m_bv_util.mk_bv_urem(shifted_sig, m_bv_util.mk_zero_extend(max_exp_diff_ui, b_sig)); - dbg_decouple("fpa2bv_rem_huge_rem", huge_rem); - - expr_ref res_sgn(m), res_sig(m), res_exp(m); - res_sgn = a_sgn; - res_sig = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits, 0, huge_rem), - m_bv_util.mk_numeral(0, 3)); - - res_exp = m_bv_util.mk_bv_sub(b_exp_ext, b_lz_ext); - - // CMW: Actual rounding is not necessary here, this is - // just convenience to get rid of the extra bits. - expr_ref bv_rm(m); - bv_rm = m_bv_util.mk_numeral(BV_RM_TIES_TO_EVEN, 3); - round(f->get_range(), bv_rm, res_sgn, res_sig, res_exp, v7); + dbg_decouple("fpa2bv_rem_nr", nr); + dbg_decouple("fpa2bv_rem_n", n); + dbg_decouple("fpa2bv_rem_yn", yn); + dbg_decouple("fpa2bv_rem_r", r); + dbg_decouple("fpa2bv_rem_v7", v7); // And finally, we tie them together. mk_ite(c6, v6, v7, result); @@ -1302,8 +1311,8 @@ void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); // fusedma means (x * y) + z - expr_ref bv_rm(m), x(m), y(m), z(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m), y(m), z(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; y = args[2]; z = args[3]; @@ -1335,7 +1344,7 @@ void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, mk_is_inf(z, z_is_inf); expr_ref rm_is_to_neg(m); - mk_is_rm(bv_rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); dbg_decouple("fpa2bv_fma_x_is_nan", x_is_nan); dbg_decouple("fpa2bv_fma_x_is_zero", x_is_zero); @@ -1597,7 +1606,7 @@ void fpa2bv_converter::mk_fma(func_decl * f, unsigned num, expr * const * args, mk_ite(rm_is_to_neg, nzero, pzero, zero_case); expr_ref rounded(m); - round(f->get_range(), bv_rm, res_sgn, res_sig, res_exp, rounded); + round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); mk_ite(is_zero_sig, zero_case, rounded, v8); @@ -1619,8 +1628,8 @@ void fpa2bv_converter::mk_sqrt(func_decl * f, unsigned num, expr * const * args, SASSERT(num == 2); SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); - expr_ref bv_rm(m), x(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); @@ -1752,7 +1761,7 @@ void fpa2bv_converter::mk_sqrt(func_decl * f, unsigned num, expr * const * args, SASSERT(m_bv_util.get_bv_size(res_sig) == sbits + 4); expr_ref rounded(m); - round(f->get_range(), bv_rm, res_sgn, res_sig, res_exp, rounded); + round(f->get_range(), rm, res_sgn, res_sig, res_exp, rounded); v5 = rounded; // And finally, we tie them together. @@ -1768,21 +1777,24 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * SASSERT(num == 2); SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); - expr_ref bv_rm(m), x(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; + mk_round_to_integral(f->get_range(), rm, x, result); +} +void fpa2bv_converter::mk_round_to_integral(sort * s, expr_ref & rm, expr_ref & x, expr_ref & result) { expr_ref rm_is_rta(m), rm_is_rte(m), rm_is_rtp(m), rm_is_rtn(m), rm_is_rtz(m); - mk_is_rm(bv_rm, BV_RM_TIES_TO_AWAY, rm_is_rta); - mk_is_rm(bv_rm, BV_RM_TIES_TO_EVEN, rm_is_rte); - mk_is_rm(bv_rm, BV_RM_TO_POSITIVE, rm_is_rtp); - mk_is_rm(bv_rm, BV_RM_TO_NEGATIVE, rm_is_rtn); - mk_is_rm(bv_rm, BV_RM_TO_ZERO, rm_is_rtz); + mk_is_rm(rm, BV_RM_TIES_TO_AWAY, rm_is_rta); + mk_is_rm(rm, BV_RM_TIES_TO_EVEN, rm_is_rte); + mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_is_rtp); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_rtn); + mk_is_rm(rm, BV_RM_TO_ZERO, rm_is_rtz); expr_ref nan(m), nzero(m), pzero(m), ninf(m), pinf(m); - mk_nan(f, nan); - mk_nzero(f, nzero); - mk_pzero(f, pzero); + mk_nan(s, nan); + mk_nzero(s, nzero); + mk_pzero(s, pzero); expr_ref x_is_zero(m), x_is_pos(m), x_is_neg(m); mk_is_zero(x, x_is_zero); @@ -1812,14 +1824,16 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * one_1 = m_bv_util.mk_numeral(1, 1); zero_1 = m_bv_util.mk_numeral(0, 1); - unsigned ebits = m_util.get_ebits(f->get_range()); - unsigned sbits = m_util.get_sbits(f->get_range()); + unsigned ebits = m_util.get_ebits(s); + unsigned sbits = m_util.get_sbits(s); expr_ref a_sgn(m), a_sig(m), a_exp(m), a_lz(m); unpack(x, a_sgn, a_sig, a_exp, a_lz, true); - dbg_decouple("fpa2bv_r2i_unpacked_sig", a_sig); + dbg_decouple("fpa2bv_r2i_unpacked_sgn", a_sgn); dbg_decouple("fpa2bv_r2i_unpacked_exp", a_exp); + dbg_decouple("fpa2bv_r2i_unpacked_sig", a_sig); + dbg_decouple("fpa2bv_r2i_unpacked_lz", a_lz); expr_ref xzero(m), sgn_eq_1(m); sgn_eq_1 = m.mk_eq(a_sgn, one_1); @@ -1833,8 +1847,8 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * c4 = exp_lt_zero; expr_ref pone(m), none(m), xone(m), c421(m), c422(m), c423(m), t1(m), t2(m), tie(m), v42(m), exp_lt_m1(m); - mk_one(f, zero_1, pone); - mk_one(f, one_1, none); + mk_one(s, zero_1, pone); + mk_one(s, one_1, none); mk_ite(sgn_eq_1, none, pone, xone); expr_ref pow_2_sbitsm1(m), m1(m); @@ -1882,49 +1896,53 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * res_sgn = a_sgn; res_exp = a_exp; - expr_ref shift(m), rshift(m), div(m), rem(m); - shift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits - 1, sbits + 1), - m_bv_util.mk_sign_extend(sbits - ebits + 1, a_exp)); - rshift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits, sbits + 1), shift); - div = m_bv_util.mk_bv_lshr(m_bv_util.mk_zero_extend(1, a_sig), shift); - rem = m_bv_util.mk_bv_lshr(m_bv_util.mk_bv_shl(m_bv_util.mk_zero_extend(1, a_sig), rshift), rshift); + SASSERT(m_bv_util.get_bv_size(a_sig) == sbits); + SASSERT(m_bv_util.get_bv_size(a_exp) == ebits); + + expr_ref zero_s(m); + zero_s = m_bv_util.mk_numeral(0, sbits); + + expr_ref shift(m), shifted_sig(m), div(m), rem(m); + shift = m_bv_util.mk_bv_sub(m_bv_util.mk_numeral(sbits - 1, sbits), + m_bv_util.mk_zero_extend(sbits-ebits, a_exp)); + shifted_sig = m_bv_util.mk_bv_lshr(m_bv_util.mk_concat(a_sig, zero_s), + m_bv_util.mk_concat(zero_s, shift)); + div = m_bv_util.mk_extract(2*sbits-1, sbits, shifted_sig); + rem = m_bv_util.mk_extract(sbits-1, 0, shifted_sig); SASSERT(is_well_sorted(m, div)); SASSERT(is_well_sorted(m, rem)); - SASSERT(m_bv_util.get_bv_size(div) == sbits + 1); - SASSERT(m_bv_util.get_bv_size(rem) == sbits + 1); + SASSERT(m_bv_util.get_bv_size(shift) == sbits); + SASSERT(m_bv_util.get_bv_size(div) == sbits); + SASSERT(m_bv_util.get_bv_size(rem) == sbits); + dbg_decouple("fpa2bv_r2i_shifted_sig", shifted_sig); dbg_decouple("fpa2bv_r2i_shift", shift); - dbg_decouple("fpa2bv_r2i_rshift", rshift); dbg_decouple("fpa2bv_r2i_div", div); dbg_decouple("fpa2bv_r2i_rem", rem); expr_ref div_p1(m); - div_p1 = m_bv_util.mk_bv_add(div, m_bv_util.mk_numeral(1, sbits+1)); + div_p1 = m_bv_util.mk_bv_add(div, m_bv_util.mk_numeral(1, sbits)); - expr_ref tie2(m), tie2_c(m), div_last(m), v51(m), rem_shl(m); - rem_shl = m_bv_util.mk_concat(m_bv_util.mk_extract(sbits - 1, 0, rem), zero_1); - m_simp.mk_eq(rem_shl, - m_bv_util.mk_bv_shl(m_bv_util.mk_numeral(1, sbits+1), shift), - tie2); + expr_ref tie_pttrn(m), tie2(m), tie2_c(m), div_last(m), v51(m); + tie_pttrn = m_bv_util.mk_concat(one_1, m_bv_util.mk_numeral(0, sbits-1)); + m_simp.mk_eq(rem, tie_pttrn, tie2); div_last = m_bv_util.mk_extract(0, 0, div); - tie2_c = m.mk_or(m.mk_and(tie2, - m.mk_or(m.mk_and(rm_is_rte, m.mk_eq(div_last, one_1)), - m.mk_and(rm_is_rta, m.mk_eq(div_last, zero_1)))), - m.mk_xor(m.mk_eq(a_sgn, one_1), - m_bv_util.mk_sle(m_bv_util.mk_bv_shl(m_bv_util.mk_numeral(1, sbits + 1), shift), - rem_shl))); + tie2_c = m.mk_ite(tie2, m.mk_or(m.mk_and(rm_is_rte, m.mk_eq(div_last, one_1)), + m.mk_and(rm_is_rta, m.mk_eq(div_last, zero_1))), + m_bv_util.mk_ule(tie_pttrn, rem)); m_simp.mk_ite(tie2_c, div_p1, div, v51); dbg_decouple("fpa2bv_r2i_v51", v51); dbg_decouple("fpa2bv_r2i_tie2", tie2); + dbg_decouple("fpa2bv_r2i_tie2_c", tie2_c); SASSERT(is_well_sorted(m, tie2)); SASSERT(is_well_sorted(m, tie2_c)); SASSERT(is_well_sorted(m, v51)); expr_ref c521(m), v52(m), rem_eq_0(m), sgn_eq_zero(m); - rem_eq_0 = m.mk_eq(rem, m_bv_util.mk_numeral(0, sbits + 1)); + rem_eq_0 = m.mk_eq(rem, m_bv_util.mk_numeral(0, sbits)); sgn_eq_zero = m.mk_eq(res_sgn, zero_1); m_simp.mk_not(rem_eq_0, c521); m_simp.mk_and(c521, sgn_eq_zero, c521); @@ -1945,14 +1963,14 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * m_simp.mk_ite(c53, v53, res_sig, res_sig); m_simp.mk_ite(c52, v52, res_sig, res_sig); m_simp.mk_ite(c51, v51, res_sig, res_sig); - res_sig = m_bv_util.mk_concat(res_sig, m_bv_util.mk_numeral(0, 3)); // rounding bits are all 0. + res_sig = m_bv_util.mk_zero_extend(1, m_bv_util.mk_concat(res_sig, m_bv_util.mk_numeral(0, 3))); // rounding bits are all 0. SASSERT(m_bv_util.get_bv_size(res_exp) == ebits); - SASSERT(m_bv_util.get_bv_size(shift) == sbits + 1); + SASSERT(m_bv_util.get_bv_size(shift) == sbits); expr_ref e_shift(m); e_shift = (ebits + 2 <= sbits + 1) ? m_bv_util.mk_extract(ebits + 1, 0, shift) : - m_bv_util.mk_sign_extend((ebits + 2) - (sbits + 1), shift); + m_bv_util.mk_sign_extend((ebits + 2) - (sbits), shift); SASSERT(m_bv_util.get_bv_size(e_shift) == ebits + 2); res_exp = m_bv_util.mk_bv_add(m_bv_util.mk_zero_extend(2, res_exp), e_shift); @@ -1961,7 +1979,7 @@ void fpa2bv_converter::mk_round_to_integral(func_decl * f, unsigned num, expr * SASSERT(m_bv_util.get_bv_size(res_exp) == ebits + 2); // CMW: We use the rounder for normalization. - round(f->get_range(), bv_rm, res_sgn, res_sig, res_exp, v6); + round(s, rm, res_sgn, res_sig, res_exp, v6); // And finally, we tie them together. mk_ite(c5, v5, v6, result); @@ -2683,8 +2701,8 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); SASSERT(m_bv_util.is_bv(args[1])); - expr_ref bv_rm(m), x(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; dbg_decouple("fpa2bv_to_fp_signed_x", x); @@ -2692,7 +2710,7 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); unsigned bv_sz = m_bv_util.get_bv_size(x); - SASSERT(m_bv_util.get_bv_size(bv_rm) == 3); + SASSERT(m_bv_util.get_bv_size(rm) == 3); expr_ref bv0_1(m), bv1_1(m), bv0_sz(m), bv1_sz(m); bv0_1 = m_bv_util.mk_numeral(0, 1); @@ -2802,7 +2820,7 @@ void fpa2bv_converter::mk_to_fp_signed(func_decl * f, unsigned num, expr * const SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); expr_ref v2(m); - round(f->get_range(), bv_rm, sgn, sig, exp, v2); + round(f->get_range(), rm, sgn, sig, exp, v2); mk_ite(c1, v1, v2, result); } @@ -2825,8 +2843,8 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); SASSERT(m_bv_util.is_bv(args[1])); - expr_ref bv_rm(m), x(m); - bv_rm = to_app(args[0])->get_arg(0); + expr_ref rm(m), x(m); + rm = to_app(args[0])->get_arg(0); x = args[1]; dbg_decouple("fpa2bv_to_fp_unsigned_x", x); @@ -2834,7 +2852,7 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con unsigned ebits = m_util.get_ebits(f->get_range()); unsigned sbits = m_util.get_sbits(f->get_range()); unsigned bv_sz = m_bv_util.get_bv_size(x); - SASSERT(m_bv_util.get_bv_size(bv_rm) == 3); + SASSERT(m_bv_util.get_bv_size(rm) == 3); expr_ref bv0_1(m), bv1_1(m), bv0_sz(m), bv1_sz(m); bv0_1 = m_bv_util.mk_numeral(0, 1); @@ -2935,7 +2953,7 @@ void fpa2bv_converter::mk_to_fp_unsigned(func_decl * f, unsigned num, expr * con SASSERT(m_bv_util.get_bv_size(exp) == ebits + 2); expr_ref v2(m); - round(f->get_range(), bv_rm, sgn, sig, exp, v2); + round(f->get_range(), rm, sgn, sig, exp, v2); mk_ite(c1, v1, v2, result); } @@ -2977,7 +2995,7 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args SASSERT(is_app_of(args[0], m_util.get_family_id(), OP_FPA_INTERNAL_RM)); SASSERT(m_util.is_float(args[1])); - expr * bv_rm = to_app(args[0])->get_arg(0); + expr * rm = to_app(args[0])->get_arg(0); expr * x = args[1]; sort * xs = m.get_sort(x); sort * bv_srt = f->get_range(); @@ -3080,7 +3098,7 @@ void fpa2bv_converter::mk_to_bv(func_decl * f, unsigned num, expr * const * args dbg_decouple("fpa2bv_to_bv_sticky", sticky); expr_ref rounding_decision(m); - rounding_decision = mk_rounding_decision(bv_rm, sgn, last, round, sticky); + rounding_decision = mk_rounding_decision(rm, sgn, last, round, sticky); SASSERT(m_bv_util.get_bv_size(rounding_decision) == 1); dbg_decouple("fpa2bv_to_bv_rounding_decision", rounding_decision); @@ -3382,7 +3400,7 @@ void fpa2bv_converter::mk_is_normal(expr * e, expr_ref & result) { m_simp.mk_not(or_ex, result); } -void fpa2bv_converter::mk_is_rm(expr * bv_rm, BV_RM_VAL rm, expr_ref & result) { +void fpa2bv_converter::mk_is_rm(expr * rme, BV_RM_VAL rm, expr_ref & result) { expr_ref rm_num(m); rm_num = m_bv_util.mk_numeral(rm, 3); @@ -3393,7 +3411,7 @@ void fpa2bv_converter::mk_is_rm(expr * bv_rm, BV_RM_VAL rm, expr_ref & result) { case BV_RM_TO_NEGATIVE: case BV_RM_TO_POSITIVE: case BV_RM_TO_ZERO: - return m_simp.mk_eq(bv_rm, rm_num, result); + return m_simp.mk_eq(rme, rm_num, result); default: UNREACHABLE(); } @@ -3598,15 +3616,34 @@ void fpa2bv_converter::dbg_decouple(const char * prefix, expr_ref & e) { #ifdef Z3DEBUG return; // CMW: This works only for quantifier-free formulas. - expr_ref new_e(m); - new_e = m.mk_fresh_const(prefix, m.get_sort(e)); - m_extra_assertions.push_back(m.mk_eq(new_e, e)); - e = new_e; + if (is_app_of(e, m_plugin->get_family_id(), OP_FPA_FP)) { + expr_ref new_bv(m); + expr *e_sgn, *e_sig, *e_exp; + split_fp(e, e_sgn, e_exp, e_sig); + unsigned ebits = m_bv_util.get_bv_size(e_exp); + unsigned sbits = m_bv_util.get_bv_size(e_sig) + 1; + unsigned bv_sz = ebits + sbits; + new_bv = m.mk_fresh_const(prefix, m_bv_util.mk_sort(bv_sz)); + expr_ref bv_sgn(m), bv_exp(m), bv_sig(m); + bv_sgn = m_bv_util.mk_extract(bv_sz-1, bv_sz-1, new_bv); + bv_exp = m_bv_util.mk_extract(bv_sz-2, bv_sz-ebits-1, new_bv); + bv_sig = m_bv_util.mk_extract(sbits-2, 0, new_bv); + m_extra_assertions.push_back(m.mk_eq(e_sgn, bv_sgn)); + m_extra_assertions.push_back(m.mk_eq(e_exp, bv_exp)); + m_extra_assertions.push_back(m.mk_eq(e_sig, bv_sig)); + e = m_util.mk_fp(bv_sgn, bv_exp, bv_sig); + } + else { + expr_ref new_e(m); + new_e = m.mk_fresh_const(prefix, m.get_sort(e)); + m_extra_assertions.push_back(m.mk_eq(new_e, e)); + e = new_e; + } #endif } -expr_ref fpa2bv_converter::mk_rounding_decision(expr * bv_rm, expr * sgn, expr * last, expr * round, expr * sticky) { - expr_ref rmr(bv_rm, m); +expr_ref fpa2bv_converter::mk_rounding_decision(expr * rm, expr * sgn, expr * last, expr * round, expr * sticky) { + expr_ref rmr(rm, m); expr_ref sgnr(sgn, m); expr_ref lastr(last, m); expr_ref roundr(round, m); @@ -3645,10 +3682,10 @@ expr_ref fpa2bv_converter::mk_rounding_decision(expr * bv_rm, expr * sgn, expr * expr_ref res(m), inc_c2(m), inc_c3(m), inc_c4(m); expr_ref rm_is_to_neg(m), rm_is_to_pos(m), rm_is_away(m), rm_is_even(m), nil_1(m); nil_1 = m_bv_util.mk_numeral(0, 1); - mk_is_rm(bv_rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); - mk_is_rm(bv_rm, BV_RM_TO_POSITIVE, rm_is_to_pos); - mk_is_rm(bv_rm, BV_RM_TIES_TO_AWAY, rm_is_away); - mk_is_rm(bv_rm, BV_RM_TIES_TO_EVEN, rm_is_even); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_is_to_pos); + mk_is_rm(rm, BV_RM_TIES_TO_AWAY, rm_is_away); + mk_is_rm(rm, BV_RM_TIES_TO_EVEN, rm_is_even); m_simp.mk_ite(rm_is_to_neg, inc_neg, nil_1, inc_c4); m_simp.mk_ite(rm_is_to_pos, inc_pos, inc_c4, inc_c3); m_simp.mk_ite(rm_is_away, inc_taway, inc_c3, inc_c2); @@ -3658,16 +3695,16 @@ expr_ref fpa2bv_converter::mk_rounding_decision(expr * bv_rm, expr * sgn, expr * return res; } -void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & result) { +void fpa2bv_converter::round(sort * s, expr_ref & rm, expr_ref & sgn, expr_ref & sig, expr_ref & exp, expr_ref & result) { unsigned ebits = m_util.get_ebits(s); unsigned sbits = m_util.get_sbits(s); - dbg_decouple("fpa2bv_rnd_rm", bv_rm); + dbg_decouple("fpa2bv_rnd_rm", rm); dbg_decouple("fpa2bv_rnd_sgn", sgn); dbg_decouple("fpa2bv_rnd_sig", sig); dbg_decouple("fpa2bv_rnd_exp", exp); - SASSERT(is_well_sorted(m, bv_rm)); + SASSERT(is_well_sorted(m, rm)); SASSERT(is_well_sorted(m, sgn)); SASSERT(is_well_sorted(m, sig)); SASSERT(is_well_sorted(m, exp)); @@ -3683,7 +3720,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_re // i.e., it has 2 + (sbits-1) + 3 = sbits + 4 bits, where the first one is in sgn. // Furthermore, note that sig is an unsigned bit-vector, while exp is signed. - SASSERT(m_bv_util.is_bv(bv_rm) && m_bv_util.get_bv_size(bv_rm) == 3); + SASSERT(m_bv_util.is_bv(rm) && m_bv_util.get_bv_size(rm) == 3); SASSERT(m_bv_util.is_bv(sgn) && m_bv_util.get_bv_size(sgn) == 1); SASSERT(m_bv_util.is_bv(sig) && m_bv_util.get_bv_size(sig) >= 5); SASSERT(m_bv_util.is_bv(exp) && m_bv_util.get_bv_size(exp) >= 4); @@ -3833,7 +3870,7 @@ void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_re sig = m_bv_util.mk_extract(sbits+1, 2, sig); expr_ref inc(m); - inc = mk_rounding_decision(bv_rm, sgn, last, round, sticky); + inc = mk_rounding_decision(rm, sgn, last, round, sticky); SASSERT(m_bv_util.get_bv_size(inc) == 1 && is_well_sorted(m, inc)); dbg_decouple("fpa2bv_rnd_inc", inc); @@ -3906,9 +3943,9 @@ void fpa2bv_converter::round(sort * s, expr_ref & bv_rm, expr_ref & sgn, expr_re nil_1 = m_bv_util.mk_numeral(0, 1); expr_ref rm_is_to_zero(m), rm_is_to_neg(m), rm_is_to_pos(m), rm_zero_or_neg(m), rm_zero_or_pos(m); - mk_is_rm(bv_rm, BV_RM_TO_ZERO, rm_is_to_zero); - mk_is_rm(bv_rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); - mk_is_rm(bv_rm, BV_RM_TO_POSITIVE, rm_is_to_pos); + mk_is_rm(rm, BV_RM_TO_ZERO, rm_is_to_zero); + mk_is_rm(rm, BV_RM_TO_NEGATIVE, rm_is_to_neg); + mk_is_rm(rm, BV_RM_TO_POSITIVE, rm_is_to_pos); m_simp.mk_or(rm_is_to_zero, rm_is_to_neg, rm_zero_or_neg); m_simp.mk_or(rm_is_to_zero, rm_is_to_pos, rm_zero_or_pos); dbg_decouple("fpa2bv_rnd_rm_is_to_zero", rm_is_to_zero); diff --git a/src/ast/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h index f69351cb2..b6a6bdbb3 100644 --- a/src/ast/fpa/fpa2bv_converter.h +++ b/src/ast/fpa/fpa2bv_converter.h @@ -145,7 +145,7 @@ public: expr_ref_vector m_extra_assertions; protected: - void mk_one(func_decl *f, expr_ref sign, expr_ref & result); + void mk_one(func_decl *f, expr_ref & sign, expr_ref & result); void mk_is_nan(expr * e, expr_ref & result); void mk_is_inf(expr * e, expr_ref & result); @@ -184,6 +184,23 @@ protected: void mk_to_bv(func_decl * f, unsigned num, expr * const * args, bool is_signed, expr_ref & result); void mk_uninterpreted_output(sort * rng, func_decl * fbv, expr_ref_buffer & new_args, expr_ref & result); + +private: + void mk_nan(sort * s, expr_ref & result); + void mk_nzero(sort * s, expr_ref & result); + void mk_pzero(sort * s, expr_ref & result); + void mk_zero(sort * s, expr_ref & sgn, expr_ref & result); + void mk_ninf(sort * s, expr_ref & result); + void mk_pinf(sort * s, expr_ref & result); + + void mk_one(sort * s, expr_ref & sign, expr_ref & result); + void mk_neg(sort * s, expr_ref & x, expr_ref & result); + void mk_add(sort * s, expr_ref & bv_rm, expr_ref & x, expr_ref & y, expr_ref & result); + void mk_sub(sort * s, expr_ref & bv_rm, expr_ref & x, expr_ref & y, expr_ref & result); + void mk_mul(sort * s, expr_ref & bv_rm, expr_ref & x, expr_ref & y, expr_ref & result); + void mk_div(sort * s, expr_ref & bv_rm, expr_ref & x, expr_ref & y, expr_ref & result); + void mk_rem(sort * s, expr_ref & x, expr_ref & y, expr_ref & result); + void mk_round_to_integral(sort * s, expr_ref & rm, expr_ref & x, expr_ref & result); }; #endif diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 5de4c406a..0af5352cc 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -1077,21 +1077,20 @@ void mpf_manager::round_to_integral(mpf_rounding_mode rm, mpf const & x, mpf & o unsigned shift = (o.sbits - 1) - ((unsigned)o.exponent); const mpz & shift_p = m_powers2(shift); + const mpz & shiftm1_p = m_powers2(shift-1); TRACE("mpf_dbg", tout << "shift=" << shift << std::endl;); + TRACE("mpf_dbg", tout << "shiftm1_p=" << m_mpz_manager.to_string(shiftm1_p) << std::endl;); scoped_mpz div(m_mpz_manager), rem(m_mpz_manager); m_mpz_manager.machine_div_rem(o.significand, shift_p, div, rem); TRACE("mpf_dbg", tout << "div=" << m_mpz_manager.to_string(div) << " rem=" << m_mpz_manager.to_string(rem) << std::endl;); - const mpz & shift_p1 = m_powers2(shift-1); - TRACE("mpf_dbg", tout << "shift_p1=" << m_mpz_manager.to_string(shift_p1) << std::endl;); - switch (rm) { case MPF_ROUND_NEAREST_TEVEN: case MPF_ROUND_NEAREST_TAWAY: { - bool tie = m_mpz_manager.eq(rem, shift_p1); - bool less_than_tie = m_mpz_manager.lt(rem, shift_p1); - bool more_than_tie = m_mpz_manager.gt(rem, shift_p1); + bool tie = m_mpz_manager.eq(rem, shiftm1_p); + bool less_than_tie = m_mpz_manager.lt(rem, shiftm1_p); + bool more_than_tie = m_mpz_manager.gt(rem, shiftm1_p); TRACE("mpf_dbg", tout << "tie= " << tie << "; tie = " << more_than_tie << std::endl;); if (tie) { if ((rm == MPF_ROUND_NEAREST_TEVEN && m_mpz_manager.is_odd(div)) || @@ -1231,43 +1230,18 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { else if (is_zero(x)) set(o, x); else { - o.ebits = x.ebits; - o.sbits = x.sbits; - o.sign = x.sign; + // r = x - y * n + scoped_mpf nr(*this), n(*this), yn(*this); + div(MPF_ROUND_NEAREST_TEVEN, x, y, nr); + round_to_integral(MPF_ROUND_NEAREST_TEVEN, nr, n); + mul(MPF_ROUND_NEAREST_TEVEN, y, n, yn); + sub(MPF_ROUND_NEAREST_TEVEN, x, yn, o); - scoped_mpf a(*this), b(*this); - set(a, x); - set(b, y); - unpack(a, true); - unpack(b, true); + if (is_zero(o)) + o.sign = x.sign; - TRACE("mpf_dbg", tout << "A = " << to_string(a) << std::endl;); - TRACE("mpf_dbg", tout << "B = " << to_string(b) << std::endl;); - - if (a.exponent() < b.exponent()) - set(o, x); - else { - mpf_exp_t exp_diff = a.exponent() - b.exponent(); - SASSERT(exp_diff >= 0); - TRACE("mpf_dbg", tout << "exp_diff = " << exp_diff << std::endl;); - - SASSERT(exp_diff < INT_MAX); - // CMW: This requires rather a lot of memory. There are algorithms that trade space for time by - // computing only a small chunk of the remainder bits at a time. - unsigned extra_bits = (unsigned) exp_diff; - m_mpz_manager.mul2k(a.significand(), extra_bits); - m_mpz_manager.rem(a.significand(), b.significand(), o.significand); - - TRACE("mpf_dbg", tout << "REM' = " << to_string(o) << std::endl;); - - if (m_mpz_manager.is_zero(o.significand)) - mk_zero(o.ebits, o.sbits, o.sign, o); - else { - o.exponent = b.exponent(); - m_mpz_manager.mul2k(o.significand, 3); // rounding bits - round(MPF_ROUND_NEAREST_TEVEN, o); - } - } + TRACE("mpf_dbg", tout << "N = " << to_string(n) << std::endl;); + TRACE("mpf_dbg", tout << "YN = " << to_string(yn) << std::endl;); } TRACE("mpf_dbg", tout << "REMAINDER = " << to_string(o) << std::endl;); From d97bddc3b50337b57e505aab045aeb99d4f3c982 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 24 Apr 2016 09:21:05 -0700 Subject: [PATCH 46/66] revert to legacy syntax to enable older versions of .NET Signed-off-by: Nikolaj Bjorner --- src/api/dotnet/ArithExpr.cs | 84 ++++++++++++++++++------------------- src/api/dotnet/BoolExpr.cs | 8 ++-- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/api/dotnet/ArithExpr.cs b/src/api/dotnet/ArithExpr.cs index 58d160900..705a9f6ca 100644 --- a/src/api/dotnet/ArithExpr.cs +++ b/src/api/dotnet/ArithExpr.cs @@ -41,129 +41,129 @@ namespace Microsoft.Z3 #region Operators - private static ArithExpr MkNum(ArithExpr e, int i) => (ArithExpr)e.Context.MkNumeral(i, e.Context.MkIntSort()); + private static ArithExpr MkNum(ArithExpr e, int i) { return (ArithExpr)e.Context.MkNumeral(i, e.Context.MkIntSort()); } - private static ArithExpr MkNum(ArithExpr e, double d) => (ArithExpr)e.Context.MkNumeral(d.ToString(), e.Context.MkRealSort()); + private static ArithExpr MkNum(ArithExpr e, double d) { return (ArithExpr)e.Context.MkNumeral(d.ToString(), e.Context.MkRealSort()); } /// Operator overloading for arithmetical divsion operator (over reals) - public static ArithExpr operator /(ArithExpr a, ArithExpr b) => a.Context.MkDiv(a, b); + public static ArithExpr operator /(ArithExpr a, ArithExpr b) { return a.Context.MkDiv(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator /(ArithExpr a, int b) => a / MkNum(a, b); + public static ArithExpr operator /(ArithExpr a, int b) { return a / MkNum(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator /(ArithExpr a, double b) => a / MkNum(a, b); + public static ArithExpr operator /(ArithExpr a, double b) { return a / MkNum(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator /(int a, ArithExpr b) => MkNum(b, a) / b; + public static ArithExpr operator /(int a, ArithExpr b) { return MkNum(b, a) / b; } /// Operator overloading for arithmetical operator - public static ArithExpr operator /(double a, ArithExpr b) => MkNum(b, a) / b; + public static ArithExpr operator /(double a, ArithExpr b) { return MkNum(b, a) / b; } /// Operator overloading for arithmetical operator - public static ArithExpr operator -(ArithExpr a, ArithExpr b) => a.Context.MkSub(a, b); + public static ArithExpr operator -(ArithExpr a, ArithExpr b) { return a.Context.MkSub(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator -(ArithExpr a, int b) => a - MkNum(a, b); + public static ArithExpr operator -(ArithExpr a, int b) { return a - MkNum(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator -(ArithExpr a, double b) => a - MkNum(a, b); + public static ArithExpr operator -(ArithExpr a, double b) { return a - MkNum(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator -(int a, ArithExpr b) => MkNum(b, a) - b; + public static ArithExpr operator -(int a, ArithExpr b) { return MkNum(b, a) - b; } /// Operator overloading for arithmetical operator - public static ArithExpr operator -(double a, ArithExpr b) => MkNum(b, a) - b; + public static ArithExpr operator -(double a, ArithExpr b) { return MkNum(b, a) - b; } /// Operator overloading for arithmetical operator - public static ArithExpr operator +(ArithExpr a, ArithExpr b) => a.Context.MkAdd(a, b); + public static ArithExpr operator +(ArithExpr a, ArithExpr b) { return a.Context.MkAdd(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator +(ArithExpr a, int b) => a + MkNum(a, b); + public static ArithExpr operator +(ArithExpr a, int b) { return a + MkNum(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator +(ArithExpr a, double b) => a + MkNum(a, b); + public static ArithExpr operator +(ArithExpr a, double b) { return a + MkNum(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator +(int a, ArithExpr b) => MkNum(b, a) + b; + public static ArithExpr operator +(int a, ArithExpr b) { return MkNum(b, a) + b; } /// Operator overloading for arithmetical operator - public static ArithExpr operator +(double a, ArithExpr b) => MkNum(b, a) + b; + public static ArithExpr operator +(double a, ArithExpr b) { return MkNum(b, a) + b; } /// Operator overloading for arithmetical operator - public static ArithExpr operator *(ArithExpr a, ArithExpr b) => a.Context.MkMul(a, b); + public static ArithExpr operator *(ArithExpr a, ArithExpr b) { return a.Context.MkMul(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator *(ArithExpr a, int b) => a * MkNum(a, b); + public static ArithExpr operator *(ArithExpr a, int b) { return a * MkNum(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator *(ArithExpr a, double b) => a * MkNum(a, b); + public static ArithExpr operator *(ArithExpr a, double b) { return a * MkNum(a, b); } /// Operator overloading for arithmetical operator - public static ArithExpr operator *(int a, ArithExpr b) => MkNum(b, a) * b; + public static ArithExpr operator *(int a, ArithExpr b) { return MkNum(b, a) * b; } /// Operator overloading for arithmetical operator - public static ArithExpr operator *(double a, ArithExpr b) => MkNum(b, a) * b; + public static ArithExpr operator *(double a, ArithExpr b) { return MkNum(b, a) * b; } /// Operator overloading for arithmetical operator - public static BoolExpr operator <=(ArithExpr a, ArithExpr b) => a.Context.MkLe(a, b); + public static BoolExpr operator <=(ArithExpr a, ArithExpr b) { return a.Context.MkLe(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator <=(ArithExpr a, int b) => a <= MkNum(a, b); + public static BoolExpr operator <=(ArithExpr a, int b) { return a <= MkNum(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator <=(ArithExpr a, double b) => a <= MkNum(a, b); + public static BoolExpr operator <=(ArithExpr a, double b) { return a <= MkNum(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator <=(int a, ArithExpr b) => MkNum(b, a) <= b; + public static BoolExpr operator <=(int a, ArithExpr b) { return MkNum(b, a) <= b; } /// Operator overloading for arithmetical operator - public static BoolExpr operator <=(double a, ArithExpr b) => MkNum(b, a) <= b; + public static BoolExpr operator <=(double a, ArithExpr b) { return MkNum(b, a) <= b; } /// Operator overloading for arithmetical operator - public static BoolExpr operator <(ArithExpr a, ArithExpr b) => a.Context.MkLt(a, b); + public static BoolExpr operator <(ArithExpr a, ArithExpr b) { return a.Context.MkLt(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator <(ArithExpr a, int b) => a < MkNum(a, b); + public static BoolExpr operator <(ArithExpr a, int b) { return a < MkNum(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator <(ArithExpr a, double b) => a < MkNum(a, b); + public static BoolExpr operator <(ArithExpr a, double b) { return a < MkNum(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator <(int a, ArithExpr b) => MkNum(b, a) < b; + public static BoolExpr operator <(int a, ArithExpr b) { return MkNum(b, a) < b; } /// Operator overloading for arithmetical operator - public static BoolExpr operator <(double a, ArithExpr b) => MkNum(b, a) < b; + public static BoolExpr operator <(double a, ArithExpr b) { return MkNum(b, a) < b; } /// Operator overloading for arithmetical operator - public static BoolExpr operator >(ArithExpr a, ArithExpr b) => a.Context.MkGt(a, b); + public static BoolExpr operator >(ArithExpr a, ArithExpr b) { return a.Context.MkGt(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator >(ArithExpr a, int b) => a > MkNum(a, b); + public static BoolExpr operator >(ArithExpr a, int b) { return a > MkNum(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator >(ArithExpr a, double b) => a > MkNum(a, b); + public static BoolExpr operator >(ArithExpr a, double b) { return a > MkNum(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator >(int a, ArithExpr b) => MkNum(b, a) > b; + public static BoolExpr operator >(int a, ArithExpr b) { return MkNum(b, a) > b; } /// Operator overloading for arithmetical operator - public static BoolExpr operator >(double a, ArithExpr b) => MkNum(b, a) > b; + public static BoolExpr operator >(double a, ArithExpr b) { return MkNum(b, a) > b; } /// Operator overloading for arithmetical operator - public static BoolExpr operator >=(ArithExpr a, ArithExpr b) => a.Context.MkGe(a, b); + public static BoolExpr operator >=(ArithExpr a, ArithExpr b) { return a.Context.MkGe(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator >=(ArithExpr a, int b) => a >= MkNum(a, b); + public static BoolExpr operator >=(ArithExpr a, int b) { return a >= MkNum(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator >=(ArithExpr a, double b) => a >= MkNum(a, b); + public static BoolExpr operator >=(ArithExpr a, double b) { return a >= MkNum(a, b); } /// Operator overloading for arithmetical operator - public static BoolExpr operator >=(int a, ArithExpr b) => MkNum(b, a) >= b; + public static BoolExpr operator >=(int a, ArithExpr b) { return MkNum(b, a) >= b; } /// Operator overloading for arithmetical operator - public static BoolExpr operator >=(double a, ArithExpr b) => MkNum(b, a) >= b; + public static BoolExpr operator >=(double a, ArithExpr b) { return MkNum(b, a) >= b; } #endregion } diff --git a/src/api/dotnet/BoolExpr.cs b/src/api/dotnet/BoolExpr.cs index 5bac65243..b25d99570 100644 --- a/src/api/dotnet/BoolExpr.cs +++ b/src/api/dotnet/BoolExpr.cs @@ -37,18 +37,18 @@ namespace Microsoft.Z3 #region Operators /// Disjunction of Boolean expressions - public static BoolExpr operator|(BoolExpr a, BoolExpr b) => a.Context.MkOr(a, b); + public static BoolExpr operator|(BoolExpr a, BoolExpr b) { return a.Context.MkOr(a, b); } /// /// Conjunction of Boolean expressions /// - public static BoolExpr operator &(BoolExpr a, BoolExpr b) => a.Context.MkAnd(a, b); + public static BoolExpr operator &(BoolExpr a, BoolExpr b) { return a.Context.MkAnd(a, b); } /// Xor of Boolean expressions - public static BoolExpr operator ^(BoolExpr a, BoolExpr b) => a.Context.MkXor(a, b); + public static BoolExpr operator ^(BoolExpr a, BoolExpr b) { return a.Context.MkXor(a, b); } /// Negation - public static BoolExpr operator !(BoolExpr a) => a.Context.MkNot(a); + public static BoolExpr operator !(BoolExpr a) { return a.Context.MkNot(a); } #endregion } From 7cedf79b38ddfec21e8be0786cf2b558f0684d5f Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 26 Apr 2016 00:06:36 +0100 Subject: [PATCH 47/66] [CMake] When building the ".NET" bindings emit ``Microsoft.Z3.dll`` and ``Microsoft.Z3.xml`` to the root build directory rather than ``/src/api/dotnet``. This fixes #573 which makes the behaviour consistent with the Python build system. --- contrib/cmake/src/api/dotnet/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/cmake/src/api/dotnet/CMakeLists.txt b/contrib/cmake/src/api/dotnet/CMakeLists.txt index f077b57ce..fdde298a0 100644 --- a/contrib/cmake/src/api/dotnet/CMakeLists.txt +++ b/contrib/cmake/src/api/dotnet/CMakeLists.txt @@ -206,9 +206,9 @@ endif() # FIXME: The get_property() command only works correctly for single configuration generators # so we can't use it. We also can't use ``$`` because the ``OUTPUT`` # argument to ``add_custom_commands()`` won't accept it. For now just output file to the -# current binary directory. +# root binary directory like the Python build system does. # get_property(Z3_DOTNET_ASSEMBLY_OUTPUT_DIR TARGET libz3 PROPERTY LIBRARY_OUTPUT_DIRECTORY) -set(Z3_DOTNET_ASSEMBLY_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}") +set(Z3_DOTNET_ASSEMBLY_OUTPUT_DIR "${CMAKE_BINARY_DIR}") set(Z3_DOTNET_ASSEMBLY_NAME "Microsoft.Z3.dll") set(Z3_DOTNET_ASSEMBLY_DLL "${Z3_DOTNET_ASSEMBLY_OUTPUT_DIR}/${Z3_DOTNET_ASSEMBLY_NAME}") # csc.exe doesn't work with UNIX style paths so convert to native path From 7213280d9b5544bf82888a8266699d70d55e28f7 Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 26 Apr 2016 00:22:39 +0100 Subject: [PATCH 48/66] [CMake] Emit a warning when configuring to build the ``.NET`` bindings under a multi-configuration generator (e.g. Visual Studio). The warning concerns different generated files clobbering each other. Unfortunately there isn't a clean way to fix this right now. See http://public.kitware.com/pipermail/cmake/2016-March/063101.html --- contrib/cmake/src/api/dotnet/CMakeLists.txt | 26 ++++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/contrib/cmake/src/api/dotnet/CMakeLists.txt b/contrib/cmake/src/api/dotnet/CMakeLists.txt index fdde298a0..e8fa12292 100644 --- a/contrib/cmake/src/api/dotnet/CMakeLists.txt +++ b/contrib/cmake/src/api/dotnet/CMakeLists.txt @@ -203,11 +203,23 @@ if (DOTNET_TOOLCHAIN_IS_WINDOWS) endif() endif() -# FIXME: The get_property() command only works correctly for single configuration generators -# so we can't use it. We also can't use ``$`` because the ``OUTPUT`` -# argument to ``add_custom_commands()`` won't accept it. For now just output file to the -# root binary directory like the Python build system does. -# get_property(Z3_DOTNET_ASSEMBLY_OUTPUT_DIR TARGET libz3 PROPERTY LIBRARY_OUTPUT_DIRECTORY) +# FIXME: Ideally we should emit files into a configuration specific directory +# when using multi-configuration generators so that the files generated by each +# configuration don't clobber each other. Unfortunately the ``get_property()`` +# command only works correctly for single configuration generators so we can't +# use it. We also can't use ``$`` because the ``OUTPUT`` +# argument to ``add_custom_commands()`` won't accept it. +# See http://public.kitware.com/pipermail/cmake/2016-March/063101.html +# +# For now just output file to the root binary directory like the Python build +# system does and emit a warning when appropriate. +if (DEFINED CMAKE_CONFIGURATION_TYPES) + # Multi-configuration build (e.g. Visual Studio and Xcode). + message(WARNING "You are using a multi-configuration generator. The build rules for" + " the \".NET\" bindings currently do not emit files per configuration so previously" + " generated files for other configurations will be overwritten.") +endif() + set(Z3_DOTNET_ASSEMBLY_OUTPUT_DIR "${CMAKE_BINARY_DIR}") set(Z3_DOTNET_ASSEMBLY_NAME "Microsoft.Z3.dll") set(Z3_DOTNET_ASSEMBLY_DLL "${Z3_DOTNET_ASSEMBLY_OUTPUT_DIR}/${Z3_DOTNET_ASSEMBLY_NAME}") @@ -252,7 +264,9 @@ if (DOTNET_TOOLCHAIN_IS_MONO) # to find the assembly install(FILES "${Z3_DOTNET_PKGCONFIG_FILE}" DESTINATION "${CMAKE_INSTALL_PKGCONFIGDIR}") - # Configure the install and uninstall scripts + # Configure the install and uninstall scripts. + # Note: If multi-configuration generator support is ever fixed then these + # scripts will be broken. configure_file(cmake_install_gac.cmake.in cmake_install_gac.cmake @ONLY) configure_file(cmake_uninstall_gac.cmake.in cmake_uninstall_gac.cmake @ONLY) From 626e0736d23df44b591c08edc7c77268292c3b0e Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 26 Apr 2016 08:47:52 +0100 Subject: [PATCH 49/66] [CMake] Implement installation of ".NET" bindings on Windows. We don't install Z3_DOTNET_ASSEMBLY_DLL into the gac. Instead we just copy into installation directory. There are several reasons for this: * We can't install the Z3_DOTNET_ASSEMBLY_DLL into the gac in a portable way like we can with mono (i.e. the ``-root`` flag). * It isn't best practice to use ``gacutil.exe`` on Windows to install into the GAC, see https://msdn.microsoft.com/en-us/library/yf1d93sz(v=vs.110).aspx . Taking this approach should be sufficient because we can now do something like this ``` mkdir build cmake -G Ninja -DCMAKE_INSTALL_PREFIX= ../ ninja mkdir ninja install ``` and then put the contents of into a zip file which creates a redistributable zip file for Windows. --- contrib/cmake/src/api/dotnet/CMakeLists.txt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contrib/cmake/src/api/dotnet/CMakeLists.txt b/contrib/cmake/src/api/dotnet/CMakeLists.txt index e8fa12292..7440f021b 100644 --- a/contrib/cmake/src/api/dotnet/CMakeLists.txt +++ b/contrib/cmake/src/api/dotnet/CMakeLists.txt @@ -282,9 +282,10 @@ if (DOTNET_TOOLCHAIN_IS_MONO) add_dependencies(uninstall remove_dotnet_dll_from_gac) elseif(DOTNET_TOOLCHAIN_IS_WINDOWS) - # FIXME: This isn't implemented because I'm not sure how/if the assembly should - # be installed to the GAC. - message(WARNING "Install of .NET bindings is not implemented for Windows") + # Don't install Z3_DOTNET_ASSEMBLY_DLL into the gac. Instead just copy into + # installation directory. + install(FILES "${Z3_DOTNET_ASSEMBLY_DLL}" DESTINATION "${CMAKE_INSTALL_LIBDIR}") + install(FILES "${Z3_DOTNET_ASSEMBLY_DLL_DOC}" DESTINATION "${CMAKE_INSTALL_LIBDIR}") else() message(FATAL_ERROR "Unknown .NET toolchain") endif() From fd7b8fe1ab8130e483a7c598302858f00a461cad Mon Sep 17 00:00:00 2001 From: Dan Liew Date: Tue, 26 Apr 2016 11:49:46 +0100 Subject: [PATCH 50/66] [CMake] On Windows fix the ``install`` target so that it installs ``libz3.dll``. I've left a comment about the installation of ``libz3.lib``. I'm not sure if we want that installed or not. --- contrib/cmake/src/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contrib/cmake/src/CMakeLists.txt b/contrib/cmake/src/CMakeLists.txt index 13403afb1..1ce1a76cd 100644 --- a/contrib/cmake/src/CMakeLists.txt +++ b/contrib/cmake/src/CMakeLists.txt @@ -166,7 +166,8 @@ endforeach() install(TARGETS libz3 LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" # On Windows this installs ``libz3.lib`` which CMake calls the "corresponding import library". Do we want this installed? + RUNTIME DESTINATION "${CMAKE_INSTALL_LIBDIR}" # For Windows. DLLs are runtime targets for CMake PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" ) From 6455bf81149990582a7de1040ccdb2ee2fcfb577 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Tue, 26 Apr 2016 21:13:02 +0100 Subject: [PATCH 51/66] New implementation for mpf_manager::rem. Relates to #561 --- src/util/mpf.cpp | 68 +++++++++++++++++++++++++++++++++++++++++------- src/util/mpf.h | 1 + 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 0af5352cc..07ccd3c7b 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -17,6 +17,7 @@ Revision History: --*/ #include +#include #include"mpf.h" mpf::mpf() : @@ -1230,18 +1231,56 @@ void mpf_manager::rem(mpf const & x, mpf const & y, mpf & o) { else if (is_zero(x)) set(o, x); else { - // r = x - y * n - scoped_mpf nr(*this), n(*this), yn(*this); - div(MPF_ROUND_NEAREST_TEVEN, x, y, nr); - round_to_integral(MPF_ROUND_NEAREST_TEVEN, nr, n); - mul(MPF_ROUND_NEAREST_TEVEN, y, n, yn); - sub(MPF_ROUND_NEAREST_TEVEN, x, yn, o); + // This is a generalized version of the algorithm for FPREM1 in the `Intel + // 64 and IA-32 Architectures Software Developer’s Manual', + // Section 3-402 Vol. 2A `FPREM1-Partial Remainder'. + scoped_mpf ST0(*this), ST1(*this); + set(ST0, x); + set(ST1, y); + const mpf_exp_t B = x.sbits-1; // max bits per iteration. + mpf_exp_t D; + do { + D = ST0.exponent() - ST1.exponent(); + TRACE("mpf_dbg_rem", tout << "st0=" << to_string_hexfloat(ST0) << std::endl; + tout << "st1=" << to_string_hexfloat(ST1) << std::endl; + tout << "D=" << D << std::endl;); + + if (D < B) { + scoped_mpf ST0_DIV_ST1(*this), Q(*this), ST1_MUL_Q(*this), ST0p(*this); + div(MPF_ROUND_NEAREST_TEVEN, ST0, ST1, ST0_DIV_ST1); + round_to_integral(MPF_ROUND_NEAREST_TEVEN, ST0_DIV_ST1, Q); + mul(MPF_ROUND_NEAREST_TEVEN, ST1, Q, ST1_MUL_Q); + sub(MPF_ROUND_NEAREST_TEVEN, ST0, ST1_MUL_Q, ST0p); + TRACE("mpf_dbg_rem", tout << "ST0/ST1=" << to_string_hexfloat(ST0_DIV_ST1) << std::endl; + tout << "Q=" << to_string_hexfloat(Q) << std::endl; + tout << "ST1*Q=" << to_string_hexfloat(ST1_MUL_Q) << std::endl; + tout << "ST0'=" << to_string_hexfloat(ST0p) << std::endl;); + set(ST0, ST0p); + } + else { + const mpf_exp_t N = B; + scoped_mpf ST0_DIV_ST1(*this), QQ(*this), ST1_MUL_QQ(*this), ST0p(*this); + div(MPF_ROUND_TOWARD_ZERO, ST0, ST1, ST0_DIV_ST1); + ST0_DIV_ST1.get().exponent -= D - N; + round_to_integral(MPF_ROUND_TOWARD_ZERO, ST0_DIV_ST1, QQ); + mul(MPF_ROUND_NEAREST_TEVEN, ST1, QQ, ST1_MUL_QQ); + ST1_MUL_QQ.get().exponent += D - N; + sub(MPF_ROUND_NEAREST_TEVEN, ST0, ST1_MUL_QQ, ST0p); + TRACE("mpf_dbg_rem", tout << "ST0/ST1/2^{D-N}=" << to_string_hexfloat(ST0_DIV_ST1) << std::endl; + tout << "QQ=" << to_string_hexfloat(QQ) << std::endl; + tout << "ST1*QQ*2^{D-N}=" << to_string_hexfloat(ST1_MUL_QQ) << std::endl; + tout << "ST0'=" << to_string_hexfloat(ST0p) << std::endl;); + SASSERT(!eq(ST0, ST0p)); + set(ST0, ST0p); + } + + SASSERT(ST0.exponent() - ST1.exponent() <= D); + } while (D >= B); + + set(o, ST0); if (is_zero(o)) o.sign = x.sign; - - TRACE("mpf_dbg", tout << "N = " << to_string(n) << std::endl;); - TRACE("mpf_dbg", tout << "YN = " << to_string(yn) << std::endl;); } TRACE("mpf_dbg", tout << "REMAINDER = " << to_string(o) << std::endl;); @@ -1326,7 +1365,7 @@ std::string mpf_manager::to_string(mpf const & x) { } //DEBUG_CODE( - // res += " " + to_string_raw(x); + // res += " " + to_string_hexfloat(x); //); return res; @@ -1368,6 +1407,15 @@ std::string mpf_manager::to_string_raw(mpf const & x) { return res; } +std::string mpf_manager::to_string_hexfloat(mpf const & x) { + std::stringstream ss(""); + std::ios::fmtflags ff = ss.setf(std::ios_base::hex | std::ios_base::uppercase | + std::ios_base::showpoint | std::ios_base::showpos); + ss.precision(13); + ss << std::hexfloat << to_double(x); + return ss.str(); +} + void mpf_manager::to_rational(mpf const & x, unsynch_mpq_manager & qm, mpq & o) { scoped_mpf a(*this); scoped_mpz n(m_mpq_manager), d(m_mpq_manager); diff --git a/src/util/mpf.h b/src/util/mpf.h index 101491e3b..1bd0ac952 100644 --- a/src/util/mpf.h +++ b/src/util/mpf.h @@ -186,6 +186,7 @@ public: void mk_ninf(unsigned ebits, unsigned sbits, mpf & o); std::string to_string_raw(mpf const & a); + std::string to_string_hexfloat(mpf const & a); unsynch_mpz_manager & mpz_manager(void) { return m_mpz_manager; } unsynch_mpq_manager & mpq_manager(void) { return m_mpq_manager; } From a1aa166ef56983ed284d071bc4ffd7b42dec5da7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 26 Apr 2016 17:15:24 -0700 Subject: [PATCH 52/66] adding local optimization to qsat Signed-off-by: Nikolaj Bjorner --- src/qe/qe_arith.cpp | 255 +++++++++++++++++++++++++++++++++++++++++++- src/qe/qe_arith.h | 1 + src/qe/qe_mbp.cpp | 9 ++ src/qe/qe_mbp.h | 11 ++ src/qe/qsat.cpp | 183 +++++++++++++++++++++++-------- src/qe/qsat.h | 13 +-- 6 files changed, 414 insertions(+), 58 deletions(-) diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index a3fcda6de..d128a4fe9 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -20,6 +20,7 @@ Revision History: --*/ #include "qe_arith.h" +#include "qe_mbp.h" #include "ast_util.h" #include "arith_decl_plugin.h" #include "ast_pp.h" @@ -49,14 +50,185 @@ namespace qe { } return is_divides(a, e1, e2, k, t) || is_divides(a, e2, e1, k, t); } + + enum ineq_type { + t_eq, + t_lt, + t_le + }; + + struct tableau { + struct var { + unsigned m_id; + rational m_coeff; + var(unsigned id, rational const& c): m_id(id), m_coeff(c) {} + }; + struct row { + vector m_vars; // variables with coefficients + rational m_coeff; // constant in inequality + ineq_type m_type; // inequality type + rational m_value; // value of m_vars + m_coeff under interpretation of m_var2value. + bool m_alive; // rows can be marked dead if they have been processed. + }; + vector m_rows; + vector m_var2rows; + vector m_var2value; + row m_objective; + + void invariant() { + // variables in each row are sorted. + } + + mbp::bound_type maximize(rational& value) { + // tbd + return mbp::unbounded; + } + + rational get_coefficient(unsigned row_id, unsigned var_id) { + row const& r = m_rows[row_id]; + unsigned lo = 0, hi = r.m_vars.size(); + while (lo < hi) { + unsigned mid = lo + (hi - lo)/2; + SASSERT(mid < hi); + unsigned id = r.m_vars[mid].m_id; + if (id == var_id) { + lo = mid; + break; + } + if (id < var_id) { + lo = mid + 1; + } + else { + hi = mid - 1; + } + } + unsigned id = r.m_vars[lo].m_id; + if (id == var_id) { + return r.m_vars[lo].m_coeff; + } + else { + return rational::zero(); + } + } + + void resolve(unsigned row_id1, unsigned row_id2, unsigned x) { + // row1 is of the form a1*x + t1 <~ 0 + // row2 is of the form a2*x + t2 <~ 0 + // assume that a1, a2 have the same sign. + // if a1 is positive, then val(t1*a2/a1) <= val(t2*a1/a2) + // replace row2 with the new inequality of the form: + // t1 - a1*t2/a2 <~~ 0 + // where <~~ is strict if either <~1 or <~2 is strict. + // if a1 is negative, then .... + // + } + + void multiply(rational const& c, unsigned row_id) { + if (c.is_one()) { + return; + } + row& r = m_rows[row_id]; + SASSERT(r.m_alive); + for (unsigned i = 0; i < r.m_vars.size(); ++i) { + r.m_vars[i].m_coeff *= c; + } + r.m_coeff *= c; + r.m_value *= c; + } + + // subtract row2 from row1, store result in row2 + + vector m_new_vars; + + void subtract(unsigned row_id1, unsigned row_id2) { + m_new_vars.reset(); + row const& r1 = m_rows[row_id1]; + row& r2 = m_rows[row_id2]; + unsigned i = 0, j = 0; + for(; i < r1.m_vars.size() || j < r2.m_vars.size(); ) { + if (j == r2.m_vars.size()) { + for (; i < r1.m_vars.size(); ++i) { + m_new_vars.push_back(r1.m_vars[i]); + m_var2rows[r1.m_vars[i].m_id].push_back(row_id2); + } + } + else if (i == r1.m_vars.size()) { + for (; j < r2.m_vars.size(); ++j) { + m_new_vars.push_back(r2.m_vars[j]); + m_new_vars.back().m_coeff.neg(); + } + } + else { + unsigned v1 = r1.m_vars[i].m_id; + unsigned v2 = r2.m_vars[j].m_id; + if (v1 == v2) { + m_new_vars.push_back(r1.m_vars[i]); + m_new_vars.back().m_coeff -= r2.m_vars[j].m_coeff; + ++i; + ++j; + if (m_new_vars.back().m_coeff.is_zero()) { + m_new_vars.pop_back(); + } + } + else if (v1 < v2) { + m_new_vars.push_back(r1.m_vars[i]); + m_var2rows[r1.m_vars[i].m_id].push_back(row_id2); + ++i; + } + else { + m_new_vars.push_back(r2.m_vars[j]); + m_new_vars.back().m_coeff.neg(); + ++j; + } + } + } + r2.m_coeff.neg(); + r2.m_coeff += r1.m_coeff; + r2.m_vars.swap(m_new_vars); + r2.m_value.neg(); + r2.m_value += r1.m_value; + if (r1.m_type == t_lt) { + r2.m_type = t_lt; + } + } + + void display(std::ostream& out) const { + for (unsigned i = 0; i < m_rows.size(); ++i) { + display(out, m_rows[i]); + } + } + + void display(std::ostream& out, row const& r) const { + vector const& vars = r.m_vars; + for (unsigned i = 0; i < vars.size(); ++i) { + if (i > 0 && vars[i].m_coeff.is_pos()) { + out << "+ "; + } + out << vars[i].m_coeff << "* v" << vars[i].m_id << " "; + } + out << r.m_coeff; + switch (r.m_type) { + case t_eq: + out << " = 0\n"; + break; + case t_lt: + out << " < 0\n"; + break; + case t_le: + out << " <= 0\n"; + break; + } + } + }; + +#if 0 + obj_map m_expr2var; + ptr_vector m_var2expr; + +#endif struct arith_project_plugin::imp { - enum ineq_type { - t_eq, - t_lt, - t_le - }; ast_manager& m; arith_util a; th_rewriter m_rw; @@ -84,6 +256,62 @@ namespace qe { } } + void insert_mul(expr* x, rational const& v, obj_map& ts) + { + rational w; + if (ts.find(x, w)) { + ts.insert(x, w + v); + } + else { + ts.insert(x, v); + } + } + + void linearize(model& model, rational const& mul, expr* t, rational& c, obj_map& ts) { + expr* t1, *t2, *t3; + rational mul1; + expr_ref val(m); + if (a.is_mul(t, t1, t2) && is_numeral(model, t1, mul1)) { + linearize(model, mul* mul1, t2, c, ts); + } + else if (a.is_mul(t, t1, t2) && is_numeral(model, t2, mul1)) { + linearize(model, mul* mul1, t1, c, ts); + } + else if (a.is_add(t)) { + app* ap = to_app(t); + for (unsigned i = 0; i < ap->get_num_args(); ++i) { + linearize(model, mul, ap->get_arg(i), c, ts); + } + } + else if (a.is_sub(t, t1, t2)) { + linearize(model, mul, t1, c, ts); + linearize(model, -mul, t2, c, ts); + } + else if (a.is_uminus(t, t1)) { + linearize(model, -mul, t1, c, ts); + } + else if (a.is_numeral(t, mul1)) { + c += mul*mul1; + } + else if (extract_mod(model, t, val)) { + insert_mul(val, mul, ts); + } + else if (m.is_ite(t, t1, t2, t3)) { + VERIFY(model.eval(t1, val)); + SASSERT(m.is_true(val) || m.is_false(val)); + TRACE("qe", tout << mk_pp(t1, m) << " := " << val << "\n";); + if (m.is_true(val)) { + linearize(model, mul, t2, c, ts); + } + else { + linearize(model, mul, t3, c, ts); + } + } + else { + insert_mul(t, mul, ts); + } + } + void is_linear(model& model, rational const& mul, expr* t, rational& c, expr_ref_vector& ts) { expr* t1, *t2, *t3; rational mul1; @@ -853,6 +1081,19 @@ namespace qe { } return true; } + + mbp::bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { + obj_map ts; + rational c(0), mul(1); + linearize(mdl, mul, t, c, ts); + + + // pick variables one by one from ts. + // m_var = alloc(contains_app, m, v); + // perform upper or lower projection depending on sign of v. + // + return mbp::unbounded; + } }; arith_project_plugin::arith_project_plugin(ast_manager& m) { @@ -875,6 +1116,10 @@ namespace qe { return m_imp->a.get_family_id(); } + mbp::bound_type arith_project_plugin::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { + return m_imp->maximize(fmls, mdl, t, value, bound); + } + bool arith_project(model& model, app* var, expr_ref_vector& lits) { ast_manager& m = lits.get_manager(); arith_project_plugin ap(m); diff --git a/src/qe/qe_arith.h b/src/qe/qe_arith.h index be5415a45..9ba3fa5fc 100644 --- a/src/qe/qe_arith.h +++ b/src/qe/qe_arith.h @@ -29,6 +29,7 @@ namespace qe { virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits); virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); virtual family_id get_family_id(); + mbp::bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound); }; bool arith_project(model& model, app* var, expr_ref_vector& lits); diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index 3d9046a4e..a3d8d077c 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -213,6 +213,11 @@ class mbp::impl { public: + mbp::bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { + arith_project_plugin arith(m); + return arith.maximize(fmls, mdl, t, value, bound); + } + void extract_literals(model& model, expr_ref_vector& fmls) { expr_ref val(m); for (unsigned i = 0; i < fmls.size(); ++i) { @@ -415,3 +420,7 @@ void mbp::solve(model& model, app_ref_vector& vars, expr_ref_vector& fmls) { void mbp::extract_literals(model& model, expr_ref_vector& lits) { m_impl->extract_literals(model, lits); } + +mbp::bound_type mbp::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { + return m_impl->maximize(fmls, mdl, t, value, bound); +} diff --git a/src/qe/qe_mbp.h b/src/qe/qe_mbp.h index 5b4c59762..2fc5a9fb9 100644 --- a/src/qe/qe_mbp.h +++ b/src/qe/qe_mbp.h @@ -70,6 +70,17 @@ namespace qe { Extract literals from formulas based on model. */ void extract_literals(model& model, expr_ref_vector& lits); + + /** + \brief + Maximize objective t under current model for constraints in fmls. + */ + enum bound_type { + unbounded, + strict, + non_strict + }; + bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound); }; } diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index 01a429038..3c3a5c0dd 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -536,6 +536,13 @@ namespace qe { ); } }; + + enum qsat_mode { + qsat_qe, + qsat_qe_rec, + qsat_sat, + qsat_maximize + }; class qsat : public tactic { @@ -559,8 +566,7 @@ namespace qe { vector m_vars; // variables from alternating prefixes. unsigned m_level; model_ref m_model; - bool m_qelim; // perform quantifier elimination - bool m_force_elim; // force elimination of variables during projection. + qsat_mode m_mode; app_ref_vector m_avars; // variables to project app_ref_vector m_free_vars; @@ -584,12 +590,12 @@ namespace qe { SASSERT(validate_model(asms)); TRACE("qe", k.display(tout); display(tout << "\n", *m_model.get()); display(tout, asms); ); push(); - break; + break; case l_false: switch (m_level) { case 0: return l_false; case 1: - if (!m_qelim) return l_true; + if (m_mode == qsat_sat) return l_true; if (m_model.get()) { project_qe(asms); } @@ -672,7 +678,7 @@ namespace qe { m_pred_abs.get_free_vars(fml, vars); m_vars.push_back(vars); vars.reset(); - if (m_qelim) { + if (m_mode != qsat_sat) { is_forall = true; hoist.pull_quantifier(is_forall, fml, vars); m_vars.push_back(vars); @@ -858,12 +864,18 @@ namespace qe { get_core(core, m_level); SASSERT(validate_core(core)); get_vars(m_level); - m_mbp(m_force_elim, m_avars, mdl, core); - fml = negate_core(core); - add_assumption(fml); - m_answer.push_back(fml); - m_free_vars.append(m_avars); - pop(1); + m_mbp(force_elim(), m_avars, mdl, core); + if (m_mode == qsat_maximize) { + maximize(core, mdl); + pop(1); + } + else { + fml = negate_core(core); + add_assumption(fml); + m_answer.push_back(fml); + m_free_vars.append(m_avars); + pop(1); + } } void project(expr_ref_vector& core) { @@ -878,7 +890,7 @@ namespace qe { get_vars(m_level-1); SASSERT(validate_project(mdl, core)); - m_mbp(m_force_elim, m_avars, mdl, core); + m_mbp(force_elim(), m_avars, mdl, core); m_free_vars.append(m_avars); fml = negate_core(core); unsigned num_scopes = 0; @@ -889,7 +901,7 @@ namespace qe { if (level.max() == UINT_MAX) { num_scopes = 2*(m_level/2); } - else if (m_qelim && !m_force_elim) { + else if (m_mode == qsat_qe_rec) { num_scopes = 2; } else { @@ -903,7 +915,7 @@ namespace qe { pop(num_scopes); TRACE("qe", tout << "backtrack: " << num_scopes << " new level: " << m_level << "\nproject:\n" << core << "\n|->\n" << fml << "\n";); - if (m_level == 0 && m_qelim) { + if (m_level == 0 && m_mode != qsat_sat) { add_assumption(fml); } else { @@ -919,9 +931,13 @@ namespace qe { } } - expr_ref negate_core(expr_ref_vector& core) { + expr_ref negate_core(expr_ref_vector const& core) { return ::push_not(::mk_and(core)); } + + bool force_elim() const { + return m_mode != qsat_qe_rec; + } expr_ref elim_rec(expr* fml) { expr_ref tmp(m); @@ -1135,7 +1151,7 @@ namespace qe { public: - qsat(ast_manager& m, params_ref const& p, bool qelim, bool force_elim): + qsat(ast_manager& m, params_ref const& p, qsat_mode mode): m(m), m_mbp(m), m_fa(m), @@ -1144,10 +1160,10 @@ namespace qe { m_answer(m), m_asms(m), m_level(0), - m_qelim(qelim), - m_force_elim(force_elim), + m_mode(mode), m_avars(m), - m_free_vars(m) + m_free_vars(m), + m_value(m) { reset(); } @@ -1182,7 +1198,7 @@ namespace qe { // fail if cores. (TBD) // fail if proofs. (TBD) - if (!m_force_elim) { + if (m_mode == qsat_qe_rec) { fml = elim_rec(fml); in->reset(); in->inc_depth(); @@ -1193,7 +1209,7 @@ namespace qe { reset(); TRACE("qe", tout << fml << "\n";); - if (m_qelim) { + if (m_mode != qsat_sat) { fml = push_not(fml); } hoist(fml); @@ -1211,11 +1227,12 @@ namespace qe { case l_false: in->reset(); in->inc_depth(); - if (m_qelim) { + if (m_mode == qsat_qe) { fml = ::mk_and(m_answer); in->assert_expr(fml); } else { + SASSERT(m_mode == qsat_sat); in->assert_expr(m.mk_false()); } result.push_back(in.get()); @@ -1262,12 +1279,110 @@ namespace qe { } tactic * translate(ast_manager & m) { - return alloc(qsat, m, m_params, m_qelim, m_force_elim); - } + return alloc(qsat, m, m_params, m_mode); + } + + app* m_objective; + expr_ref m_value; + mbp::bound_type m_bound; + bool m_was_sat; + + lbool maximize(expr_ref_vector const& fmls, app* t, expr_ref& value, mbp::bound_type& bound) { + expr_ref_vector defs(m); + expr_ref fml = negate_core(fmls); + hoist(fml); + m_objective = t; + m_value = 0; + m_bound = mbp::unbounded; + m_was_sat = false; + m_pred_abs.abstract_atoms(fml, defs); + fml = m_pred_abs.mk_abstract(fml); + m_ex.assert_expr(mk_and(defs)); + m_fa.assert_expr(mk_and(defs)); + m_ex.assert_expr(fml); + m_fa.assert_expr(m.mk_not(fml)); + lbool is_sat = check_sat(); + switch (is_sat) { + case l_false: + if (!m_was_sat) { + return l_false; + } + break; + case l_true: + UNREACHABLE(); + break; + case l_undef: + std::string s = m_ex.k().last_failure_as_string(); + if (s == "ok") { + s = m_fa.k().last_failure_as_string(); + } + throw tactic_exception(s.c_str()); + } + value = m_value; + bound = m_bound; + return l_true; + } + + void maximize(expr_ref_vector const& core, model& mdl) { + TRACE("qe", tout << "maximize: " << core << "\n";); + m_was_sat |= !core.empty(); + if (core.empty()) { + m_ex.assert_expr(m.mk_false()); + m_fa.assert_expr(m.mk_false()); + return; + } + expr_ref bound(m); + m_bound = m_mbp.maximize(core, mdl, m_objective, m_value, bound); + switch (m_bound) { + case mbp::unbounded: + m_ex.assert_expr(m.mk_false()); + m_fa.assert_expr(m.mk_false()); + break; + case mbp::strict: + m_ex.assert_expr(bound); + break; + case mbp::non_strict: + m_ex.assert_expr(bound); + break; + } + } + }; - + lbool maximize(expr_ref_vector const& fmls, app* t, expr_ref& value, mbp::bound_type& bound, params_ref const& p) { + ast_manager& m = fmls.get_manager(); + qsat qs(m, p, qsat_maximize); + return qs.maximize(fmls, t, value, bound); + } +}; +tactic * mk_qsat_tactic(ast_manager& m, params_ref const& p) { + return alloc(qe::qsat, m, p, qe::qsat_sat); +} + +tactic * mk_qe2_tactic(ast_manager& m, params_ref const& p) { + return alloc(qe::qsat, m, p, qe::qsat_qe); +} + +tactic * mk_qe_rec_tactic(ast_manager& m, params_ref const& p) { + return alloc(qe::qsat, m, p, qe::qsat_qe_rec); +} + + + + +#if 0 + + class min_max_opt { + struct imp; + imp* m_imp; + public: + min_max_opt(ast_manager& m); + ~min_max_opt(); + void add(expr* e); + void add(expr_ref_vector const& fmls); + lbool check(svector const& is_max, app_ref_vector const& vars, app* t); + }; struct min_max_opt::imp { ast_manager& m; @@ -1346,20 +1461,4 @@ namespace qe { return m_imp->check(is_max, vars, t); } - - -}; - -tactic * mk_qsat_tactic(ast_manager& m, params_ref const& p) { - return alloc(qe::qsat, m, p, false, true); -} - -tactic * mk_qe2_tactic(ast_manager& m, params_ref const& p) { - return alloc(qe::qsat, m, p, true, true); -} - -tactic * mk_qe_rec_tactic(ast_manager& m, params_ref const& p) { - return alloc(qe::qsat, m, p, true, false); -} - - +#endif diff --git a/src/qe/qsat.h b/src/qe/qsat.h index 5c6b80a04..dfba75e6c 100644 --- a/src/qe/qsat.h +++ b/src/qe/qsat.h @@ -23,6 +23,7 @@ Revision History: #include "tactic.h" #include "filter_model_converter.h" +#include "qe_mbp.h" namespace qe { @@ -113,17 +114,7 @@ namespace qe { void collect_statistics(statistics& st) const; }; - class min_max_opt { - struct imp; - imp* m_imp; - public: - min_max_opt(ast_manager& m); - ~min_max_opt(); - void add(expr* e); - void add(expr_ref_vector const& fmls); - lbool check(svector const& is_max, app_ref_vector const& vars, app* t); - }; - + lbool maximize(expr_ref_vector const& fmls, app* t, expr_ref& value, mbp::bound_type& bound, params_ref const& p); } From 68c7d64d00e8c31eba34eecb5611fe104af31697 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 27 Apr 2016 11:18:20 -0700 Subject: [PATCH 53/66] adding model-based opt facility Signed-off-by: Nikolaj Bjorner --- contrib/cmake/src/math/simplex/CMakeLists.txt | 1 + src/api/dotnet/ArithExpr.cs | 3 + src/api/dotnet/BoolExpr.cs | 4 +- src/math/simplex/model_based_opt.cpp | 307 ++++++++++++++++++ src/math/simplex/model_based_opt.h | 102 ++++++ src/qe/qe_arith.cpp | 245 +++----------- src/qe/qe_arith.h | 2 +- src/qe/qe_mbp.cpp | 4 +- src/qe/qe_mbp.h | 8 +- src/qe/qsat.cpp | 14 +- src/qe/qsat.h | 2 +- 11 files changed, 465 insertions(+), 227 deletions(-) create mode 100644 src/math/simplex/model_based_opt.cpp create mode 100644 src/math/simplex/model_based_opt.h diff --git a/contrib/cmake/src/math/simplex/CMakeLists.txt b/contrib/cmake/src/math/simplex/CMakeLists.txt index 6f965919d..de55f1634 100644 --- a/contrib/cmake/src/math/simplex/CMakeLists.txt +++ b/contrib/cmake/src/math/simplex/CMakeLists.txt @@ -1,6 +1,7 @@ z3_add_component(simplex SOURCES simplex.cpp + model_based_opt.cpp COMPONENT_DEPENDENCIES util ) diff --git a/src/api/dotnet/ArithExpr.cs b/src/api/dotnet/ArithExpr.cs index 705a9f6ca..b6beaef0c 100644 --- a/src/api/dotnet/ArithExpr.cs +++ b/src/api/dotnet/ArithExpr.cs @@ -60,6 +60,9 @@ namespace Microsoft.Z3 /// Operator overloading for arithmetical operator public static ArithExpr operator /(double a, ArithExpr b) { return MkNum(b, a) / b; } + /// Operator overloading for arithmetical operator + public static ArithExpr operator -(ArithExpr a) { return a.Context.MkUnaryMinus(a); } + /// Operator overloading for arithmetical operator public static ArithExpr operator -(ArithExpr a, ArithExpr b) { return a.Context.MkSub(a, b); } diff --git a/src/api/dotnet/BoolExpr.cs b/src/api/dotnet/BoolExpr.cs index b25d99570..c52109352 100644 --- a/src/api/dotnet/BoolExpr.cs +++ b/src/api/dotnet/BoolExpr.cs @@ -39,9 +39,7 @@ namespace Microsoft.Z3 /// Disjunction of Boolean expressions public static BoolExpr operator|(BoolExpr a, BoolExpr b) { return a.Context.MkOr(a, b); } - /// - /// Conjunction of Boolean expressions - /// + /// Conjunction of Boolean expressions public static BoolExpr operator &(BoolExpr a, BoolExpr b) { return a.Context.MkAnd(a, b); } /// Xor of Boolean expressions diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp new file mode 100644 index 000000000..10008345a --- /dev/null +++ b/src/math/simplex/model_based_opt.cpp @@ -0,0 +1,307 @@ +/*++ +Copyright (c) 2016 Microsoft Corporation + +Module Name: + + model_based_opt.cpp + +Abstract: + + Model-based optimization for linear real arithmetic. + +Author: + + Nikolaj Bjorner (nbjorner) 2016-27-4 + +Revision History: + + +--*/ + +#include "model_based_opt.h" + +namespace opt { + + + bool model_based_opt::invariant() { + // variables in each row are sorted. + for (unsigned i = 0; i < m_rows.size(); ++i) { + if (!invariant(m_rows[i])) { + return false; + } + } + return invariant(m_objective); + } + + bool model_based_opt::invariant(row const& r) { + rational val = r.m_coeff; + vector const& vars = r.m_vars; + for (unsigned i = 0; i < vars.size(); ++i) { + var const& v = vars[i]; + SASSERT(i + 1 == vars.size() || v.m_id < vars[i+1].m_id); + SASSERT(!v.m_coeff.is_zero()); + val += v.m_coeff * m_var2value[v.m_id]; + } + SASSERT(val == r.m_value); + SASSERT(r.m_type != t_eq || val.is_zero()); + SASSERT(r.m_type != t_lt || val.is_neg()); + SASSERT(r.m_type != t_le || !val.is_pos()); + return true; + } + + // a1*x + obj + // a2*x + t2 <= 0 + // a3*x + t3 <= 0 + // a4*x + t4 <= 0 + // a1 > 0, a2 > 0, a3 > 0, a4 < 0 + // x <= -t2/a2 + // x <= -t2/a3 + // determine lub among these. + // then resolve lub with others + // e.g., -t2/a2 <= -t3/a3, then + // replace inequality a3*x + t3 <= 0 by -t2/a2 + t3/a3 <= 0 + // mark a4 as invalid. + // + + // a1 < 0, a2 < 0, a3 < 0, a4 > 0 + // x >= t2/a2 + // x >= t3/a3 + // determine glb among these + // the resolve glb with others. + // e.g. t2/a2 >= t3/a3 + // then replace a3*x + t3 by t3/a3 - t2/a2 <= 0 + // + bound_type model_based_opt::maximize(rational& value) { + // tbd + SASSERT(invariant()); + vector & vars = m_objective.m_vars; + unsigned_vector other; + while (!vars.empty()) { + var const& v = vars.back(); + unsigned x = v.m_id; + rational const& coeff = v.m_coeff; + rational const& x_val = m_var2value[x]; + unsigned_vector const& row_ids = m_var2row_ids[x]; + unsigned bound_index; + other.reset(); + if (find_bound(x, bound_index, other, coeff.is_pos())) { + rational bound_coeff = m_rows[bound_index].m_coeff; + for (unsigned i = 0; i < other.size(); ++i) { + resolve(other[i], bound_coeff, bound_index, x); + } + // coeff*x + objective -> coeff*(bound) + objective + // tbd: + multiply(coeff/bound_coeff, bound_index); + //add(m_objective_id, bound_index); + m_rows[bound_index].m_alive = false; + } + else { + return unbounded; + } + } + value = m_objective.m_coeff; + switch (m_objective.m_type) { + case t_lt: return strict; + case t_le: return non_strict; + case t_eq: return non_strict; + } + return non_strict; + } + + bool model_based_opt::find_bound(unsigned x, unsigned& bound_index, unsigned_vector& other, bool is_pos) { + bound_index = UINT_MAX; + rational lub_val; + rational const& x_val = m_var2value[x]; + unsigned_vector const& row_ids = m_var2row_ids[x]; + for (unsigned i = 0; i < row_ids.size(); ++i) { + unsigned row_id = row_ids[i]; + row& r = m_rows[row_id]; + if (r.m_alive) { + rational a = get_coefficient(row_id, x); + if (a.is_pos() == is_pos) { + rational value = r.m_value - x_val*a; // r.m_value = val_x*a + val(t), val(t) := r.m_value - val_x*a; + if (bound_index == UINT_MAX) { + lub_val = value; + bound_index = row_id; + } + else if ((is_pos && value < lub_val) || (!is_pos && value > lub_val)) { + other.push_back(bound_index); + lub_val = value; + bound_index = row_id; + } + else { + other.push_back(bound_index); + } + } + else if (!a.is_zero()) { + r.m_alive = false; + } + } + } + return bound_index != UINT_MAX; + } + + rational model_based_opt::get_coefficient(unsigned row_id, unsigned var_id) { + row const& r = m_rows[row_id]; + unsigned lo = 0, hi = r.m_vars.size(); + while (lo < hi) { + unsigned mid = lo + (hi - lo)/2; + SASSERT(mid < hi); + unsigned id = r.m_vars[mid].m_id; + if (id == var_id) { + lo = mid; + break; + } + if (id < var_id) { + lo = mid + 1; + } + else { + hi = mid - 1; + } + } + unsigned id = r.m_vars[lo].m_id; + if (id == var_id) { + return r.m_vars[lo].m_coeff; + } + else { + return rational::zero(); + } + } + + bool model_based_opt::resolve(unsigned row_id1, rational const& a1, unsigned row_id2, unsigned x) { + // row1 is of the form a1*x + t1 <~ 0 + // row2 is of the form a2*x + t2 <~ 0 + // assume that a1, a2 have the same sign. + // if a1 is positive, then val(t1*a2/a1) <= val(t2*a1/a2) + // replace row2 with the new inequality of the form: + // t1 - a1*t2/a2 <~~ 0 + // where <~~ is strict if either <~1 or <~2 is strict. + // if a1 is negative, then .... + // + if (!m_rows[row_id2].m_alive) { + return false; + } + rational a2 = get_coefficient(row_id2, x); + if (a2.is_zero()) { + return false; + } + else if (a1.is_pos() && a2.is_pos()) { + multiply(-a1/a2, row_id2); + add(row_id2, row_id1); + return true; + } + else if (a1.is_neg() && a2.is_neg()) { + NOT_IMPLEMENTED_YET(); + // tbd + return true; + } + else { + m_rows[row_id2].m_alive = false; + return false; + } + } + + void model_based_opt::multiply(rational const& c, unsigned row_id) { + if (c.is_one()) { + return; + } + row& r = m_rows[row_id]; + SASSERT(r.m_alive); + for (unsigned i = 0; i < r.m_vars.size(); ++i) { + r.m_vars[i].m_coeff *= c; + } + r.m_coeff *= c; + r.m_value *= c; + } + + // add row2 to row1, store result in row1. + + void model_based_opt::add(unsigned row_id1, unsigned row_id2) { + m_new_vars.reset(); + row& r1 = m_rows[row_id1]; + row const& r2 = m_rows[row_id2]; + unsigned i = 0, j = 0; + for(; i < r1.m_vars.size() || j < r2.m_vars.size(); ) { + if (j == r2.m_vars.size()) { + m_new_vars.append(r1.m_vars.size() - i, r1.m_vars.c_ptr() + i); + } + else if (i == r1.m_vars.size()) { + for (; j < r2.m_vars.size(); ++j) { + m_new_vars.push_back(r2.m_vars[j]); + m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); + } + } + else { + unsigned v1 = r1.m_vars[i].m_id; + unsigned v2 = r2.m_vars[j].m_id; + if (v1 == v2) { + m_new_vars.push_back(r1.m_vars[i]); + m_new_vars.back().m_coeff += r2.m_vars[j].m_coeff; + ++i; + ++j; + if (m_new_vars.back().m_coeff.is_zero()) { + m_new_vars.pop_back(); + } + } + else if (v1 < v2) { + m_new_vars.push_back(r1.m_vars[i]); + ++i; + } + else { + m_new_vars.push_back(r2.m_vars[j]); + m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); + ++j; + } + } + } + r1.m_coeff += r2.m_coeff; + r1.m_vars.swap(m_new_vars); + r1.m_value += r2.m_value; + if (r2.m_type == t_lt) { + r1.m_type = t_lt; + } + } + + void model_based_opt::display(std::ostream& out) const { + for (unsigned i = 0; i < m_rows.size(); ++i) { + display(out, m_rows[i]); + } + } + + void model_based_opt::display(std::ostream& out, row const& r) const { + vector const& vars = r.m_vars; + for (unsigned i = 0; i < vars.size(); ++i) { + if (i > 0 && vars[i].m_coeff.is_pos()) { + out << "+ "; + } + out << vars[i].m_coeff << "* v" << vars[i].m_id << " "; + } + out << r.m_coeff; + switch (r.m_type) { + case t_eq: + out << " = 0\n"; + break; + case t_lt: + out << " < 0\n"; + break; + case t_le: + out << " <= 0\n"; + break; + } + } + + unsigned model_based_opt::add_var(rational const& value) { + NOT_IMPLEMENTED_YET(); + return 0; + } + + void model_based_opt::add_constraint(vector const& coeffs, rational const& c, ineq_type r) { + NOT_IMPLEMENTED_YET(); + } + + void model_based_opt::set_objective(vector const& coeffs, rational const& c) { + NOT_IMPLEMENTED_YET(); + } + +} + diff --git a/src/math/simplex/model_based_opt.h b/src/math/simplex/model_based_opt.h new file mode 100644 index 000000000..d881d3caa --- /dev/null +++ b/src/math/simplex/model_based_opt.h @@ -0,0 +1,102 @@ +/*++ +Copyright (c) 2016 Microsoft Corporation + +Module Name: + + model_based_opt.h + +Abstract: + + Model-based optimization for linear real arithmetic. + +Author: + + Nikolaj Bjorner (nbjorner) 2016-27-4 + +Revision History: + + +--*/ + +#ifndef __MODEL_BASED_OPT_H__ +#define __MODEL_BASED_OPT_H__ + +#include "util.h" +#include "rational.h" + +namespace opt { + + enum ineq_type { + t_eq, + t_lt, + t_le + }; + + enum bound_type { + unbounded, + strict, + non_strict + }; + + class model_based_opt { + public: + struct var { + unsigned m_id; + rational m_coeff; + var(unsigned id, rational const& c): m_id(id), m_coeff(c) {} + }; + private: + struct row { + vector m_vars; // variables with coefficients + rational m_coeff; // constant in inequality + ineq_type m_type; // inequality type + rational m_value; // value of m_vars + m_coeff under interpretation of m_var2value. + bool m_alive; // rows can be marked dead if they have been processed. + }; + vector m_rows; + vector m_var2row_ids; + vector m_var2value; + row m_objective; + vector m_new_vars; + + bool invariant(); + bool invariant(row const& r); + + + bool find_bound(unsigned x, unsigned& bound_index, unsigned_vector& other, bool is_pos); + + rational get_coefficient(unsigned row_id, unsigned var_id); + + bool resolve(unsigned row_id1, rational const& a1, unsigned row_id2, unsigned x); + + void multiply(rational const& c, unsigned row_id); + + void add(unsigned row_id1, unsigned row_id2); + + public: + + // add a fresh variable with value 'value'. + unsigned add_var(rational const& value); + + // add a constraint. We assume that the constraint is + // satisfied under the values provided to the variables. + void add_constraint(vector const& coeffs, rational const& c, ineq_type r); + + // Set the objective function (linear). + void set_objective(vector const& coeffs, rational const& c); + + // find a maximal value for the objective function over the current values. + // in other words, the returned maximal value may not be globally optimal, + // but the current evaluation of variables are used to select a local + // optimal. + bound_type maximize(rational& value); + + + void display(std::ostream& out) const; + void display(std::ostream& out, row const& r) const; + + }; + +} + +#endif diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index d128a4fe9..f1637dfb7 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -51,175 +51,6 @@ namespace qe { return is_divides(a, e1, e2, k, t) || is_divides(a, e2, e1, k, t); } - enum ineq_type { - t_eq, - t_lt, - t_le - }; - - struct tableau { - struct var { - unsigned m_id; - rational m_coeff; - var(unsigned id, rational const& c): m_id(id), m_coeff(c) {} - }; - struct row { - vector m_vars; // variables with coefficients - rational m_coeff; // constant in inequality - ineq_type m_type; // inequality type - rational m_value; // value of m_vars + m_coeff under interpretation of m_var2value. - bool m_alive; // rows can be marked dead if they have been processed. - }; - vector m_rows; - vector m_var2rows; - vector m_var2value; - row m_objective; - - void invariant() { - // variables in each row are sorted. - } - - mbp::bound_type maximize(rational& value) { - // tbd - return mbp::unbounded; - } - - rational get_coefficient(unsigned row_id, unsigned var_id) { - row const& r = m_rows[row_id]; - unsigned lo = 0, hi = r.m_vars.size(); - while (lo < hi) { - unsigned mid = lo + (hi - lo)/2; - SASSERT(mid < hi); - unsigned id = r.m_vars[mid].m_id; - if (id == var_id) { - lo = mid; - break; - } - if (id < var_id) { - lo = mid + 1; - } - else { - hi = mid - 1; - } - } - unsigned id = r.m_vars[lo].m_id; - if (id == var_id) { - return r.m_vars[lo].m_coeff; - } - else { - return rational::zero(); - } - } - - void resolve(unsigned row_id1, unsigned row_id2, unsigned x) { - // row1 is of the form a1*x + t1 <~ 0 - // row2 is of the form a2*x + t2 <~ 0 - // assume that a1, a2 have the same sign. - // if a1 is positive, then val(t1*a2/a1) <= val(t2*a1/a2) - // replace row2 with the new inequality of the form: - // t1 - a1*t2/a2 <~~ 0 - // where <~~ is strict if either <~1 or <~2 is strict. - // if a1 is negative, then .... - // - } - - void multiply(rational const& c, unsigned row_id) { - if (c.is_one()) { - return; - } - row& r = m_rows[row_id]; - SASSERT(r.m_alive); - for (unsigned i = 0; i < r.m_vars.size(); ++i) { - r.m_vars[i].m_coeff *= c; - } - r.m_coeff *= c; - r.m_value *= c; - } - - // subtract row2 from row1, store result in row2 - - vector m_new_vars; - - void subtract(unsigned row_id1, unsigned row_id2) { - m_new_vars.reset(); - row const& r1 = m_rows[row_id1]; - row& r2 = m_rows[row_id2]; - unsigned i = 0, j = 0; - for(; i < r1.m_vars.size() || j < r2.m_vars.size(); ) { - if (j == r2.m_vars.size()) { - for (; i < r1.m_vars.size(); ++i) { - m_new_vars.push_back(r1.m_vars[i]); - m_var2rows[r1.m_vars[i].m_id].push_back(row_id2); - } - } - else if (i == r1.m_vars.size()) { - for (; j < r2.m_vars.size(); ++j) { - m_new_vars.push_back(r2.m_vars[j]); - m_new_vars.back().m_coeff.neg(); - } - } - else { - unsigned v1 = r1.m_vars[i].m_id; - unsigned v2 = r2.m_vars[j].m_id; - if (v1 == v2) { - m_new_vars.push_back(r1.m_vars[i]); - m_new_vars.back().m_coeff -= r2.m_vars[j].m_coeff; - ++i; - ++j; - if (m_new_vars.back().m_coeff.is_zero()) { - m_new_vars.pop_back(); - } - } - else if (v1 < v2) { - m_new_vars.push_back(r1.m_vars[i]); - m_var2rows[r1.m_vars[i].m_id].push_back(row_id2); - ++i; - } - else { - m_new_vars.push_back(r2.m_vars[j]); - m_new_vars.back().m_coeff.neg(); - ++j; - } - } - } - r2.m_coeff.neg(); - r2.m_coeff += r1.m_coeff; - r2.m_vars.swap(m_new_vars); - r2.m_value.neg(); - r2.m_value += r1.m_value; - if (r1.m_type == t_lt) { - r2.m_type = t_lt; - } - } - - void display(std::ostream& out) const { - for (unsigned i = 0; i < m_rows.size(); ++i) { - display(out, m_rows[i]); - } - } - - void display(std::ostream& out, row const& r) const { - vector const& vars = r.m_vars; - for (unsigned i = 0; i < vars.size(); ++i) { - if (i > 0 && vars[i].m_coeff.is_pos()) { - out << "+ "; - } - out << vars[i].m_coeff << "* v" << vars[i].m_id << " "; - } - out << r.m_coeff; - switch (r.m_type) { - case t_eq: - out << " = 0\n"; - break; - case t_lt: - out << " < 0\n"; - break; - case t_le: - out << " <= 0\n"; - break; - } - } - }; #if 0 obj_map m_expr2var; @@ -234,7 +65,7 @@ namespace qe { th_rewriter m_rw; expr_ref_vector m_ineq_terms; vector m_ineq_coeffs; - svector m_ineq_types; + svector m_ineq_types; expr_ref_vector m_div_terms; vector m_div_divisors, m_div_coeffs; expr_ref_vector m_new_lits; @@ -368,7 +199,7 @@ namespace qe { bool is_linear(model& model, expr* lit, bool& found_eq) { rational c(0), mul(1); expr_ref t(m); - ineq_type ty = t_le; + opt::ineq_type ty = opt::t_le; expr* e1, *e2; expr_ref_vector ts(m); bool is_not = m.is_not(lit, lit); @@ -379,17 +210,17 @@ namespace qe { if (a.is_le(lit, e1, e2) || a.is_ge(lit, e2, e1)) { is_linear(model, mul, e1, c, ts); is_linear(model, -mul, e2, c, ts); - ty = is_not?t_lt:t_le; + ty = is_not? opt::t_lt : opt::t_le; } else if (a.is_lt(lit, e1, e2) || a.is_gt(lit, e2, e1)) { is_linear(model, mul, e1, c, ts); is_linear(model, -mul, e2, c, ts); - ty = is_not?t_le:t_lt; + ty = is_not? opt::t_le: opt::t_lt; } else if (m.is_eq(lit, e1, e2) && !is_not && is_arith(e1)) { is_linear(model, mul, e1, c, ts); is_linear(model, -mul, e2, c, ts); - ty = t_eq; + ty = opt::t_eq; } else if (m.is_distinct(lit) && !is_not && is_arith(to_app(lit)->get_arg(0))) { expr_ref val(m); @@ -408,7 +239,7 @@ namespace qe { is_linear(model, mul, nums[i+1].first, c, ts); is_linear(model, -mul, nums[i].first, c, ts); t = add(ts); - accumulate_linear(model, c, t, t_lt); + accumulate_linear(model, c, t, opt::t_lt); } t = mk_num(0); c.reset(); @@ -427,7 +258,7 @@ namespace qe { if (r1 < r2) { std::swap(e1, e2); } - ty = t_lt; + ty = opt::t_lt; is_linear(model, mul, e1, c, ts); is_linear(model, -mul, e2, c, ts); } @@ -435,24 +266,24 @@ namespace qe { TRACE("qe", tout << "can't project:" << mk_pp(lit, m) << "\n";); throw cant_project(); } - if (ty == t_lt && is_int()) { + if (ty == opt::t_lt && is_int()) { ts.push_back(mk_num(1)); - ty = t_le; + ty = opt::t_le; } t = add(ts); - if (ty == t_eq && c.is_neg()) { + if (ty == opt::t_eq && c.is_neg()) { t = mk_uminus(t); c.neg(); } - if (ty == t_eq && c > rational(1)) { + if (ty == opt::t_eq && c > rational(1)) { m_ineq_coeffs.push_back(-c); m_ineq_terms.push_back(mk_uminus(t)); - m_ineq_types.push_back(t_le); + m_ineq_types.push_back(opt::t_le); m_num_neg++; - ty = t_le; + ty = opt::t_le; } accumulate_linear(model, c, t, ty); - found_eq = !c.is_zero() && ty == t_eq; + found_eq = !c.is_zero() && ty == opt::t_eq; return true; } @@ -503,16 +334,16 @@ namespace qe { } }; - void accumulate_linear(model& model, rational const& c, expr_ref& t, ineq_type ty) { + void accumulate_linear(model& model, rational const& c, expr_ref& t, opt::ineq_type ty) { if (c.is_zero()) { switch (ty) { - case t_eq: + case opt::t_eq: t = a.mk_eq(t, mk_num(0)); break; - case t_lt: + case opt::t_lt: t = a.mk_lt(t, mk_num(0)); break; - case t_le: + case opt::t_le: t = a.mk_le(t, mk_num(0)); break; } @@ -522,7 +353,7 @@ namespace qe { m_ineq_coeffs.push_back(c); m_ineq_terms.push_back(t); m_ineq_types.push_back(ty); - if (ty == t_eq) { + if (ty == opt::t_eq) { // skip } else if (c.is_pos()) { @@ -632,18 +463,18 @@ namespace qe { expr* ineq_term(unsigned i) const { return m_ineq_terms[i]; } rational const& ineq_coeff(unsigned i) const { return m_ineq_coeffs[i]; } - ineq_type ineq_ty(unsigned i) const { return m_ineq_types[i]; } + opt::ineq_type ineq_ty(unsigned i) const { return m_ineq_types[i]; } app_ref mk_ineq_pred(unsigned i) { app_ref result(m); result = to_app(mk_add(mk_mul(ineq_coeff(i), m_var->x()), ineq_term(i))); switch (ineq_ty(i)) { - case t_lt: + case opt::t_lt: result = a.mk_lt(result, mk_num(0)); break; - case t_le: + case opt::t_le: result = a.mk_le(result, mk_num(0)); break; - case t_eq: + case opt::t_eq: result = m.mk_eq(result, mk_num(0)); break; } @@ -652,9 +483,9 @@ namespace qe { void display_ineq(std::ostream& out, unsigned i) const { out << mk_pp(ineq_term(i), m) << " " << ineq_coeff(i) << "*" << mk_pp(m_var->x(), m); switch (ineq_ty(i)) { - case t_eq: out << " = 0\n"; break; - case t_le: out << " <= 0\n"; break; - case t_lt: out << " < 0\n"; break; + case opt::t_eq: out << " = 0\n"; break; + case opt::t_le: out << " <= 0\n"; break; + case opt::t_lt: out << " < 0\n"; break; } } unsigned num_ineqs() const { return m_ineq_terms.size(); } @@ -769,7 +600,7 @@ namespace qe { bool is_int = a.is_int(m_var->x()); for (unsigned i = 0; i < num_ineqs(); ++i) { rational const& ac = m_ineq_coeffs[i]; - SASSERT(!is_int || t_le == ineq_ty(i)); + SASSERT(!is_int || opt::t_le == ineq_ty(i)); // // ac*x + t < 0 @@ -783,7 +614,7 @@ namespace qe { new_max = new_max || (r > max_r) || - (r == max_r && t_lt == ineq_ty(i)) || + (r == max_r && opt::t_lt == ineq_ty(i)) || (r == max_r && is_int && ac.is_one()); TRACE("qe", tout << "max: " << mk_pp(ineq_term(i), m) << "/" << abs(ac) << " := " << r << " " << (new_max?"":"not ") << "new max\n";); @@ -821,7 +652,7 @@ namespace qe { expr_ref ts = mk_add(bt, as); expr_ref z = mk_num(0); expr_ref fml(m); - if (t_lt == ineq_ty(i) || t_lt == ineq_ty(j)) { + if (opt::t_lt == ineq_ty(i) || opt::t_lt == ineq_ty(j)) { fml = a.mk_lt(ts, z); } else { @@ -838,7 +669,7 @@ namespace qe { rational ac = ineq_coeff(i); rational bc = ineq_coeff(j); expr_ref tmp(m); - SASSERT(t_le == ineq_ty(i) && t_le == ineq_ty(j)); + SASSERT(opt::t_le == ineq_ty(i) && opt::t_le == ineq_ty(j)); SASSERT(ac.is_pos() == bc.is_neg()); rational abs_a = abs(ac); rational abs_b = abs(bc); @@ -917,7 +748,7 @@ namespace qe { expr* s = ineq_term(j); expr_ref bt = mk_mul(abs(bc), t); expr_ref as = mk_mul(abs(ac), s); - if (t_lt == ineq_ty(i) && t_le == ineq_ty(j)) { + if (opt::t_lt == ineq_ty(i) && opt::t_le == ineq_ty(j)) { return expr_ref(a.mk_lt(bt, as), m); } else { @@ -988,9 +819,9 @@ namespace qe { expr_ref lhs(m), val(m); lhs = mk_sub(mk_mul(c, ineq_term(i)), mk_mul(ineq_coeff(i), t)); switch (ineq_ty(i)) { - case t_lt: lhs = a.mk_lt(lhs, mk_num(0)); break; - case t_le: lhs = a.mk_le(lhs, mk_num(0)); break; - case t_eq: lhs = m.mk_eq(lhs, mk_num(0)); break; + case opt::t_lt: lhs = a.mk_lt(lhs, mk_num(0)); break; + case opt::t_le: lhs = a.mk_le(lhs, mk_num(0)); break; + case opt::t_eq: lhs = m.mk_eq(lhs, mk_num(0)); break; } TRACE("qe", tout << lhs << "\n";); SASSERT (model.eval(lhs, val) && m.is_true(val)); @@ -1082,17 +913,17 @@ namespace qe { return true; } - mbp::bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { + opt::bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { obj_map ts; rational c(0), mul(1); linearize(mdl, mul, t, c, ts); - + // TBD: // pick variables one by one from ts. // m_var = alloc(contains_app, m, v); // perform upper or lower projection depending on sign of v. // - return mbp::unbounded; + return opt::unbounded; } }; @@ -1116,7 +947,7 @@ namespace qe { return m_imp->a.get_family_id(); } - mbp::bound_type arith_project_plugin::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { + opt::bound_type arith_project_plugin::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { return m_imp->maximize(fmls, mdl, t, value, bound); } diff --git a/src/qe/qe_arith.h b/src/qe/qe_arith.h index 9ba3fa5fc..f79a61245 100644 --- a/src/qe/qe_arith.h +++ b/src/qe/qe_arith.h @@ -29,7 +29,7 @@ namespace qe { virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits); virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); virtual family_id get_family_id(); - mbp::bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound); + opt::bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound); }; bool arith_project(model& model, app* var, expr_ref_vector& lits); diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index a3d8d077c..c592445ea 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -213,7 +213,7 @@ class mbp::impl { public: - mbp::bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { + opt::bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { arith_project_plugin arith(m); return arith.maximize(fmls, mdl, t, value, bound); } @@ -421,6 +421,6 @@ void mbp::extract_literals(model& model, expr_ref_vector& lits) { m_impl->extract_literals(model, lits); } -mbp::bound_type mbp::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { +opt::bound_type mbp::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { return m_impl->maximize(fmls, mdl, t, value, bound); } diff --git a/src/qe/qe_mbp.h b/src/qe/qe_mbp.h index 2fc5a9fb9..4081288b4 100644 --- a/src/qe/qe_mbp.h +++ b/src/qe/qe_mbp.h @@ -24,6 +24,7 @@ Revision History: #include "ast.h" #include "params.h" #include "model.h" +#include "model_based_opt.h" namespace qe { @@ -75,12 +76,7 @@ namespace qe { \brief Maximize objective t under current model for constraints in fmls. */ - enum bound_type { - unbounded, - strict, - non_strict - }; - bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound); + opt::bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound); }; } diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index 3c3a5c0dd..a220cf268 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -1284,16 +1284,16 @@ namespace qe { app* m_objective; expr_ref m_value; - mbp::bound_type m_bound; + opt::bound_type m_bound; bool m_was_sat; - lbool maximize(expr_ref_vector const& fmls, app* t, expr_ref& value, mbp::bound_type& bound) { + lbool maximize(expr_ref_vector const& fmls, app* t, expr_ref& value, opt::bound_type& bound) { expr_ref_vector defs(m); expr_ref fml = negate_core(fmls); hoist(fml); m_objective = t; m_value = 0; - m_bound = mbp::unbounded; + m_bound = opt::unbounded; m_was_sat = false; m_pred_abs.abstract_atoms(fml, defs); fml = m_pred_abs.mk_abstract(fml); @@ -1334,14 +1334,14 @@ namespace qe { expr_ref bound(m); m_bound = m_mbp.maximize(core, mdl, m_objective, m_value, bound); switch (m_bound) { - case mbp::unbounded: + case opt::unbounded: m_ex.assert_expr(m.mk_false()); m_fa.assert_expr(m.mk_false()); break; - case mbp::strict: + case opt::strict: m_ex.assert_expr(bound); break; - case mbp::non_strict: + case opt::non_strict: m_ex.assert_expr(bound); break; } @@ -1349,7 +1349,7 @@ namespace qe { }; - lbool maximize(expr_ref_vector const& fmls, app* t, expr_ref& value, mbp::bound_type& bound, params_ref const& p) { + lbool maximize(expr_ref_vector const& fmls, app* t, expr_ref& value, opt::bound_type& bound, params_ref const& p) { ast_manager& m = fmls.get_manager(); qsat qs(m, p, qsat_maximize); return qs.maximize(fmls, t, value, bound); diff --git a/src/qe/qsat.h b/src/qe/qsat.h index dfba75e6c..fd10e3e75 100644 --- a/src/qe/qsat.h +++ b/src/qe/qsat.h @@ -114,7 +114,7 @@ namespace qe { void collect_statistics(statistics& st) const; }; - lbool maximize(expr_ref_vector const& fmls, app* t, expr_ref& value, mbp::bound_type& bound, params_ref const& p); + lbool maximize(expr_ref_vector const& fmls, app* t, expr_ref& value, opt::bound_type& bound, params_ref const& p); } From 10cc8c3a75f8b0070a19ee2be32ee8974001496d Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Wed, 27 Apr 2016 20:15:22 +0100 Subject: [PATCH 54/66] Build fix for VS2012 and earlier. --- src/util/hwf.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index a990e6e6d..8c10e3170 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -383,7 +383,13 @@ void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o } void hwf_manager::rem(hwf const & x, hwf const & y, hwf & o) { +#if defined(_WINDOWS) && _MSC_VER < 1800 + o.value = fmod(x.value, y.value); + if (o.value >= (y.value/2)) + o.value /= 2.0; +#else o.value = remainder(x.value, y.value); +#endif #if 0 // Here is an x87 alternative if the above makes problems; this may also be faster. From 6aa61028912df525ee066a1776587d0222340552 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 27 Apr 2016 15:08:10 -0700 Subject: [PATCH 55/66] factor out model-based-opt code Signed-off-by: Nikolaj Bjorner --- contrib/cmake/src/test/CMakeLists.txt | 1 + src/math/simplex/model_based_opt.cpp | 81 ++++++++++++++++++++------- src/math/simplex/model_based_opt.h | 15 ++++- src/test/main.cpp | 1 + src/test/model_based_opt.cpp | 49 ++++++++++++++++ 5 files changed, 125 insertions(+), 22 deletions(-) create mode 100644 src/test/model_based_opt.cpp diff --git a/contrib/cmake/src/test/CMakeLists.txt b/contrib/cmake/src/test/CMakeLists.txt index f504760e0..427cedcdb 100644 --- a/contrib/cmake/src/test/CMakeLists.txt +++ b/contrib/cmake/src/test/CMakeLists.txt @@ -61,6 +61,7 @@ add_executable(test-z3 "${CMAKE_CURRENT_BINARY_DIR}/mem_initializer.cpp" memory.cpp model2expr.cpp + model_based_opt.cpp model_evaluator.cpp model_retrieval.cpp mpbq.cpp diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index 10008345a..d96c12302 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -22,6 +22,13 @@ Revision History: namespace opt { + + model_based_opt::model_based_opt(): + m_objective_id(0) + { + m_rows.push_back(row()); + } + bool model_based_opt::invariant() { // variables in each row are sorted. @@ -30,12 +37,13 @@ namespace opt { return false; } } - return invariant(m_objective); + return true; } bool model_based_opt::invariant(row const& r) { rational val = r.m_coeff; vector const& vars = r.m_vars; + SASSERT(!vars.empty()); for (unsigned i = 0; i < vars.size(); ++i) { var const& v = vars[i]; SASSERT(i + 1 == vars.size() || v.m_id < vars[i+1].m_id); @@ -72,40 +80,40 @@ namespace opt { // then replace a3*x + t3 by t3/a3 - t2/a2 <= 0 // bound_type model_based_opt::maximize(rational& value) { - // tbd SASSERT(invariant()); - vector & vars = m_objective.m_vars; unsigned_vector other; - while (!vars.empty()) { - var const& v = vars.back(); + while (!objective().m_vars.empty()) { + var const& v = objective().m_vars.back(); unsigned x = v.m_id; rational const& coeff = v.m_coeff; rational const& x_val = m_var2value[x]; unsigned_vector const& row_ids = m_var2row_ids[x]; unsigned bound_index; other.reset(); - if (find_bound(x, bound_index, other, coeff.is_pos())) { + if (find_bound(x, bound_index, other, coeff.is_pos())) { rational bound_coeff = m_rows[bound_index].m_coeff; for (unsigned i = 0; i < other.size(); ++i) { resolve(other[i], bound_coeff, bound_index, x); } // coeff*x + objective -> coeff*(bound) + objective - // tbd: multiply(coeff/bound_coeff, bound_index); - //add(m_objective_id, bound_index); + SASSERT(invariant(m_rows[bound_index])); + objective().m_vars.back().m_coeff.reset(); + add(m_objective_id, bound_index); + SASSERT(invariant(objective())); m_rows[bound_index].m_alive = false; } else { return unbounded; } } - value = m_objective.m_coeff; - switch (m_objective.m_type) { - case t_lt: return strict; - case t_le: return non_strict; - case t_eq: return non_strict; + value = objective().m_coeff; + if (objective().m_type == t_lt) { + return strict; + } + else { + return non_strict; } - return non_strict; } bool model_based_opt::find_bound(unsigned x, unsigned& bound_index, unsigned_vector& other, bool is_pos) { @@ -143,6 +151,7 @@ namespace opt { rational model_based_opt::get_coefficient(unsigned row_id, unsigned var_id) { row const& r = m_rows[row_id]; + SASSERT(!r.m_vars.empty()); unsigned lo = 0, hi = r.m_vars.size(); while (lo < hi) { unsigned mid = lo + (hi - lo)/2; @@ -228,7 +237,9 @@ namespace opt { else if (i == r1.m_vars.size()) { for (; j < r2.m_vars.size(); ++j) { m_new_vars.push_back(r2.m_vars[j]); - m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); + if (row_id1 != m_objective_id) { + m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); + } } } else { @@ -249,7 +260,9 @@ namespace opt { } else { m_new_vars.push_back(r2.m_vars[j]); - m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); + if (row_id1 != m_objective_id) { + m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); + } ++j; } } @@ -260,6 +273,7 @@ namespace opt { if (r2.m_type == t_lt) { r1.m_type = t_lt; } + SASSERT(invariant(r1)); } void model_based_opt::display(std::ostream& out) const { @@ -291,16 +305,41 @@ namespace opt { } unsigned model_based_opt::add_var(rational const& value) { - NOT_IMPLEMENTED_YET(); - return 0; + unsigned v = m_var2value.size(); + m_var2value.push_back(value); + m_var2row_ids.push_back(unsigned_vector()); + return v; } - void model_based_opt::add_constraint(vector const& coeffs, rational const& c, ineq_type r) { - NOT_IMPLEMENTED_YET(); + void model_based_opt::set_row(row& r, vector const& coeffs, rational const& c, ineq_type rel) { + rational val(c); + SASSERT(r.m_vars.empty()); + r.m_vars.append(coeffs.size(), coeffs.c_ptr()); + std::sort(r.m_vars.begin(), r.m_vars.end(), var::compare()); + for (unsigned i = 0; i < coeffs.size(); ++i) { + val += m_var2value[coeffs[i].m_id] * coeffs[i].m_coeff; + } + r.m_alive = true; + r.m_coeff = c; + r.m_value = val; + r.m_type = rel; + SASSERT(invariant(r)); + } + + void model_based_opt::add_constraint(vector const& coeffs, rational const& c, ineq_type rel) { + rational val(c); + row r0; + unsigned row_id = m_rows.size(); + m_rows.push_back(r0); + row& r = m_rows.back(); + set_row(r, coeffs, c, rel); + for (unsigned i = 0; i < coeffs.size(); ++i) { + m_var2row_ids[coeffs[i].m_id].push_back(row_id); + } } void model_based_opt::set_objective(vector const& coeffs, rational const& c) { - NOT_IMPLEMENTED_YET(); + set_row(objective(), coeffs, c, t_le); } } diff --git a/src/math/simplex/model_based_opt.h b/src/math/simplex/model_based_opt.h index d881d3caa..44e3d44bb 100644 --- a/src/math/simplex/model_based_opt.h +++ b/src/math/simplex/model_based_opt.h @@ -44,23 +44,32 @@ namespace opt { unsigned m_id; rational m_coeff; var(unsigned id, rational const& c): m_id(id), m_coeff(c) {} + struct compare { + bool operator()(var x, var y) { + return x.m_id < y.m_id; + } + }; }; private: struct row { + row(): m_type(t_le), m_value(0), m_alive(false) {} vector m_vars; // variables with coefficients rational m_coeff; // constant in inequality ineq_type m_type; // inequality type rational m_value; // value of m_vars + m_coeff under interpretation of m_var2value. bool m_alive; // rows can be marked dead if they have been processed. }; + vector m_rows; + unsigned m_objective_id; vector m_var2row_ids; vector m_var2value; - row m_objective; vector m_new_vars; bool invariant(); bool invariant(row const& r); + + row& objective() { return m_rows[0]; } bool find_bound(unsigned x, unsigned& bound_index, unsigned_vector& other, bool is_pos); @@ -73,8 +82,12 @@ namespace opt { void add(unsigned row_id1, unsigned row_id2); + void set_row(row& r, vector const& coeffs, rational const& c, ineq_type rel); + public: + model_based_opt(); + // add a fresh variable with value 'value'. unsigned add_var(rational const& value); diff --git a/src/test/main.cpp b/src/test/main.cpp index 875f7bed0..8fc0a2de6 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -186,6 +186,7 @@ int main(int argc, char ** argv) { TST(smt_context); TST(theory_dl); TST(model_retrieval); + TST(model_based_opt); TST(factor_rewriter); TST(smt2print_parse); TST(substitution); diff --git a/src/test/model_based_opt.cpp b/src/test/model_based_opt.cpp new file mode 100644 index 000000000..f21627b2d --- /dev/null +++ b/src/test/model_based_opt.cpp @@ -0,0 +1,49 @@ +#include "model_based_opt.h" + +static void test1() { + opt::model_based_opt mbo; + typedef opt::model_based_opt::var var; + vector vars; + unsigned x = mbo.add_var(rational(2)); + unsigned y = mbo.add_var(rational(3)); + unsigned z = mbo.add_var(rational(4)); + unsigned u = mbo.add_var(rational(5)); + + vars.reset(); + vars.push_back(var(x, rational(1))); + vars.push_back(var(y, rational(-1))); + mbo.add_constraint(vars, rational(0), opt::t_le); + + vars.reset(); + vars.push_back(var(x, rational(1))); + vars.push_back(var(z, rational(-1))); + mbo.add_constraint(vars, rational(0), opt::t_le); + + vars.reset(); + vars.push_back(var(y, rational(1))); + vars.push_back(var(u, rational(-1))); + mbo.add_constraint(vars, rational(0), opt::t_le); + + vars.reset(); + vars.push_back(var(z, rational(1))); + vars.push_back(var(u, rational(-1))); + mbo.add_constraint(vars, rational(-1), opt::t_le); + + vars.reset(); + vars.push_back(var(u, rational(1))); + mbo.add_constraint(vars, rational(4), opt::t_le); + + vars.reset(); + vars.push_back(var(x, rational(2))); + mbo.set_objective(vars, rational(0)); + + rational value; + opt::bound_type bound = mbo.maximize(value); + + std::cout << bound << ": " << value << "\n"; + +} + +void tst_model_based_opt() { + test1(); +} From cba82325de3cf21721ac6eaa5d44d4e3c6585120 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 28 Apr 2016 12:52:36 +0100 Subject: [PATCH 56/66] Build fix for old systems that don't have a float remainder(...) function. --- src/util/hwf.cpp | 6 +++--- src/util/mpf.cpp | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index 8c10e3170..b3b9951b6 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -383,10 +383,10 @@ void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o } void hwf_manager::rem(hwf const & x, hwf const & y, hwf & o) { -#if defined(_WINDOWS) && _MSC_VER < 1800 +#if defined(_WINDOWS) && _MSC_VER < 1700 o.value = fmod(x.value, y.value); - if (o.value >= (y.value/2)) - o.value /= 2.0; + if (o.value >= (y.value/2.0)) + o.value -= y.value; #else o.value = remainder(x.value, y.value); #endif diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 07ccd3c7b..1725ead72 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -1412,7 +1412,11 @@ std::string mpf_manager::to_string_hexfloat(mpf const & x) { std::ios::fmtflags ff = ss.setf(std::ios_base::hex | std::ios_base::uppercase | std::ios_base::showpoint | std::ios_base::showpos); ss.precision(13); +#if defined(_WIN32) && _MSC_VER >= 1800 ss << std::hexfloat << to_double(x); +#else + ss << std::hex << (*reinterpret_cast(&(x))); +#endif return ss.str(); } From f3c74a06ebc4180081d532df6a5ad3c8e7c59e07 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 28 Apr 2016 12:54:10 +0100 Subject: [PATCH 57/66] debug fix for mpf_manager --- src/util/mpf.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/mpf.cpp b/src/util/mpf.cpp index 1725ead72..1bf968cdd 100644 --- a/src/util/mpf.cpp +++ b/src/util/mpf.cpp @@ -1411,6 +1411,7 @@ std::string mpf_manager::to_string_hexfloat(mpf const & x) { std::stringstream ss(""); std::ios::fmtflags ff = ss.setf(std::ios_base::hex | std::ios_base::uppercase | std::ios_base::showpoint | std::ios_base::showpos); + ss.setf(ff); ss.precision(13); #if defined(_WIN32) && _MSC_VER >= 1800 ss << std::hexfloat << to_double(x); From 47ec3b1f87a73995528e4f26f2f4dd2043b5b5a3 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Thu, 28 Apr 2016 13:17:39 +0100 Subject: [PATCH 58/66] Build fix for VS2012 --- src/util/hwf.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index b3b9951b6..914d113b2 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -383,7 +383,7 @@ void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o } void hwf_manager::rem(hwf const & x, hwf const & y, hwf & o) { -#if defined(_WINDOWS) && _MSC_VER < 1700 +#if defined(_WINDOWS) && _MSC_VER <= 1700 o.value = fmod(x.value, y.value); if (o.value >= (y.value/2.0)) o.value -= y.value; From 932ef442aec712b6a696a42c2628fff271f4232b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 28 Apr 2016 09:47:55 -0700 Subject: [PATCH 59/66] model based opt dev Signed-off-by: Nikolaj Bjorner --- src/math/simplex/model_based_opt.cpp | 231 +++++++++++++++------------ src/math/simplex/model_based_opt.h | 16 +- src/test/model_based_opt.cpp | 125 +++++++++++---- 3 files changed, 239 insertions(+), 133 deletions(-) diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index d96c12302..f56f327d4 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -20,6 +20,25 @@ Revision History: #include "model_based_opt.h" +std::ostream& operator<<(std::ostream& out, opt::bound_type bt) { + switch (bt) { + case opt::unbounded: return out << "unbounded"; + case opt::strict: return out << "strict"; + case opt::non_strict: return out << "non-strict"; + } + return out; +} + +std::ostream& operator<<(std::ostream& out, opt::ineq_type ie) { + switch (ie) { + case opt::t_eq: return out << " = "; + case opt::t_lt: return out << " < "; + case opt::t_le: return out << " <= "; + } + return out; +} + + namespace opt { @@ -33,17 +52,16 @@ namespace opt { bool model_based_opt::invariant() { // variables in each row are sorted. for (unsigned i = 0; i < m_rows.size(); ++i) { - if (!invariant(m_rows[i])) { + if (!invariant(i, m_rows[i])) { return false; } } return true; } - bool model_based_opt::invariant(row const& r) { + bool model_based_opt::invariant(unsigned index, row const& r) { rational val = r.m_coeff; vector const& vars = r.m_vars; - SASSERT(!vars.empty()); for (unsigned i = 0; i < vars.size(); ++i) { var const& v = vars[i]; SASSERT(i + 1 == vars.size() || v.m_id < vars[i+1].m_id); @@ -52,8 +70,8 @@ namespace opt { } SASSERT(val == r.m_value); SASSERT(r.m_type != t_eq || val.is_zero()); - SASSERT(r.m_type != t_lt || val.is_neg()); - SASSERT(r.m_type != t_le || !val.is_pos()); + SASSERT(index == 0 || r.m_type != t_lt || val.is_neg()); + SASSERT(index == 0 || r.m_type != t_le || !val.is_pos()); return true; } @@ -83,31 +101,34 @@ namespace opt { SASSERT(invariant()); unsigned_vector other; while (!objective().m_vars.empty()) { - var const& v = objective().m_vars.back(); + TRACE("opt", tout << "tableau\n";); + var v = objective().m_vars.back(); unsigned x = v.m_id; rational const& coeff = v.m_coeff; rational const& x_val = m_var2value[x]; unsigned_vector const& row_ids = m_var2row_ids[x]; - unsigned bound_index; + unsigned bound_row_index; + rational bound_coeff; other.reset(); - if (find_bound(x, bound_index, other, coeff.is_pos())) { - rational bound_coeff = m_rows[bound_index].m_coeff; + if (find_bound(x, bound_row_index, bound_coeff, other, coeff.is_pos())) { + row& r = m_rows[bound_row_index]; + SASSERT(!bound_coeff.is_zero()); for (unsigned i = 0; i < other.size(); ++i) { - resolve(other[i], bound_coeff, bound_index, x); + resolve(bound_row_index, bound_coeff, other[i], x); } - // coeff*x + objective -> coeff*(bound) + objective - multiply(coeff/bound_coeff, bound_index); - SASSERT(invariant(m_rows[bound_index])); - objective().m_vars.back().m_coeff.reset(); - add(m_objective_id, bound_index); - SASSERT(invariant(objective())); - m_rows[bound_index].m_alive = false; + // coeff*x + objective <= ub + // a2*x + t2 <= 0 + // => coeff*x <= -t2*coeff/a2 + // objective + t2*coeff/a2 <= ub + + mul_add(m_objective_id, - coeff/bound_coeff, bound_row_index); + m_rows[bound_row_index].m_alive = false; } else { return unbounded; } } - value = objective().m_coeff; + value = objective().m_value; if (objective().m_type == t_lt) { return strict; } @@ -116,8 +137,8 @@ namespace opt { } } - bool model_based_opt::find_bound(unsigned x, unsigned& bound_index, unsigned_vector& other, bool is_pos) { - bound_index = UINT_MAX; + bool model_based_opt::find_bound(unsigned x, unsigned& bound_row_index, rational& bound_coeff, unsigned_vector& other, bool is_pos) { + bound_row_index = UINT_MAX; rational lub_val; rational const& x_val = m_var2value[x]; unsigned_vector const& row_ids = m_var2row_ids[x]; @@ -126,32 +147,39 @@ namespace opt { row& r = m_rows[row_id]; if (r.m_alive) { rational a = get_coefficient(row_id, x); - if (a.is_pos() == is_pos) { - rational value = r.m_value - x_val*a; // r.m_value = val_x*a + val(t), val(t) := r.m_value - val_x*a; - if (bound_index == UINT_MAX) { + if (a.is_zero()) { + // skip + } + else if (a.is_pos() == is_pos) { + rational value = x_val - (r.m_value/a); + if (bound_row_index == UINT_MAX) { lub_val = value; - bound_index = row_id; + bound_row_index = row_id; + bound_coeff = a; } else if ((is_pos && value < lub_val) || (!is_pos && value > lub_val)) { - other.push_back(bound_index); + other.push_back(bound_row_index); lub_val = value; - bound_index = row_id; + bound_row_index = row_id; + bound_coeff = a; } - else { - other.push_back(bound_index); + else if (bound_row_index != row_id) { + other.push_back(row_id); } } - else if (!a.is_zero()) { + else { r.m_alive = false; } } } - return bound_index != UINT_MAX; + return bound_row_index != UINT_MAX; } rational model_based_opt::get_coefficient(unsigned row_id, unsigned var_id) { row const& r = m_rows[row_id]; - SASSERT(!r.m_vars.empty()); + if (r.m_vars.empty()) { + return rational::zero(); + } unsigned lo = 0, hi = r.m_vars.size(); while (lo < hi) { unsigned mid = lo + (hi - lo)/2; @@ -177,7 +205,22 @@ namespace opt { } } + // v0 - v1 <= 0 + // v0 - v2 <= 0 + // v2 >= v1 + // -> v1 - v2 <= 0 + // + // t1 + a1*x <= 0 + // t2 + a2*x <= 0 + // (t2 + a2*x) <= (t1 + a1*x)*a2/a1 + // => t2*a1/a2 - t1 <= 0 + // => t2 - t1*a2/a1 <= 0 + bool model_based_opt::resolve(unsigned row_id1, rational const& a1, unsigned row_id2, unsigned x) { + + SASSERT(a1 == get_coefficient(row_id1, x)); + SASSERT(!a1.is_zero()); + // row1 is of the form a1*x + t1 <~ 0 // row2 is of the form a2*x + t2 <~ 0 // assume that a1, a2 have the same sign. @@ -194,38 +237,20 @@ namespace opt { if (a2.is_zero()) { return false; } - else if (a1.is_pos() && a2.is_pos()) { - multiply(-a1/a2, row_id2); - add(row_id2, row_id1); + if (a1.is_pos() == a2.is_pos()) { + mul_add(row_id2, -a2/a1, row_id1); return true; } - else if (a1.is_neg() && a2.is_neg()) { - NOT_IMPLEMENTED_YET(); - // tbd - return true; - } else { m_rows[row_id2].m_alive = false; return false; } } - - void model_based_opt::multiply(rational const& c, unsigned row_id) { - if (c.is_one()) { - return; - } - row& r = m_rows[row_id]; - SASSERT(r.m_alive); - for (unsigned i = 0; i < r.m_vars.size(); ++i) { - r.m_vars[i].m_coeff *= c; - } - r.m_coeff *= c; - r.m_value *= c; - } - // add row2 to row1, store result in row1. - - void model_based_opt::add(unsigned row_id1, unsigned row_id2) { + // + // set row1 <- row1 + c*row2 + // + void model_based_opt::mul_add(unsigned row_id1, rational const& c, unsigned row_id2) { m_new_vars.reset(); row& r1 = m_rows[row_id1]; row const& r2 = m_rows[row_id2]; @@ -233,75 +258,82 @@ namespace opt { for(; i < r1.m_vars.size() || j < r2.m_vars.size(); ) { if (j == r2.m_vars.size()) { m_new_vars.append(r1.m_vars.size() - i, r1.m_vars.c_ptr() + i); + break; } - else if (i == r1.m_vars.size()) { + if (i == r1.m_vars.size()) { for (; j < r2.m_vars.size(); ++j) { m_new_vars.push_back(r2.m_vars[j]); + m_new_vars.back().m_coeff *= c; if (row_id1 != m_objective_id) { m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); } } + break; + } + + unsigned v1 = r1.m_vars[i].m_id; + unsigned v2 = r2.m_vars[j].m_id; + if (v1 == v2) { + m_new_vars.push_back(r1.m_vars[i]); + m_new_vars.back().m_coeff += c*r2.m_vars[j].m_coeff; + ++i; + ++j; + if (m_new_vars.back().m_coeff.is_zero()) { + m_new_vars.pop_back(); + } + } + else if (v1 < v2) { + m_new_vars.push_back(r1.m_vars[i]); + ++i; } else { - unsigned v1 = r1.m_vars[i].m_id; - unsigned v2 = r2.m_vars[j].m_id; - if (v1 == v2) { - m_new_vars.push_back(r1.m_vars[i]); - m_new_vars.back().m_coeff += r2.m_vars[j].m_coeff; - ++i; - ++j; - if (m_new_vars.back().m_coeff.is_zero()) { - m_new_vars.pop_back(); - } + m_new_vars.push_back(r2.m_vars[j]); + m_new_vars.back().m_coeff *= c; + if (row_id1 != m_objective_id) { + m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); } - else if (v1 < v2) { - m_new_vars.push_back(r1.m_vars[i]); - ++i; - } - else { - m_new_vars.push_back(r2.m_vars[j]); - if (row_id1 != m_objective_id) { - m_var2row_ids[r2.m_vars[j].m_id].push_back(row_id1); - } - ++j; - } - } + ++j; + } } - r1.m_coeff += r2.m_coeff; + r1.m_coeff += c*r2.m_coeff; r1.m_vars.swap(m_new_vars); - r1.m_value += r2.m_value; + r1.m_value += c*r2.m_value; if (r2.m_type == t_lt) { r1.m_type = t_lt; } - SASSERT(invariant(r1)); + SASSERT(invariant(row_id1, r1)); } void model_based_opt::display(std::ostream& out) const { for (unsigned i = 0; i < m_rows.size(); ++i) { display(out, m_rows[i]); } + for (unsigned i = 0; i < m_var2row_ids.size(); ++i) { + unsigned_vector const& rows = m_var2row_ids[i]; + out << i << ": "; + for (unsigned j = 0; j < rows.size(); ++j) { + out << rows[j] << " "; + } + out << "\n"; + } } void model_based_opt::display(std::ostream& out, row const& r) const { vector const& vars = r.m_vars; + out << (r.m_alive?"+":"-") << " "; for (unsigned i = 0; i < vars.size(); ++i) { if (i > 0 && vars[i].m_coeff.is_pos()) { out << "+ "; } out << vars[i].m_coeff << "* v" << vars[i].m_id << " "; } - out << r.m_coeff; - switch (r.m_type) { - case t_eq: - out << " = 0\n"; - break; - case t_lt: - out << " < 0\n"; - break; - case t_le: - out << " <= 0\n"; - break; + if (r.m_coeff.is_pos()) { + out << " + " << r.m_coeff << " "; } + else if (r.m_coeff.is_neg()) { + out << r.m_coeff << " "; + } + out << r.m_type << " 0; value: " << r.m_value << "\n"; } unsigned model_based_opt::add_var(rational const& value) { @@ -311,7 +343,8 @@ namespace opt { return v; } - void model_based_opt::set_row(row& r, vector const& coeffs, rational const& c, ineq_type rel) { + void model_based_opt::set_row(unsigned row_id, vector const& coeffs, rational const& c, ineq_type rel) { + row& r = m_rows[row_id]; rational val(c); SASSERT(r.m_vars.empty()); r.m_vars.append(coeffs.size(), coeffs.c_ptr()); @@ -323,23 +356,21 @@ namespace opt { r.m_coeff = c; r.m_value = val; r.m_type = rel; - SASSERT(invariant(r)); + SASSERT(invariant(row_id, r)); } void model_based_opt::add_constraint(vector const& coeffs, rational const& c, ineq_type rel) { - rational val(c); - row r0; + rational val(c); unsigned row_id = m_rows.size(); - m_rows.push_back(r0); - row& r = m_rows.back(); - set_row(r, coeffs, c, rel); + m_rows.push_back(row()); + set_row(row_id, coeffs, c, rel); for (unsigned i = 0; i < coeffs.size(); ++i) { m_var2row_ids[coeffs[i].m_id].push_back(row_id); } } void model_based_opt::set_objective(vector const& coeffs, rational const& c) { - set_row(objective(), coeffs, c, t_le); + set_row(m_objective_id, coeffs, c, t_le); } } diff --git a/src/math/simplex/model_based_opt.h b/src/math/simplex/model_based_opt.h index 44e3d44bb..6a348ff31 100644 --- a/src/math/simplex/model_based_opt.h +++ b/src/math/simplex/model_based_opt.h @@ -37,6 +37,8 @@ namespace opt { strict, non_strict }; + + class model_based_opt { public: @@ -67,22 +69,20 @@ namespace opt { vector m_new_vars; bool invariant(); - bool invariant(row const& r); + bool invariant(unsigned index, row const& r); row& objective() { return m_rows[0]; } - bool find_bound(unsigned x, unsigned& bound_index, unsigned_vector& other, bool is_pos); + bool find_bound(unsigned x, unsigned& bound_index, rational& bound_coeff, unsigned_vector& other, bool is_pos); rational get_coefficient(unsigned row_id, unsigned var_id); bool resolve(unsigned row_id1, rational const& a1, unsigned row_id2, unsigned x); - void multiply(rational const& c, unsigned row_id); + void mul_add(unsigned row_id1, rational const& c, unsigned row_id2); - void add(unsigned row_id1, unsigned row_id2); - - void set_row(row& r, vector const& coeffs, rational const& c, ineq_type rel); + void set_row(unsigned row_id, vector const& coeffs, rational const& c, ineq_type rel); public: @@ -112,4 +112,8 @@ namespace opt { } +std::ostream& operator<<(std::ostream& out, opt::bound_type bt); +std::ostream& operator<<(std::ostream& out, opt::ineq_type ie); + + #endif diff --git a/src/test/model_based_opt.cpp b/src/test/model_based_opt.cpp index f21627b2d..83e21bf7a 100644 --- a/src/test/model_based_opt.cpp +++ b/src/test/model_based_opt.cpp @@ -1,49 +1,120 @@ #include "model_based_opt.h" +typedef opt::model_based_opt::var var; + +static void add_ineq(opt::model_based_opt& mbo, unsigned x, int a, unsigned y, int b, int k, opt::ineq_type rel) { + vector vars; + vars.push_back(var(x, rational(a))); + vars.push_back(var(y, rational(b))); + mbo.add_constraint(vars, rational(k), rel); +} + +static void add_ineq(opt::model_based_opt& mbo, unsigned x, int a, int k, opt::ineq_type rel) { + vector vars; + vars.push_back(var(x, rational(a))); + mbo.add_constraint(vars, rational(k), rel); +} + +// test with upper bounds static void test1() { opt::model_based_opt mbo; - typedef opt::model_based_opt::var var; vector vars; unsigned x = mbo.add_var(rational(2)); unsigned y = mbo.add_var(rational(3)); unsigned z = mbo.add_var(rational(4)); unsigned u = mbo.add_var(rational(5)); - vars.reset(); - vars.push_back(var(x, rational(1))); - vars.push_back(var(y, rational(-1))); - mbo.add_constraint(vars, rational(0), opt::t_le); - - vars.reset(); - vars.push_back(var(x, rational(1))); - vars.push_back(var(z, rational(-1))); - mbo.add_constraint(vars, rational(0), opt::t_le); - - vars.reset(); - vars.push_back(var(y, rational(1))); - vars.push_back(var(u, rational(-1))); - mbo.add_constraint(vars, rational(0), opt::t_le); - - vars.reset(); - vars.push_back(var(z, rational(1))); - vars.push_back(var(u, rational(-1))); - mbo.add_constraint(vars, rational(-1), opt::t_le); - - vars.reset(); - vars.push_back(var(u, rational(1))); - mbo.add_constraint(vars, rational(4), opt::t_le); + add_ineq(mbo, x, 1, y, -1, 0, opt::t_le); + add_ineq(mbo, x, 1, z, -1, 0, opt::t_le); + add_ineq(mbo, y, 1, u, -1, 0, opt::t_le); + add_ineq(mbo, z, 1, u, -1, 1, opt::t_le); + add_ineq(mbo, u, 1, -6, opt::t_le); vars.reset(); vars.push_back(var(x, rational(2))); mbo.set_objective(vars, rational(0)); rational value; - opt::bound_type bound = mbo.maximize(value); - + opt::bound_type bound = mbo.maximize(value); std::cout << bound << ": " << value << "\n"; - } +// test with lower bounds +static void test2() { + opt::model_based_opt mbo; + vector vars; + unsigned x = mbo.add_var(rational(5)); + unsigned y = mbo.add_var(rational(4)); + unsigned z = mbo.add_var(rational(3)); + unsigned u = mbo.add_var(rational(2)); + + add_ineq(mbo, x, -1, y, 1, 0, opt::t_le); + add_ineq(mbo, x, -1, z, 1, 0, opt::t_le); + add_ineq(mbo, y, -1, u, 1, 0, opt::t_le); + add_ineq(mbo, z, -1, u, 1, 1, opt::t_le); + add_ineq(mbo, u, -1, -6, opt::t_le); + + vars.reset(); + vars.push_back(var(x, rational(-2))); + mbo.set_objective(vars, rational(0)); + + rational value; + opt::bound_type bound = mbo.maximize(value); + std::cout << bound << ": " << value << "\n"; +} + +// test unbounded +static void test3() { + opt::model_based_opt mbo; + vector vars; + unsigned x = mbo.add_var(rational(2)); + unsigned y = mbo.add_var(rational(3)); + unsigned z = mbo.add_var(rational(4)); + unsigned u = mbo.add_var(rational(5)); + + add_ineq(mbo, x, 1, y, -1, 0, opt::t_le); + add_ineq(mbo, x, 1, z, -1, 0, opt::t_le); + add_ineq(mbo, y, 1, u, -1, 0, opt::t_le); + add_ineq(mbo, z, 1, u, -1, 1, opt::t_le); + + vars.reset(); + vars.push_back(var(x, rational(2))); + mbo.set_objective(vars, rational(0)); + + rational value; + opt::bound_type bound = mbo.maximize(value); + std::cout << bound << ": " << value << "\n"; +} + +// test strict +static void test4() { + opt::model_based_opt mbo; + vector vars; + unsigned x = mbo.add_var(rational(2)); + unsigned y = mbo.add_var(rational(3)); + unsigned z = mbo.add_var(rational(4)); + unsigned u = mbo.add_var(rational(5)); + + add_ineq(mbo, x, 1, y, -1, 0, opt::t_lt); + add_ineq(mbo, x, 1, z, -1, 0, opt::t_lt); + add_ineq(mbo, y, 1, u, -1, 0, opt::t_le); + add_ineq(mbo, z, 1, u, -1, 1, opt::t_le); + add_ineq(mbo, u, 1, -6, opt::t_le); + + vars.reset(); + vars.push_back(var(x, rational(2))); + mbo.set_objective(vars, rational(0)); + + rational value; + opt::bound_type bound = mbo.maximize(value); + std::cout << bound << ": " << value << "\n"; +} + +// test with mix of upper and lower bounds + void tst_model_based_opt() { test1(); + test2(); + test3(); + test4(); } From ae2821dea1a5c39a914da6d56aa5f08cd31f2d91 Mon Sep 17 00:00:00 2001 From: xlauko Date: Thu, 28 Apr 2016 21:58:05 +0200 Subject: [PATCH 60/66] Add srem, urem, shift, ext operators to c++ api --- src/api/c++/z3++.h | 47 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index fad4ca7a2..6e8544770 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -876,7 +876,7 @@ namespace z3 { unsigned lo() const { assert (is_app() && Z3_get_decl_num_parameters(ctx(), decl()) == 2); return static_cast(Z3_get_decl_int_parameter(ctx(), decl(), 1)); } unsigned hi() const { assert (is_app() && Z3_get_decl_num_parameters(ctx(), decl()) == 2); return static_cast(Z3_get_decl_int_parameter(ctx(), decl(), 0)); } - /** + /** \brief sequence and regular expression operations. + is overloaeded as sequence concatenation and regular expression union. concat is overloaded to handle sequences and regular expressions @@ -1275,6 +1275,51 @@ namespace z3 { inline expr udiv(expr const & a, int b) { return udiv(a, a.ctx().num_val(b, a.get_sort())); } inline expr udiv(int a, expr const & b) { return udiv(b.ctx().num_val(a, b.get_sort()), b); } + /** + \brief signed reminder operator for bitvectors + */ + inline expr srem(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvsrem(a.ctx(), a, b)); } + inline expr srem(expr const & a, int b) { return srem(a, a.ctx().num_val(b, a.get_sort())); } + inline expr srem(int a, expr const & b) { return srem(b.ctx().num_val(a, b.get_sort()), b); } + + /** + \brief unsigned reminder operator for bitvectors + */ + inline expr urem(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvurem(a.ctx(), a, b)); } + inline expr urem(expr const & a, int b) { return urem(a, a.ctx().num_val(b, a.get_sort())); } + inline expr urem(int a, expr const & b) { return urem(b.ctx().num_val(a, b.get_sort()), b); } + + /** + \brief shift left operator for bitvectors + */ + inline expr shl(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvshl(a.ctx(), a, b)); } + inline expr shl(expr const & a, int b) { return shl(a, a.ctx().num_val(b, a.get_sort())); } + inline expr shl(int a, expr const & b) { return shl(b.ctx().num_val(a, b.get_sort()), b); } + + /** + \brief logic shift right operator for bitvectors + */ + inline expr lshr(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvlshr(a.ctx(), a, b)); } + inline expr lshr(expr const & a, int b) { return lshr(a, a.ctx().num_val(b, a.get_sort())); } + inline expr lshr(int a, expr const & b) { return lshr(b.ctx().num_val(a, b.get_sort()), b); } + + /** + \brief arithmetic shift right operator for bitvectors + */ + inline expr ashr(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvashr(a.ctx(), a, b)); } + inline expr ashr(expr const & a, int b) { return ashr(a, a.ctx().num_val(b, a.get_sort())); } + inline expr ashr(int a, expr const & b) { return ashr(b.ctx().num_val(a, b.get_sort()), b); } + + /** + \brief Extend the given bit-vector with zeros to the (unsigned) equivalent bitvector of size m+i, where m is the size of the given bit-vector. + */ + inline expr zext(expr const & a, unsigned i) { return to_expr(a.ctx(), Z3_mk_zero_ext(a.ctx(), i, a)); } + + /** + \brief Sign-extend of the given bit-vector to the (signed) equivalent bitvector of size m+i, where m is the size of the given bit-vector. + */ + inline expr sext(expr const & a, unsigned i) { return to_expr(a.ctx(), Z3_mk_sign_ext(a.ctx(), i, a)); } + template class cast_ast; template<> class cast_ast { From c75fd02c954e50259f48d67a4da6040a75db6d9a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 28 Apr 2016 21:31:16 -0700 Subject: [PATCH 61/66] qsat-opt Signed-off-by: Nikolaj Bjorner --- src/math/simplex/model_based_opt.cpp | 16 ++--- src/opt/opt_context.cpp | 39 +++++++++++ src/opt/opt_context.h | 5 +- src/qe/qe_arith.cpp | 100 +++++++++++++++++++++++++-- src/qe/qsat.cpp | 18 +---- 5 files changed, 145 insertions(+), 33 deletions(-) diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index f56f327d4..ff6ecf1cd 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -105,13 +105,10 @@ namespace opt { var v = objective().m_vars.back(); unsigned x = v.m_id; rational const& coeff = v.m_coeff; - rational const& x_val = m_var2value[x]; - unsigned_vector const& row_ids = m_var2row_ids[x]; unsigned bound_row_index; rational bound_coeff; other.reset(); if (find_bound(x, bound_row_index, bound_coeff, other, coeff.is_pos())) { - row& r = m_rows[bound_row_index]; SASSERT(!bound_coeff.is_zero()); for (unsigned i = 0; i < other.size(); ++i) { resolve(bound_row_index, bound_coeff, other[i], x); @@ -150,7 +147,7 @@ namespace opt { if (a.is_zero()) { // skip } - else if (a.is_pos() == is_pos) { + else if (a.is_pos() == is_pos || r.m_type == t_eq) { rational value = x_val - (r.m_value/a); if (bound_row_index == UINT_MAX) { lub_val = value; @@ -221,6 +218,7 @@ namespace opt { SASSERT(a1 == get_coefficient(row_id1, x)); SASSERT(!a1.is_zero()); + // // row1 is of the form a1*x + t1 <~ 0 // row2 is of the form a2*x + t2 <~ 0 // assume that a1, a2 have the same sign. @@ -229,20 +227,22 @@ namespace opt { // t1 - a1*t2/a2 <~~ 0 // where <~~ is strict if either <~1 or <~2 is strict. // if a1 is negative, then .... - // - if (!m_rows[row_id2].m_alive) { + // + + row& row2 = m_rows[row_id2]; + if (!row2.m_alive) { return false; } rational a2 = get_coefficient(row_id2, x); if (a2.is_zero()) { return false; } - if (a1.is_pos() == a2.is_pos()) { + if (a1.is_pos() == a2.is_pos() || row2.m_type == t_eq) { mul_add(row_id2, -a2/a1, row_id1); return true; } else { - m_rows[row_id2].m_alive = false; + row2.m_alive = false; return false; } } diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index b4dbb3dd5..8757232d3 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -42,6 +42,7 @@ Notes: #include "filter_model_converter.h" #include "ast_pp_util.h" #include "inc_sat_solver.h" +#include "qsat.h" namespace opt { @@ -1439,4 +1440,42 @@ namespace opt { } } } + + bool context::is_qsat_opt() { + if (m_objectives.size() != 1) { + return false; + } + if (m_objectives[0].m_type != O_MAXIMIZE && + m_objectives[0].m_type != O_MINIMIZE) { + return false; + } + for (unsigned i = 0; i < m_hard_constraints.size(); ++i) { + if (has_quantifiers(m_hard_constraints[i].get())) { + return true; + } + } + return false; + } + + lbool context::run_qsat_opt() { + SASSERT(is_qsat_opt()); + app_ref objective(m); + opt::bound_type bound; + expr_ref value(m); + lbool result = qe::maximize(m_hard_constraints, objective, value, bound, m_params); + if (result != l_undef) { + switch (bound) { + case opt::unbounded: + case opt::strict: + case opt::non_strict: + // set_max + break; + // TBD: + + default: + break; + } + } + return l_undef; + } } diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index e427a487f..b7dceb674 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -289,12 +289,15 @@ namespace opt { void display_benchmark(); - // pareto void yield(); expr_ref mk_ge(expr* t, expr* s); expr_ref mk_cmp(bool is_ge, model_ref& mdl, objective const& obj); + + // quantifiers + bool is_qsat_opt(); + lbool run_qsat_opt(); }; } diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index f1637dfb7..3a0d0bbac 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -28,6 +28,7 @@ Revision History: #include "expr_functors.h" #include "model_v2_pp.h" #include "expr_safe_replace.h" +#include "model_based_opt.h" namespace qe { @@ -98,6 +99,55 @@ namespace qe { } } + void linearize(model& model, opt::model_based_opt& mbo, expr* lit, obj_map& tids) { + obj_map ts; + rational c(0), mul(1); + expr_ref t(m); + opt::ineq_type ty = opt::t_le; + expr* e1, *e2; + bool is_not = m.is_not(lit, lit); + if (is_not) { + mul.neg(); + } + SASSERT(!m.is_not(lit)); + if (a.is_le(lit, e1, e2) || a.is_ge(lit, e2, e1)) { + if (is_not) mul.neg(); + linearize(model, mul, e1, c, ts); + linearize(model, -mul, e2, c, ts); + ty = is_not ? opt::t_lt : opt::t_le; + } + else if (a.is_lt(lit, e1, e2) || a.is_gt(lit, e2, e1)) { + if (is_not) mul.neg(); + linearize(model, mul, e1, c, ts); + linearize(model, -mul, e2, c, ts); + ty = is_not ? opt::t_le: opt::t_lt; + } + else if (m.is_eq(lit, e1, e2) && !is_not && is_arith(e1)) { + linearize(model, mul, e1, c, ts); + linearize(model, -mul, e2, c, ts); + ty = opt::t_eq; + } + else if (m.is_distinct(lit) && !is_not && is_arith(to_app(lit)->get_arg(0))) { + UNREACHABLE(); + } + else if (m.is_distinct(lit) && is_not && is_arith(to_app(lit)->get_arg(0))) { + UNREACHABLE(); + } + else if (m.is_eq(lit, e1, e2) && is_not && is_arith(e1)) { + UNREACHABLE(); + } + else { + return; + } + if (ty == opt::t_lt && is_int()) { + c += rational(1); + ty = opt::t_le; + } + vars coeffs; + extract_coefficients(ts, tids, coeffs); + mbo.add_constraint(coeffs, c, ty); + } + void linearize(model& model, rational const& mul, expr* t, rational& c, obj_map& ts) { expr* t1, *t2, *t3; rational mul1; @@ -913,18 +963,54 @@ namespace qe { return true; } + typedef opt::model_based_opt::var var; + typedef vector vars; + opt::bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { + opt::model_based_opt mbo; + obj_map ts; + obj_map tids; + vars coeffs; rational c(0), mul(1); linearize(mdl, mul, t, c, ts); - - // TBD: - // pick variables one by one from ts. - // m_var = alloc(contains_app, m, v); - // perform upper or lower projection depending on sign of v. - // - return opt::unbounded; + extract_coefficients(ts, tids, coeffs); + mbo.set_objective(coeffs, c); + + for (unsigned i = 0; i < fmls.size(); ++i) { + linearize(mdl, mbo, fmls[i], tids); + } + + rational val; + opt::bound_type result = mbo.maximize(val); + value = a.mk_numeral(val, false); + switch (result) { + case opt::unbounded: + bound = m.mk_false(); + break; + case opt::strict: + bound = a.mk_le(value, t); + break; + case opt::non_strict: + bound = a.mk_lt(value, t); + break; + } + return result; } + + void extract_coefficients(obj_map const& ts, obj_map& tids, vars& coeffs) { + coeffs.reset(); + obj_map::iterator it = ts.begin(), end = ts.end(); + for (; it != end; ++it) { + unsigned id; + if (!tids.find(it->m_key, id)) { + id = tids.size(); + tids.insert(it->m_key, id); + } + coeffs.push_back(var(id, it->m_value)); + } + } + }; arith_project_plugin::arith_project_plugin(ast_manager& m) { diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index a220cf268..76cf62aa5 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -1326,25 +1326,9 @@ namespace qe { void maximize(expr_ref_vector const& core, model& mdl) { TRACE("qe", tout << "maximize: " << core << "\n";); m_was_sat |= !core.empty(); - if (core.empty()) { - m_ex.assert_expr(m.mk_false()); - m_fa.assert_expr(m.mk_false()); - return; - } expr_ref bound(m); m_bound = m_mbp.maximize(core, mdl, m_objective, m_value, bound); - switch (m_bound) { - case opt::unbounded: - m_ex.assert_expr(m.mk_false()); - m_fa.assert_expr(m.mk_false()); - break; - case opt::strict: - m_ex.assert_expr(bound); - break; - case opt::non_strict: - m_ex.assert_expr(bound); - break; - } + m_ex.assert_expr(bound); } }; From 2428bf18f11e25490b45f11826544485a30b7a33 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 29 Apr 2016 19:08:10 -0700 Subject: [PATCH 62/66] add model correction Signed-off-by: Nikolaj Bjorner --- src/math/simplex/model_based_opt.cpp | 79 +++++++++++++++++++++++++--- src/math/simplex/model_based_opt.h | 16 ++++-- src/opt/opt_context.cpp | 31 +++++------ src/opt/optsmt.h | 3 ++ src/qe/qe_arith.cpp | 56 +++++++++++++------- src/qe/qe_arith.h | 5 +- src/qe/qe_mbp.cpp | 8 +-- src/qe/qe_mbp.h | 2 +- src/qe/qsat.cpp | 18 +++---- src/qe/qsat.h | 2 +- src/util/inf_eps_rational.h | 1 + 11 files changed, 156 insertions(+), 65 deletions(-) diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index ff6ecf1cd..989db2cad 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -97,9 +97,10 @@ namespace opt { // e.g. t2/a2 >= t3/a3 // then replace a3*x + t3 by t3/a3 - t2/a2 <= 0 // - bound_type model_based_opt::maximize(rational& value) { + inf_eps model_based_opt::maximize() { SASSERT(invariant()); unsigned_vector other; + unsigned_vector bound_trail, bound_vars; while (!objective().m_vars.empty()) { TRACE("opt", tout << "tableau\n";); var v = objective().m_vars.back(); @@ -120,17 +121,79 @@ namespace opt { mul_add(m_objective_id, - coeff/bound_coeff, bound_row_index); m_rows[bound_row_index].m_alive = false; + bound_trail.push_back(bound_row_index); + bound_vars.push_back(x); } else { - return unbounded; + return inf_eps::infinity(); } } - value = objective().m_value; - if (objective().m_type == t_lt) { - return strict; + // + // update the evaluation of variables to satisfy the bound. + + update_values(bound_vars, bound_trail); + + rational value = objective().m_value; + if (objective().m_type == t_lt) { + return inf_eps(inf_rational(value, rational(-1))); } else { - return non_strict; + return inf_eps(inf_rational(value)); + } + } + + + void model_based_opt::update_values(unsigned_vector const& bound_vars, unsigned_vector const& bound_trail) { + rational eps(0); + for (unsigned i = bound_trail.size(); i > 0; ) { + --i; + unsigned x = bound_vars[i]; + row const& r = m_rows[bound_trail[i]]; + rational val = r.m_coeff; + rational x_coeff; + vector const& vars = r.m_vars; + for (unsigned j = 0; j < vars.size(); ++j) { + var const& v = vars[j]; + if (x = v.m_id) { + x_coeff = v.m_coeff; + } + else { + val += m_var2value[v.m_id]*v.m_coeff; + } + } + SASSERT(!x_coeff.is_zero()); + val /= -x_coeff; + // Adjust epsilon to be s + if (eps.is_zero() || (!val.is_zero() && eps > abs(val))) { + eps = abs(val)/rational(2); + } + if (eps.is_zero() || (!r.m_value.is_zero() && eps > abs(r.m_value))) { + eps = abs(r.m_value)/rational(2); + } + // + // + // ax + t < 0 + // <=> x < -t/a + // <=> x := -t/a - epsilon + // + if (x_coeff.is_pos() && r.m_type == t_lt) { + val -= eps; + } + // + // -ax + t < 0 + // <=> -ax < -t + // <=> -x < -t/a + // <=> x > t/a + // <=> x := t/a + epsilon + // + + if (x_coeff.is_pos() && r.m_type == t_lt) { + val -= eps; + } + else if (x_coeff.is_neg() && r.m_type == t_lt) { + val += eps; + } + m_var2value[x] = val; } } @@ -343,6 +406,10 @@ namespace opt { return v; } + rational model_based_opt::get_value(unsigned var) { + return m_var2value[var]; + } + void model_based_opt::set_row(unsigned row_id, vector const& coeffs, rational const& c, ineq_type rel) { row& r = m_rows[row_id]; rational val(c); diff --git a/src/math/simplex/model_based_opt.h b/src/math/simplex/model_based_opt.h index 6a348ff31..8a1694059 100644 --- a/src/math/simplex/model_based_opt.h +++ b/src/math/simplex/model_based_opt.h @@ -23,6 +23,7 @@ Revision History: #include "util.h" #include "rational.h" +#include"inf_eps_rational.h" namespace opt { @@ -38,7 +39,7 @@ namespace opt { non_strict }; - + typedef inf_eps_rational inf_eps; class model_based_opt { public: @@ -71,8 +72,7 @@ namespace opt { bool invariant(); bool invariant(unsigned index, row const& r); - row& objective() { return m_rows[0]; } - + row& objective() { return m_rows[0]; } bool find_bound(unsigned x, unsigned& bound_index, rational& bound_coeff, unsigned_vector& other, bool is_pos); @@ -83,6 +83,8 @@ namespace opt { void mul_add(unsigned row_id1, rational const& c, unsigned row_id2); void set_row(unsigned row_id, vector const& coeffs, rational const& c, ineq_type rel); + + void update_values(unsigned_vector const& bound_vars, unsigned_vector const& bound_trail); public: @@ -91,6 +93,9 @@ namespace opt { // add a fresh variable with value 'value'. unsigned add_var(rational const& value); + // retrieve updated value of variable. + rational get_value(unsigned var_id); + // add a constraint. We assume that the constraint is // satisfied under the values provided to the variables. void add_constraint(vector const& coeffs, rational const& c, ineq_type r); @@ -98,12 +103,13 @@ namespace opt { // Set the objective function (linear). void set_objective(vector const& coeffs, rational const& c); + // // find a maximal value for the objective function over the current values. // in other words, the returned maximal value may not be globally optimal, // but the current evaluation of variables are used to select a local // optimal. - bound_type maximize(rational& value); - + // + inf_eps maximize(); void display(std::ostream& out) const; void display(std::ostream& out, row const& r) const; diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 8757232d3..c96ba563a 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -1459,23 +1459,20 @@ namespace opt { lbool context::run_qsat_opt() { SASSERT(is_qsat_opt()); - app_ref objective(m); - opt::bound_type bound; - expr_ref value(m); - lbool result = qe::maximize(m_hard_constraints, objective, value, bound, m_params); - if (result != l_undef) { - switch (bound) { - case opt::unbounded: - case opt::strict: - case opt::non_strict: - // set_max - break; - // TBD: - - default: - break; - } + objective const& obj = m_objectives[0]; + app_ref term(obj.m_term); + if (obj.m_type == O_MINIMIZE) { + term = m_arith.mk_uminus(term); } - return l_undef; + inf_eps value; + lbool result = qe::maximize(m_hard_constraints, term, value, m_params); + if (result != l_undef && obj.m_type == O_MINIMIZE) { + value.neg(); + } + if (result != l_undef) { + m_optsmt.update_lower(obj.m_index, value); + m_optsmt.update_upper(obj.m_index, value); + } + return result; } } diff --git a/src/opt/optsmt.h b/src/opt/optsmt.h index d11b84370..e01c58681 100644 --- a/src/opt/optsmt.h +++ b/src/opt/optsmt.h @@ -61,7 +61,9 @@ namespace opt { void get_model(model_ref& mdl, svector& labels); model* get_model(unsigned index) const { return m_models[index]; } + void update_lower(unsigned idx, inf_eps const& r); + void update_upper(unsigned idx, inf_eps const& r); void reset(); @@ -82,6 +84,7 @@ namespace opt { lbool update_upper(); + }; }; diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 3a0d0bbac..243b3dce6 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -965,37 +965,57 @@ namespace qe { typedef opt::model_based_opt::var var; typedef vector vars; + - opt::bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { + opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& bound) { opt::model_based_opt mbo; - + opt::inf_eps value; obj_map ts; obj_map tids; vars coeffs; rational c(0), mul(1); - linearize(mdl, mul, t, c, ts); + linearize(mdl, mul, t, c, ts); extract_coefficients(ts, tids, coeffs); mbo.set_objective(coeffs, c); for (unsigned i = 0; i < fmls.size(); ++i) { linearize(mdl, mbo, fmls[i], tids); } + - rational val; - opt::bound_type result = mbo.maximize(val); - value = a.mk_numeral(val, false); - switch (result) { - case opt::unbounded: + value = mbo.maximize(); + + + + expr_ref val(a.mk_numeral(value.get_rational(), false), m); + if (!value.is_finite()) { bound = m.mk_false(); - break; - case opt::strict: - bound = a.mk_le(value, t); - break; - case opt::non_strict: - bound = a.mk_lt(value, t); - break; + return value; + } + + // update model + ptr_vector vars; + obj_map::iterator it = tids.begin(), end = tids.end(); + for (; it != end; ++it) { + expr* e = it->m_key; + if (is_uninterp_const(e)) { + unsigned id = it->m_value; + func_decl* f = to_app(e)->get_decl(); + expr_ref val(a.mk_numeral(mbo.get_value(id), false), m); + mdl.register_decl(f, val); + } + else { + TRACE("qe", tout << "omitting model update for non-uninterpreted constant " << mk_pp(e, m) << "\n";); + } + } + + if (value.get_infinitesimal().is_neg()) { + bound = a.mk_le(val, t); + } + else { + bound = a.mk_lt(val, t); } - return result; + return value; } void extract_coefficients(obj_map const& ts, obj_map& tids, vars& coeffs) { @@ -1033,8 +1053,8 @@ namespace qe { return m_imp->a.get_family_id(); } - opt::bound_type arith_project_plugin::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { - return m_imp->maximize(fmls, mdl, t, value, bound); + opt::inf_eps arith_project_plugin::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& bound) { + return m_imp->maximize(fmls, mdl, t, bound); } bool arith_project(model& model, app* var, expr_ref_vector& lits) { diff --git a/src/qe/qe_arith.h b/src/qe/qe_arith.h index f79a61245..b89d16d04 100644 --- a/src/qe/qe_arith.h +++ b/src/qe/qe_arith.h @@ -22,14 +22,15 @@ namespace qe { class arith_project_plugin : public project_plugin { struct imp; - imp* m_imp; + imp* m_imp; public: arith_project_plugin(ast_manager& m); virtual ~arith_project_plugin(); virtual bool operator()(model& model, app* var, app_ref_vector& vars, expr_ref_vector& lits); virtual bool solve(model& model, app_ref_vector& vars, expr_ref_vector& lits); virtual family_id get_family_id(); - opt::bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound); + + opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& bound); }; bool arith_project(model& model, app* var, expr_ref_vector& lits); diff --git a/src/qe/qe_mbp.cpp b/src/qe/qe_mbp.cpp index c592445ea..7d5f3800e 100644 --- a/src/qe/qe_mbp.cpp +++ b/src/qe/qe_mbp.cpp @@ -213,9 +213,9 @@ class mbp::impl { public: - opt::bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { + opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& bound) { arith_project_plugin arith(m); - return arith.maximize(fmls, mdl, t, value, bound); + return arith.maximize(fmls, mdl, t, bound); } void extract_literals(model& model, expr_ref_vector& fmls) { @@ -421,6 +421,6 @@ void mbp::extract_literals(model& model, expr_ref_vector& lits) { m_impl->extract_literals(model, lits); } -opt::bound_type mbp::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound) { - return m_impl->maximize(fmls, mdl, t, value, bound); +opt::inf_eps mbp::maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& bound) { + return m_impl->maximize(fmls, mdl, t, bound); } diff --git a/src/qe/qe_mbp.h b/src/qe/qe_mbp.h index 4081288b4..332659c0b 100644 --- a/src/qe/qe_mbp.h +++ b/src/qe/qe_mbp.h @@ -76,7 +76,7 @@ namespace qe { \brief Maximize objective t under current model for constraints in fmls. */ - opt::bound_type maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& value, expr_ref& bound); + opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& bound); }; } diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index 76cf62aa5..03d8987cf 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -1162,8 +1162,7 @@ namespace qe { m_level(0), m_mode(mode), m_avars(m), - m_free_vars(m), - m_value(m) + m_free_vars(m) { reset(); } @@ -1283,17 +1282,15 @@ namespace qe { } app* m_objective; - expr_ref m_value; - opt::bound_type m_bound; + opt::inf_eps m_value; bool m_was_sat; - lbool maximize(expr_ref_vector const& fmls, app* t, expr_ref& value, opt::bound_type& bound) { + lbool maximize(expr_ref_vector const& fmls, app* t, opt::inf_eps& value) { expr_ref_vector defs(m); expr_ref fml = negate_core(fmls); hoist(fml); m_objective = t; - m_value = 0; - m_bound = opt::unbounded; + m_value = opt::inf_eps(); m_was_sat = false; m_pred_abs.abstract_atoms(fml, defs); fml = m_pred_abs.mk_abstract(fml); @@ -1319,7 +1316,6 @@ namespace qe { throw tactic_exception(s.c_str()); } value = m_value; - bound = m_bound; return l_true; } @@ -1327,16 +1323,16 @@ namespace qe { TRACE("qe", tout << "maximize: " << core << "\n";); m_was_sat |= !core.empty(); expr_ref bound(m); - m_bound = m_mbp.maximize(core, mdl, m_objective, m_value, bound); + m_value = m_mbp.maximize(core, mdl, m_objective, bound); m_ex.assert_expr(bound); } }; - lbool maximize(expr_ref_vector const& fmls, app* t, expr_ref& value, opt::bound_type& bound, params_ref const& p) { + lbool maximize(expr_ref_vector const& fmls, app* t, opt::inf_eps& value, params_ref const& p) { ast_manager& m = fmls.get_manager(); qsat qs(m, p, qsat_maximize); - return qs.maximize(fmls, t, value, bound); + return qs.maximize(fmls, t, value); } }; diff --git a/src/qe/qsat.h b/src/qe/qsat.h index fd10e3e75..08a2cbd53 100644 --- a/src/qe/qsat.h +++ b/src/qe/qsat.h @@ -114,7 +114,7 @@ namespace qe { void collect_statistics(statistics& st) const; }; - lbool maximize(expr_ref_vector const& fmls, app* t, expr_ref& value, opt::bound_type& bound, params_ref const& p); + lbool maximize(expr_ref_vector const& fmls, app* t, opt::inf_eps& value, params_ref const& p); } diff --git a/src/util/inf_eps_rational.h b/src/util/inf_eps_rational.h index 4327b47c0..048b6f383 100644 --- a/src/util/inf_eps_rational.h +++ b/src/util/inf_eps_rational.h @@ -23,6 +23,7 @@ Revision History: #include"debug.h" #include"vector.h" #include"rational.h" +#include"inf_rational.h" template class inf_eps_rational { From e29adbf304fc137703c8747f383eca4237b1d8f7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 30 Apr 2016 11:18:34 -0700 Subject: [PATCH 63/66] fix issues #581: nested timeouts canceled each-other Signed-off-by: Nikolaj Bjorner --- src/cmd_context/cmd_context.h | 2 - src/math/simplex/model_based_opt.cpp | 86 +++++++++++----------------- src/math/simplex/model_based_opt.h | 8 +-- src/opt/opt_context.cpp | 7 ++- src/parsers/smt2/smt2parser.cpp | 2 +- src/qe/qe_arith.cpp | 11 ++-- src/qe/qsat.cpp | 7 ++- src/qe/qsat.h | 2 +- src/solver/combined_solver.cpp | 10 ++-- src/util/cancel_eh.h | 8 ++- src/util/rlimit.cpp | 31 +++++++--- src/util/rlimit.h | 9 ++- 12 files changed, 96 insertions(+), 87 deletions(-) diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index be3a1f4f8..976fe946a 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -272,8 +272,6 @@ public: cmd_context(bool main_ctx = true, ast_manager * m = 0, symbol const & l = symbol::null); ~cmd_context(); void set_cancel(bool f); - void cancel() { set_cancel(true); } - void reset_cancel() { set_cancel(false); } context_params & params() { return m_params; } solver_factory &get_solver_factory() { return *m_solver_factory; } solver_factory &get_interpolating_solver_factory() { return *m_interpolating_solver_factory; } diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index 989db2cad..77e552da9 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -20,14 +20,6 @@ Revision History: #include "model_based_opt.h" -std::ostream& operator<<(std::ostream& out, opt::bound_type bt) { - switch (bt) { - case opt::unbounded: return out << "unbounded"; - case opt::strict: return out << "strict"; - case opt::non_strict: return out << "non-strict"; - } - return out; -} std::ostream& operator<<(std::ostream& out, opt::ineq_type ie) { switch (ie) { @@ -128,8 +120,10 @@ namespace opt { return inf_eps::infinity(); } } + // // update the evaluation of variables to satisfy the bound. + // update_values(bound_vars, bound_trail); @@ -164,10 +158,10 @@ namespace opt { SASSERT(!x_coeff.is_zero()); val /= -x_coeff; // Adjust epsilon to be s - if (eps.is_zero() || (!val.is_zero() && eps > abs(val))) { + if (!val.is_zero() && (eps.is_zero() || eps > abs(val))) { eps = abs(val)/rational(2); } - if (eps.is_zero() || (!r.m_value.is_zero() && eps > abs(r.m_value))) { + if (!r.m_value.is_zero() && (eps.is_zero() || eps > abs(r.m_value))) { eps = abs(r.m_value)/rational(2); } // @@ -186,10 +180,6 @@ namespace opt { // <=> x > t/a // <=> x := t/a + epsilon // - - if (x_coeff.is_pos() && r.m_type == t_lt) { - val -= eps; - } else if (x_coeff.is_neg() && r.m_type == t_lt) { val += eps; } @@ -265,48 +255,36 @@ namespace opt { } } - // v0 - v1 <= 0 - // v0 - v2 <= 0 - // v2 >= v1 - // -> v1 - v2 <= 0 // - // t1 + a1*x <= 0 - // t2 + a2*x <= 0 - // (t2 + a2*x) <= (t1 + a1*x)*a2/a1 - // => t2*a1/a2 - t1 <= 0 - // => t2 - t1*a2/a1 <= 0 + // Let + // row1: t1 + a1*x <= 0 + // row2: t2 + a2*x <= 0 + // + // assume a1, a2 have the same signs: + // (t2 + a2*x) <= (t1 + a1*x)*a2/a1 + // <=> t2*a1/a2 - t1 <= 0 + // <=> t2 - t1*a2/a1 <= 0 + // + // assume a1 > 0, -a2 < 0: + // t1 + a1*x <= 0, t2 - a2*x <= 0 + // t2/a2 <= -t1/a1 + // t2 + t1*a2/a1 <= 0 + // assume -a1 < 0, a2 > 0: + // t1 - a1*x <= 0, t2 + a2*x <= 0 + // t1/a1 <= -t2/a2 + // t2 + t1*a2/a1 <= 0 + // + // the resolvent is the same in all cases (simpler proof should exist) + // - bool model_based_opt::resolve(unsigned row_id1, rational const& a1, unsigned row_id2, unsigned x) { + void model_based_opt::resolve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x) { - SASSERT(a1 == get_coefficient(row_id1, x)); + SASSERT(a1 == get_coefficient(row_src, x)); SASSERT(!a1.is_zero()); - - // - // row1 is of the form a1*x + t1 <~ 0 - // row2 is of the form a2*x + t2 <~ 0 - // assume that a1, a2 have the same sign. - // if a1 is positive, then val(t1*a2/a1) <= val(t2*a1/a2) - // replace row2 with the new inequality of the form: - // t1 - a1*t2/a2 <~~ 0 - // where <~~ is strict if either <~1 or <~2 is strict. - // if a1 is negative, then .... - // - row& row2 = m_rows[row_id2]; - if (!row2.m_alive) { - return false; - } - rational a2 = get_coefficient(row_id2, x); - if (a2.is_zero()) { - return false; - } - if (a1.is_pos() == a2.is_pos() || row2.m_type == t_eq) { - mul_add(row_id2, -a2/a1, row_id1); - return true; - } - else { - row2.m_alive = false; - return false; + if (m_rows[row_dst].m_alive) { + rational a2 = get_coefficient(row_dst, x); + mul_add(row_dst, -a2/a1, row_src); } } @@ -314,6 +292,9 @@ namespace opt { // set row1 <- row1 + c*row2 // void model_based_opt::mul_add(unsigned row_id1, rational const& c, unsigned row_id2) { + if (c.is_zero()) { + return; + } m_new_vars.reset(); row& r1 = m_rows[row_id1]; row const& r2 = m_rows[row_id2]; @@ -364,6 +345,9 @@ namespace opt { if (r2.m_type == t_lt) { r1.m_type = t_lt; } + else if (r2.m_type == t_le && r1.m_type == t_eq) { + r1.m_type = t_le; + } SASSERT(invariant(row_id1, r1)); } diff --git a/src/math/simplex/model_based_opt.h b/src/math/simplex/model_based_opt.h index 8a1694059..4efcf8f8f 100644 --- a/src/math/simplex/model_based_opt.h +++ b/src/math/simplex/model_based_opt.h @@ -33,11 +33,6 @@ namespace opt { t_le }; - enum bound_type { - unbounded, - strict, - non_strict - }; typedef inf_eps_rational inf_eps; @@ -78,7 +73,7 @@ namespace opt { rational get_coefficient(unsigned row_id, unsigned var_id); - bool resolve(unsigned row_id1, rational const& a1, unsigned row_id2, unsigned x); + void resolve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x); void mul_add(unsigned row_id1, rational const& c, unsigned row_id2); @@ -118,7 +113,6 @@ namespace opt { } -std::ostream& operator<<(std::ostream& out, opt::bound_type bt); std::ostream& operator<<(std::ostream& out, opt::ineq_type ie); diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index c96ba563a..4d65b9a25 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -238,6 +238,11 @@ namespace opt { import_scoped_state(); normalize(); internalize(); +#if 0 + if (is_qsat_opt()) { + return run_qsat_opt(); + } +#endif update_solver(); solver& s = get_solver(); s.assert_expr(m_hard_constraints); @@ -1465,7 +1470,7 @@ namespace opt { term = m_arith.mk_uminus(term); } inf_eps value; - lbool result = qe::maximize(m_hard_constraints, term, value, m_params); + lbool result = qe::maximize(m_hard_constraints, term, value, m_model, m_params); if (result != l_undef && obj.m_type == O_MINIMIZE) { value.neg(); } diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 71ef2a06e..2e98a4072 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -403,7 +403,7 @@ namespace smt2 { void check_float(char const * msg) { if (!curr_is_float()) throw parser_exception(msg); } void error(unsigned line, unsigned pos, char const * msg) { - m_ctx.reset_cancel(); + m_ctx.set_cancel(false); if (use_vs_format()) { m_ctx.diagnostic_stream() << "Z3(" << line << ", " << pos << "): ERROR: " << msg; if (msg[strlen(msg)-1] != '\n') diff --git a/src/qe/qe_arith.cpp b/src/qe/qe_arith.cpp index 243b3dce6..60111a473 100644 --- a/src/qe/qe_arith.cpp +++ b/src/qe/qe_arith.cpp @@ -968,32 +968,34 @@ namespace qe { opt::inf_eps maximize(expr_ref_vector const& fmls, model& mdl, app* t, expr_ref& bound) { + SASSERT(a.is_real(t)); opt::model_based_opt mbo; opt::inf_eps value; obj_map ts; obj_map tids; + + // extract objective function. vars coeffs; rational c(0), mul(1); linearize(mdl, mul, t, c, ts); extract_coefficients(ts, tids, coeffs); mbo.set_objective(coeffs, c); + // extract linear constraints for (unsigned i = 0; i < fmls.size(); ++i) { linearize(mdl, mbo, fmls[i], tids); } - + // find optimal value value = mbo.maximize(); - - expr_ref val(a.mk_numeral(value.get_rational(), false), m); if (!value.is_finite()) { bound = m.mk_false(); return value; } - // update model + // update model to use new values that satisfy optimality ptr_vector vars; obj_map::iterator it = tids.begin(), end = tids.end(); for (; it != end; ++it) { @@ -1009,6 +1011,7 @@ namespace qe { } } + // update the predicate 'bound' which forces larger values. if (value.get_infinitesimal().is_neg()) { bound = a.mk_le(val, t); } diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index 03d8987cf..74b1101fe 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -1285,7 +1285,7 @@ namespace qe { opt::inf_eps m_value; bool m_was_sat; - lbool maximize(expr_ref_vector const& fmls, app* t, opt::inf_eps& value) { + lbool maximize(expr_ref_vector const& fmls, app* t, model_ref& mdl, opt::inf_eps& value) { expr_ref_vector defs(m); expr_ref fml = negate_core(fmls); hoist(fml); @@ -1299,6 +1299,7 @@ namespace qe { m_ex.assert_expr(fml); m_fa.assert_expr(m.mk_not(fml)); lbool is_sat = check_sat(); + mdl = m_model.get(); switch (is_sat) { case l_false: if (!m_was_sat) { @@ -1329,10 +1330,10 @@ namespace qe { }; - lbool maximize(expr_ref_vector const& fmls, app* t, opt::inf_eps& value, params_ref const& p) { + lbool maximize(expr_ref_vector const& fmls, app* t, opt::inf_eps& value, model_ref& mdl, params_ref const& p) { ast_manager& m = fmls.get_manager(); qsat qs(m, p, qsat_maximize); - return qs.maximize(fmls, t, value); + return qs.maximize(fmls, t, mdl, value); } }; diff --git a/src/qe/qsat.h b/src/qe/qsat.h index 08a2cbd53..456711c4f 100644 --- a/src/qe/qsat.h +++ b/src/qe/qsat.h @@ -114,7 +114,7 @@ namespace qe { void collect_statistics(statistics& st) const; }; - lbool maximize(expr_ref_vector const& fmls, app* t, opt::inf_eps& value, params_ref const& p); + lbool maximize(expr_ref_vector const& fmls, app* t, opt::inf_eps& value, model_ref& mdl, params_ref const& p); } diff --git a/src/solver/combined_solver.cpp b/src/solver/combined_solver.cpp index e19d7a1e3..4e645116c 100644 --- a/src/solver/combined_solver.cpp +++ b/src/solver/combined_solver.cpp @@ -83,9 +83,14 @@ private: solver * m_solver; volatile bool m_canceled; aux_timeout_eh(solver * s):m_solver(s), m_canceled(false) {} + ~aux_timeout_eh() { + if (m_canceled) { + m_solver->get_manager().limit().dec_cancel(); + } + } virtual void operator()() { - m_solver->get_manager().limit().cancel(); m_canceled = true; + m_solver->get_manager().limit().inc_cancel(); } }; @@ -225,9 +230,6 @@ public: if ((r != l_undef || !use_solver1_when_undef()) && !eh.m_canceled) { return r; } - if (eh.m_canceled) { - m_solver1->get_manager().limit().reset_cancel(); - } } IF_VERBOSE(PS_VB_LVL, verbose_stream() << "(combined-solver \"solver 2 failed, trying solver1\")\n";); } diff --git a/src/util/cancel_eh.h b/src/util/cancel_eh.h index 6b8fc8351..d23e5f544 100644 --- a/src/util/cancel_eh.h +++ b/src/util/cancel_eh.h @@ -26,12 +26,14 @@ Revision History: */ template class cancel_eh : public event_handler { + bool m_canceled; T & m_obj; public: - cancel_eh(T & o):m_obj(o) {} - ~cancel_eh() { m_obj.reset_cancel(); } + cancel_eh(T & o): m_canceled(false), m_obj(o) {} + ~cancel_eh() { if (m_canceled) m_obj.dec_cancel(); } virtual void operator()() { - m_obj.cancel(); + m_canceled = true; + m_obj.inc_cancel(); } }; diff --git a/src/util/rlimit.cpp b/src/util/rlimit.cpp index fa34a9555..b3f055955 100644 --- a/src/util/rlimit.cpp +++ b/src/util/rlimit.cpp @@ -31,7 +31,7 @@ uint64 reslimit::count() const { bool reslimit::inc() { ++m_count; - return !m_cancel && (m_limit == 0 || m_count <= m_limit); + return m_cancel == 0 && (m_limit == 0 || m_count <= m_limit); } bool reslimit::inc(unsigned offset) { @@ -46,7 +46,7 @@ void reslimit::push(unsigned delta_limit) { } m_limits.push_back(m_limit); m_limit = m_limit==0?new_limit:std::min(new_limit, m_limit); - m_cancel = false; + m_cancel = 0; } void reslimit::pop() { @@ -55,11 +55,11 @@ void reslimit::pop() { } m_limit = m_limits.back(); m_limits.pop_back(); - m_cancel = false; + m_cancel = 0; } char const* reslimit::get_cancel_msg() const { - if (m_cancel) { + if (m_cancel > 0) { return Z3_CANCELED_MSG; } else { @@ -84,7 +84,7 @@ void reslimit::pop_child() { void reslimit::cancel() { #pragma omp critical (reslimit_cancel) { - set_cancel(true); + set_cancel(m_cancel+1); } } @@ -92,11 +92,28 @@ void reslimit::cancel() { void reslimit::reset_cancel() { #pragma omp critical (reslimit_cancel) { - set_cancel(false); + set_cancel(0); } } -void reslimit::set_cancel(bool f) { +void reslimit::inc_cancel() { + #pragma omp critical (reslimit_cancel) + { + set_cancel(m_cancel+1); + } +} + + +void reslimit::dec_cancel() { + #pragma omp critical (reslimit_cancel) + { + if (m_cancel > 0) { + set_cancel(m_cancel-1); + } + } +} + +void reslimit::set_cancel(unsigned f) { m_cancel = f; for (unsigned i = 0; i < m_children.size(); ++i) { m_children[i]->set_cancel(f); diff --git a/src/util/rlimit.h b/src/util/rlimit.h index c16a8d49b..ac5db6136 100644 --- a/src/util/rlimit.h +++ b/src/util/rlimit.h @@ -22,13 +22,13 @@ Revision History: #include "vector.h" class reslimit { - volatile bool m_cancel; + volatile unsigned m_cancel; uint64 m_count; uint64 m_limit; svector m_limits; ptr_vector m_children; - void set_cancel(bool f); + void set_cancel(unsigned f); public: reslimit(); @@ -42,10 +42,13 @@ public: uint64 count() const; - bool get_cancel_flag() const { return m_cancel; } + bool get_cancel_flag() const { return m_cancel > 0; } char const* get_cancel_msg() const; void cancel(); void reset_cancel(); + + void inc_cancel(); + void dec_cancel(); }; class scoped_rlimit { From 4b940bde111e1e5d3984772a49650fd197f0dabc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 30 Apr 2016 11:46:25 -0700 Subject: [PATCH 64/66] fix compilation of unit tests Signed-off-by: Nikolaj Bjorner --- src/test/model_based_opt.cpp | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/test/model_based_opt.cpp b/src/test/model_based_opt.cpp index 83e21bf7a..ad1ff1a85 100644 --- a/src/test/model_based_opt.cpp +++ b/src/test/model_based_opt.cpp @@ -34,9 +34,8 @@ static void test1() { vars.push_back(var(x, rational(2))); mbo.set_objective(vars, rational(0)); - rational value; - opt::bound_type bound = mbo.maximize(value); - std::cout << bound << ": " << value << "\n"; + opt::inf_eps value = mbo.maximize(); + std::cout << value << "\n"; } // test with lower bounds @@ -58,9 +57,8 @@ static void test2() { vars.push_back(var(x, rational(-2))); mbo.set_objective(vars, rational(0)); - rational value; - opt::bound_type bound = mbo.maximize(value); - std::cout << bound << ": " << value << "\n"; + opt::inf_eps value = mbo.maximize(); + std::cout << value << "\n"; } // test unbounded @@ -81,9 +79,9 @@ static void test3() { vars.push_back(var(x, rational(2))); mbo.set_objective(vars, rational(0)); - rational value; - opt::bound_type bound = mbo.maximize(value); - std::cout << bound << ": " << value << "\n"; + opt::inf_eps value = mbo.maximize(); + std::cout << value << "\n"; + } // test strict @@ -105,9 +103,8 @@ static void test4() { vars.push_back(var(x, rational(2))); mbo.set_objective(vars, rational(0)); - rational value; - opt::bound_type bound = mbo.maximize(value); - std::cout << bound << ": " << value << "\n"; + opt::inf_eps value = mbo.maximize(); + std::cout << value << "\n"; } // test with mix of upper and lower bounds From 22507281cf25d79911fc796e1c3d6bf3e4c8edb0 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 30 Apr 2016 12:23:46 -0700 Subject: [PATCH 65/66] fix model generation in opt Signed-off-by: Nikolaj Bjorner --- src/math/simplex/model_based_opt.cpp | 60 +++++++++++++++++----------- src/test/model_based_opt.cpp | 8 ++++ 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index 77e552da9..abc93a0b9 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -142,49 +142,61 @@ namespace opt { for (unsigned i = bound_trail.size(); i > 0; ) { --i; unsigned x = bound_vars[i]; - row const& r = m_rows[bound_trail[i]]; + row& r = m_rows[bound_trail[i]]; rational val = r.m_coeff; + rational x_val; rational x_coeff; vector const& vars = r.m_vars; for (unsigned j = 0; j < vars.size(); ++j) { var const& v = vars[j]; - if (x = v.m_id) { + if (x == v.m_id) { x_coeff = v.m_coeff; } else { val += m_var2value[v.m_id]*v.m_coeff; } } + TRACE("opt", display(tout << "v" << x << " val: " << val + << " coeff_x: " << x_coeff << " val_x: " << m_var2value[x] << " ", r); ); SASSERT(!x_coeff.is_zero()); - val /= -x_coeff; - // Adjust epsilon to be s - if (!val.is_zero() && (eps.is_zero() || eps > abs(val))) { - eps = abs(val)/rational(2); - } - if (!r.m_value.is_zero() && (eps.is_zero() || eps > abs(r.m_value))) { - eps = abs(r.m_value)/rational(2); - } + x_val = -val/x_coeff; // // // ax + t < 0 // <=> x < -t/a // <=> x := -t/a - epsilon // - if (x_coeff.is_pos() && r.m_type == t_lt) { - val -= eps; + if (r.m_type == t_lt) { + // Adjust epsilon to be + if (!x_val.is_zero() && (eps.is_zero() || eps >= abs(x_val))) { + eps = abs(x_val)/rational(2); + } + if (!r.m_value.is_zero() && (eps.is_zero() || eps >= abs(r.m_value))) { + eps = abs(r.m_value)/rational(2); + } + + SASSERT(!eps.is_zero()); + if (x_coeff.is_pos()) { + x_val -= eps; + } + // + // -ax + t < 0 + // <=> -ax < -t + // <=> -x < -t/a + // <=> x > t/a + // <=> x := t/a + epsilon + // + else if (x_coeff.is_neg()) { + x_val += eps; + } } - // - // -ax + t < 0 - // <=> -ax < -t - // <=> -x < -t/a - // <=> x > t/a - // <=> x := t/a + epsilon - // - else if (x_coeff.is_neg() && r.m_type == t_lt) { - val += eps; - } - m_var2value[x] = val; - } + m_var2value[x] = x_val; + r.m_value = (x_val * x_coeff) + val; + + TRACE("opt", display(tout << "v" << x << " val: " << val << " coeff_x: " + << x_coeff << " val_x: " << m_var2value[x] << " ", r); ); + SASSERT(invariant(bound_trail[i], r)); + } } bool model_based_opt::find_bound(unsigned x, unsigned& bound_row_index, rational& bound_coeff, unsigned_vector& other, bool is_pos) { diff --git a/src/test/model_based_opt.cpp b/src/test/model_based_opt.cpp index ad1ff1a85..94ac81222 100644 --- a/src/test/model_based_opt.cpp +++ b/src/test/model_based_opt.cpp @@ -36,6 +36,10 @@ static void test1() { opt::inf_eps value = mbo.maximize(); std::cout << value << "\n"; + std::cout << "x: " << mbo.get_value(x) << "\n"; + std::cout << "y: " << mbo.get_value(y) << "\n"; + std::cout << "z: " << mbo.get_value(z) << "\n"; + std::cout << "u: " << mbo.get_value(u) << "\n"; } // test with lower bounds @@ -105,6 +109,10 @@ static void test4() { opt::inf_eps value = mbo.maximize(); std::cout << value << "\n"; + std::cout << "x: " << mbo.get_value(x) << "\n"; + std::cout << "y: " << mbo.get_value(y) << "\n"; + std::cout << "z: " << mbo.get_value(z) << "\n"; + std::cout << "u: " << mbo.get_value(u) << "\n"; } // test with mix of upper and lower bounds From 67e49b4adc22b53576f63ee02cb373a8306e60b3 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 1 May 2016 17:15:20 -0700 Subject: [PATCH 66/66] fixing model-based-opt Signed-off-by: Nikolaj Bjorner --- src/math/simplex/model_based_opt.cpp | 38 ++++++---- src/math/simplex/model_based_opt.h | 2 +- src/test/model_based_opt.cpp | 106 ++++++++++++++++++++++++++- 3 files changed, 130 insertions(+), 16 deletions(-) diff --git a/src/math/simplex/model_based_opt.cpp b/src/math/simplex/model_based_opt.cpp index abc93a0b9..e5db201a8 100644 --- a/src/math/simplex/model_based_opt.cpp +++ b/src/math/simplex/model_based_opt.cpp @@ -19,7 +19,7 @@ Revision History: --*/ #include "model_based_opt.h" - +#include "uint_set.h" std::ostream& operator<<(std::ostream& out, opt::ineq_type ie) { switch (ie) { @@ -94,7 +94,7 @@ namespace opt { unsigned_vector other; unsigned_vector bound_trail, bound_vars; while (!objective().m_vars.empty()) { - TRACE("opt", tout << "tableau\n";); + TRACE("opt", display(tout << "tableau\n");); var v = objective().m_vars.back(); unsigned x = v.m_id; rational const& coeff = v.m_coeff; @@ -111,7 +111,7 @@ namespace opt { // => coeff*x <= -t2*coeff/a2 // objective + t2*coeff/a2 <= ub - mul_add(m_objective_id, - coeff/bound_coeff, bound_row_index); + mul_add(false, m_objective_id, - coeff/bound_coeff, bound_row_index); m_rows[bound_row_index].m_alive = false; bound_trail.push_back(bound_row_index); bound_vars.push_back(x); @@ -204,8 +204,13 @@ namespace opt { rational lub_val; rational const& x_val = m_var2value[x]; unsigned_vector const& row_ids = m_var2row_ids[x]; + uint_set visited; for (unsigned i = 0; i < row_ids.size(); ++i) { unsigned row_id = row_ids[i]; + if (visited.contains(row_id)) { + continue; + } + visited.insert(row_id); row& r = m_rows[row_id]; if (r.m_alive) { rational a = get_coefficient(row_id, x); @@ -219,13 +224,15 @@ namespace opt { bound_row_index = row_id; bound_coeff = a; } - else if ((is_pos && value < lub_val) || (!is_pos && value > lub_val)) { + else if ((value == lub_val && r.m_type == opt::t_lt) || + (is_pos && value < lub_val) || + (!is_pos && value > lub_val)) { other.push_back(bound_row_index); lub_val = value; bound_row_index = row_id; bound_coeff = a; } - else if (bound_row_index != row_id) { + else { other.push_back(row_id); } } @@ -253,11 +260,14 @@ namespace opt { } if (id < var_id) { lo = mid + 1; - } + } else { - hi = mid - 1; + hi = mid; } } + if (lo == r.m_vars.size()) { + return rational::zero(); + } unsigned id = r.m_vars[lo].m_id; if (id == var_id) { return r.m_vars[lo].m_coeff; @@ -293,17 +303,18 @@ namespace opt { SASSERT(a1 == get_coefficient(row_src, x)); SASSERT(!a1.is_zero()); - + SASSERT(row_src != row_dst); + if (m_rows[row_dst].m_alive) { rational a2 = get_coefficient(row_dst, x); - mul_add(row_dst, -a2/a1, row_src); + mul_add(row_dst != m_objective_id && a1.is_pos() == a2.is_pos(), row_dst, -a2/a1, row_src); } } // // set row1 <- row1 + c*row2 // - void model_based_opt::mul_add(unsigned row_id1, rational const& c, unsigned row_id2) { + void model_based_opt::mul_add(bool same_sign, unsigned row_id1, rational const& c, unsigned row_id2) { if (c.is_zero()) { return; } @@ -354,11 +365,12 @@ namespace opt { r1.m_coeff += c*r2.m_coeff; r1.m_vars.swap(m_new_vars); r1.m_value += c*r2.m_value; - if (r2.m_type == t_lt) { + + if (!same_sign && r2.m_type == t_lt) { r1.m_type = t_lt; } - else if (r2.m_type == t_le && r1.m_type == t_eq) { - r1.m_type = t_le; + else if (same_sign && r1.m_type == t_lt && r2.m_type == t_lt) { + r1.m_type = t_le; } SASSERT(invariant(row_id1, r1)); } diff --git a/src/math/simplex/model_based_opt.h b/src/math/simplex/model_based_opt.h index 4efcf8f8f..b48a96edb 100644 --- a/src/math/simplex/model_based_opt.h +++ b/src/math/simplex/model_based_opt.h @@ -75,7 +75,7 @@ namespace opt { void resolve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x); - void mul_add(unsigned row_id1, rational const& c, unsigned row_id2); + void mul_add(bool same_sign, unsigned row_id1, rational const& c, unsigned row_id2); void set_row(unsigned row_id, vector const& coeffs, rational const& c, ineq_type rel); diff --git a/src/test/model_based_opt.cpp b/src/test/model_based_opt.cpp index 94ac81222..8c4148638 100644 --- a/src/test/model_based_opt.cpp +++ b/src/test/model_based_opt.cpp @@ -1,20 +1,121 @@ #include "model_based_opt.h" +#include "util.h" +#include "uint_set.h" typedef opt::model_based_opt::var var; -static void add_ineq(opt::model_based_opt& mbo, unsigned x, int a, unsigned y, int b, int k, opt::ineq_type rel) { +static void add_ineq(opt::model_based_opt& mbo, unsigned x, int a, int k, opt::ineq_type rel) { + vector vars; + vars.push_back(var(x, rational(a))); + mbo.add_constraint(vars, rational(k), rel); +} + +static void add_ineq(opt::model_based_opt& mbo, + unsigned x, int a, + unsigned y, int b, int k, + opt::ineq_type rel) { vector vars; vars.push_back(var(x, rational(a))); vars.push_back(var(y, rational(b))); mbo.add_constraint(vars, rational(k), rel); } -static void add_ineq(opt::model_based_opt& mbo, unsigned x, int a, int k, opt::ineq_type rel) { +static void add_ineq(opt::model_based_opt& mbo, + unsigned x, int a, + unsigned y, int b, + unsigned z, int c, int k, + opt::ineq_type rel) { vector vars; vars.push_back(var(x, rational(a))); + vars.push_back(var(y, rational(b))); + vars.push_back(var(z, rational(c))); mbo.add_constraint(vars, rational(k), rel); } +static void add_random_ineq(opt::model_based_opt& mbo, + random_gen& r, + svector const& values, + unsigned max_vars, + unsigned max_coeff) { + unsigned num_vars = values.size(); + uint_set used_vars; + vector vars; + int value = 0; + for (unsigned i = 0; i < max_vars; ++i) { + unsigned x = r(num_vars); + if (used_vars.contains(x)) { + continue; + } + used_vars.insert(x); + int coeff = r(max_coeff + 1); + if (coeff == 0) { + continue; + } + unsigned sign = r(2); + coeff = sign == 0 ? coeff : -coeff; + vars.push_back(var(x, rational(coeff))); + value += coeff*values[x]; + } + unsigned abs_value = value < 0 ? - value : value; + // value + k <= 0 + // k <= - value + // range for k is 2*|value| + // k <= - value - range + opt::ineq_type rel = opt::t_le; + + int coeff = 0; + if (r(4) == 0) { + rel = opt::t_eq; + coeff = -value; + } + else { + if (abs_value > 0) { + coeff = -value - r(2*abs_value); + } + else { + coeff = 0; + } + if (coeff != -value && r(3) == 0) { + rel = opt::t_lt; + } + } + mbo.add_constraint(vars, rational(coeff), rel); +} + +static void check_random_ineqs(random_gen& r, unsigned num_vars, unsigned max_value, unsigned num_ineqs, unsigned max_vars, unsigned max_coeff) { + opt::model_based_opt mbo; + svector values; + for (unsigned i = 0; i < num_vars; ++i) { + values.push_back(r(max_value + 1)); + mbo.add_var(rational(values.back())); + } + for (unsigned i = 0; i < num_ineqs; ++i) { + add_random_ineq(mbo, r, values, max_vars, max_coeff); + } + + vector vars; + vars.reset(); + vars.push_back(var(0, rational(2))); + vars.push_back(var(1, rational(-2))); + mbo.set_objective(vars, rational(0)); + + mbo.display(std::cout); + opt::inf_eps value = mbo.maximize(); + std::cout << "optimal: " << value << "\n"; + mbo.display(std::cout); + for (unsigned i = 0; i < values.size(); ++i) { + std::cout << i << ": " << values[i] << " -> " << mbo.get_value(i) << "\n"; + } +} + +static void check_random_ineqs() { + random_gen r(1); + + for (unsigned i = 0; i < 1009; ++i) { + check_random_ineqs(r, 4, 5, 5, 3, 6); + } +} + // test with upper bounds static void test1() { opt::model_based_opt mbo; @@ -118,6 +219,7 @@ static void test4() { // test with mix of upper and lower bounds void tst_model_based_opt() { + check_random_ineqs(); test1(); test2(); test3();