From b68a38ff96685071d931defd92ba5e9052c5237a Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 8 May 2018 14:53:02 -0700 Subject: [PATCH 001/138] fixes for re.loop in theory_str --- src/smt/theory_str.cpp | 44 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 126594b06..531572502 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -1829,6 +1829,14 @@ namespace smt { u.str.is_string(range1, range1val); u.str.is_string(range2, range2val); return zstring("[") + range1val + zstring("-") + range2val + zstring("]"); + } else if (u.re.is_loop(a_regex)) { + expr * body; + unsigned lo, hi; + u.re.is_loop(a_regex, body, lo, hi); + rational rLo(lo); + rational rHi(hi); + zstring bodyStr = get_std_regex_str(body); + return zstring("(") + bodyStr + zstring("{") + zstring(rLo.to_string().c_str()) + zstring(",") + zstring(rHi.to_string().c_str()) + zstring("})"); } else if (u.re.is_full_seq(a_regex)) { return zstring("(.*)"); } else if (u.re.is_full_char(a_regex)) { @@ -6644,6 +6652,7 @@ namespace smt { ENSURE(u.is_re(re)); expr * sub1; expr * sub2; + unsigned lo, hi; if (u.re.is_to_re(re, sub1)) { SASSERT(u.str.is_string(sub1)); zstring str; @@ -6662,6 +6671,9 @@ namespace smt { } else if (u.re.is_star(re, sub1) || u.re.is_plus(re, sub1)) { unsigned cx = estimate_regex_complexity(sub1); return _qmul(2, cx); + } else if (u.re.is_loop(re, sub1, lo, hi)) { + unsigned cx = estimate_regex_complexity(sub1); + return _qadd(lo, cx); } else if (u.re.is_range(re, sub1, sub2)) { SASSERT(u.str.is_string(sub1)); SASSERT(u.str.is_string(sub2)); @@ -6683,6 +6695,7 @@ namespace smt { ENSURE(u.is_re(re)); expr * sub1; expr * sub2; + unsigned lo, hi; if (u.re.is_to_re(re, sub1)) { SASSERT(u.str.is_string(sub1)); zstring str; @@ -6701,7 +6714,7 @@ namespace smt { unsigned cx1 = estimate_regex_complexity_under_complement(sub1); unsigned cx2 = estimate_regex_complexity_under_complement(sub2); return _qmul(cx1, cx2); - } else if (u.re.is_star(re, sub1) || u.re.is_plus(re, sub1)) { + } else if (u.re.is_star(re, sub1) || u.re.is_plus(re, sub1) || u.re.is_loop(re, sub1, lo, hi)) { unsigned cx = estimate_regex_complexity_under_complement(sub1); return _qmul(2, cx); } else if (u.re.is_range(re, sub1, sub2)) { @@ -6735,6 +6748,7 @@ namespace smt { bool theory_str::check_regex_length_linearity_helper(expr * re, bool already_star) { expr * sub1; expr * sub2; + unsigned lo, hi; if (u.re.is_to_re(re)) { return true; } else if (u.re.is_concat(re, sub1, sub2)) { @@ -6756,6 +6770,8 @@ namespace smt { } else if (u.re.is_complement(re)) { // TODO can we do better? return false; + } else if (u.re.is_loop(re, sub1, lo, hi)) { + return check_regex_length_linearity_helper(sub1, already_star); } else { TRACE("str", tout << "WARNING: unknown regex term " << mk_pp(re, get_manager()) << std::endl;); UNREACHABLE(); return false; @@ -6766,6 +6782,7 @@ namespace smt { void theory_str::check_subterm_lengths(expr * re, integer_set & lens) { expr * sub1; expr * sub2; + unsigned lo, hi; if (u.re.is_to_re(re, sub1)) { SASSERT(u.str.is_string(sub1)); zstring(str); @@ -6820,6 +6837,14 @@ namespace smt { lens.reset(); } else if (u.re.is_complement(re)) { lens.reset(); + } else if (u.re.is_loop(re, sub1, lo, hi)) { + integer_set lens_1; + check_subterm_lengths(sub1, lens_1); + for (unsigned i = lo; i <= hi; ++i) { + for (auto j : lens_1) { + lens.insert(i * j); + } + } } else { TRACE("str", tout << "WARNING: unknown regex term " << mk_pp(re, get_manager()) << std::endl;); lens.reset(); @@ -6841,6 +6866,7 @@ namespace smt { ast_manager & m = get_manager(); expr * sub1; expr * sub2; + unsigned lo, hi; if (u.re.is_to_re(re, sub1)) { SASSERT(u.str.is_string(sub1)); zstring str; @@ -6909,6 +6935,22 @@ namespace smt { expr_ref retval(ctx.mk_eq_atom(lenVar, m_autil.mk_add_simplify(sum_terms)), m); return retval; } + } else if (u.re.is_loop(re, sub1, lo, hi)) { + expr * v1 = mk_int_var("rlen"); + freeVariables.push_back(v1); + expr_ref r1 = infer_all_regex_lengths(v1, sub1, freeVariables); + expr_ref_vector v1_choices(m); + for (unsigned i = lo; i <= hi; ++i) { + rational rI(i); + expr_ref v1_i(ctx.mk_eq_atom(lenVar, m_autil.mk_mul(m_autil.mk_numeral(rI, true), v1)), m); + v1_choices.push_back(v1_i); + } + expr_ref_vector finalResult(m); + finalResult.push_back(r1); + finalResult.push_back(mk_or(v1_choices)); + expr_ref retval(mk_and(finalResult), m); + SASSERT(retval); + return retval; } else if (u.re.is_range(re, sub1, sub2)) { SASSERT(u.str.is_string(sub1)); SASSERT(u.str.is_string(sub2)); From c65dbaea90b6f9d632b8ffbc61f41b54f7aa906e Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 7 Aug 2018 15:12:37 -0400 Subject: [PATCH 002/138] z3str3: fix contains-indexof precondition --- src/smt/theory_str.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index a6ef6eac2..aadcb63a7 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -1388,7 +1388,7 @@ namespace smt { // (but don't introduce it if it isn't already in the instance) expr_ref haystack(ex->get_arg(0), m), needle(ex->get_arg(1), m), startIdx(ex->get_arg(2), m); expr_ref zeroAst(mk_int(0), m); - // (H contains N) <==> (H indexof N, i) >= 0 + // (H contains N) <==> (H indexof N, 0) >= 0 expr_ref premise(u.str.mk_contains(haystack, needle), m); ctx.internalize(premise, false); expr_ref conclusion(m_autil.mk_ge(ex, zeroAst), m); @@ -1482,14 +1482,23 @@ namespace smt { { // heuristic: integrate with str.contains information // (but don't introduce it if it isn't already in the instance) - // (H contains N) <==> (H indexof N, i) >= 0 + // (0 <= i < len(H)) ==> (H contains N) <==> (H indexof N, i) >= 0 + expr_ref precondition1(m_autil.mk_gt(i, minus_one), m); + //expr_ref precondition2(m_autil.mk_lt(i, mk_strlen(H)), m); + expr_ref precondition2(m.mk_not(m_autil.mk_ge(m_autil.mk_add(i, m_autil.mk_mul(minus_one, mk_strlen(H))), zero)), m); + expr_ref _precondition(m.mk_and(precondition1, precondition2), m); + expr_ref precondition(_precondition); + th_rewriter rw(m); + rw(precondition); + expr_ref premise(u.str.mk_contains(H, N), m); ctx.internalize(premise, false); expr_ref conclusion(m_autil.mk_ge(e, zero), m); expr_ref containsAxiom(ctx.mk_eq_atom(premise, conclusion), m); - SASSERT(containsAxiom); + expr_ref finalAxiom(rewrite_implication(precondition, containsAxiom), m); + SASSERT(finalAxiom); // we can't assert this during init_search as it breaks an invariant if the instance becomes inconsistent - m_delayed_assertions_todo.push_back(containsAxiom); + m_delayed_assertions_todo.push_back(finalAxiom); } } From 0aca1ad4c1ee2337baf079899d6fdbfb66d5661a Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 10 Aug 2018 17:37:23 -0500 Subject: [PATCH 003/138] feat(smt/dt): expose the configuration param for datatype case splits --- src/smt/params/smt_params.cpp | 1 + src/smt/params/smt_params_helper.pyg | 3 ++- src/smt/params/theory_datatype_params.h | 9 +++++---- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/smt/params/smt_params.cpp b/src/smt/params/smt_params.cpp index 3ad20cf90..650afda25 100644 --- a/src/smt/params/smt_params.cpp +++ b/src/smt/params/smt_params.cpp @@ -63,6 +63,7 @@ void smt_params::updt_params(params_ref const & p) { theory_bv_params::updt_params(p); theory_pb_params::updt_params(p); // theory_array_params::updt_params(p); + theory_datatype_params::updt_params(p); updt_local_params(p); } diff --git a/src/smt/params/smt_params_helper.pyg b/src/smt/params/smt_params_helper.pyg index 3f4105c34..76e9f03b1 100644 --- a/src/smt/params/smt_params_helper.pyg +++ b/src/smt/params/smt_params_helper.pyg @@ -94,5 +94,6 @@ def_module_params(module_name='smt', ('core.extend_patterns', BOOL, False, 'extend unsat core with literals that trigger (potential) quantifier instances'), ('core.extend_patterns.max_distance', UINT, UINT_MAX, 'limits the distance of a pattern-extended unsat core'), ('core.extend_nonlocal_patterns', BOOL, False, 'extend unsat cores with literals that have quantifiers with patterns that contain symbols which are not in the quantifier\'s body'), - ('lemma_gc_strategy', UINT, 0, 'lemma garbage collection strategy: 0 - fixed, 1 - geometric, 2 - at restart, 3 - none') + ('lemma_gc_strategy', UINT, 0, 'lemma garbage collection strategy: 0 - fixed, 1 - geometric, 2 - at restart, 3 - none'), + ('dt_lazy_splits', UINT, 1, 'How lazy datatype splits are performed: 0- eager, 1- lazy for infinite types, 2- lazy') )) diff --git a/src/smt/params/theory_datatype_params.h b/src/smt/params/theory_datatype_params.h index 9f801e46c..d2d851ffe 100644 --- a/src/smt/params/theory_datatype_params.h +++ b/src/smt/params/theory_datatype_params.h @@ -19,6 +19,8 @@ Revision History: #ifndef THEORY_DATATYPE_PARAMS_H_ #define THEORY_DATATYPE_PARAMS_H_ +#include "smt/params/smt_params_helper.hpp" + struct theory_datatype_params { unsigned m_dt_lazy_splits; @@ -26,11 +28,10 @@ struct theory_datatype_params { m_dt_lazy_splits(1) { } -#if 0 - void register_params(ini_params & p) { - p.register_unsigned_param("dt_lazy_splits", m_dt_lazy_splits, "How lazy datatype splits are performed: 0- eager, 1- lazy for infinite types, 2- lazy"); + void updt_params(params_ref const & _p) { + smt_params_helper p(_p); + m_dt_lazy_splits = p.dt_lazy_splits(); } -#endif void display(std::ostream & out) const { out << "m_dt_lazy_splits=" << m_dt_lazy_splits << std::endl; } }; From 8de8c4cade20d4df070725b53646bf912820c702 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 11 Aug 2018 11:41:06 -0700 Subject: [PATCH 004/138] fix #1798 Signed-off-by: Nikolaj Bjorner --- src/smt/theory_arith_core.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 54f61a09a..c11c54b69 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -504,9 +504,9 @@ namespace smt { tout << "lower: " << lower << "\n"; tout << "upper: " << upper << "\n";); - mk_axiom(eqz, eq, !is_numeral); - mk_axiom(eqz, lower, !is_numeral); - mk_axiom(eqz, upper, !is_numeral); + mk_axiom(eqz, eq, true); + mk_axiom(eqz, lower, false); + mk_axiom(eqz, upper, false); rational k; context& ctx = get_context(); (void)ctx; From 96d3b98a441b4ee52b160f6f8fa3f2b286cc81a2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 11 Aug 2018 13:33:09 -0700 Subject: [PATCH 005/138] fix #1783, wronge clausification of negated pb inequalities. Signs were ignored Signed-off-by: Nikolaj Bjorner --- src/sat/tactic/goal2sat.cpp | 20 ++++++++++++++++++-- src/tactic/arith/lia2card_tactic.cpp | 3 ++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index 7faa89370..dc4dfc3a8 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -451,7 +451,15 @@ struct goal2sat::imp { unsigned sz = m_result_stack.size(); if (root) { m_result_stack.reset(); - m_ext->add_pb_ge(sat::null_bool_var, wlits, k.get_unsigned()); + unsigned k1 = k.get_unsigned(); + if (sign) { + k1 = 1 - k1; + for (wliteral& wl : wlits) { + wl.second.neg(); + k1 += wl.first; + } + } + m_ext->add_pb_ge(sat::null_bool_var, wlits, k1); } else { sat::bool_var v = m_solver.mk_var(true); @@ -476,7 +484,15 @@ struct goal2sat::imp { unsigned sz = m_result_stack.size(); if (root) { m_result_stack.reset(); - m_ext->add_pb_ge(sat::null_bool_var, wlits, k.get_unsigned()); + unsigned k1 = k.get_unsigned(); + if (sign) { + k1 = 1 - k1; + for (wliteral& wl : wlits) { + wl.second.neg(); + k1 += wl.first; + } + } + m_ext->add_pb_ge(sat::null_bool_var, wlits, k1); } else { sat::bool_var v = m_solver.mk_var(true); diff --git a/src/tactic/arith/lia2card_tactic.cpp b/src/tactic/arith/lia2card_tactic.cpp index ec18ae2bf..88e4a5583 100644 --- a/src/tactic/arith/lia2card_tactic.cpp +++ b/src/tactic/arith/lia2card_tactic.cpp @@ -167,7 +167,8 @@ public: m_mc.reset(); expr_ref_vector axioms(m); expr_safe_replace rep(m); - + + TRACE("pb", g->display(tout);); tactic_report report("lia2card", *g); bound_manager bounds(m); From 95963f71f43e5d722e8941aff602047f28bc5b9a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 11 Aug 2018 17:18:11 -0700 Subject: [PATCH 006/138] fix bug introduced in fix of #1798 Signed-off-by: Nikolaj Bjorner --- src/smt/theory_arith_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index c11c54b69..f96b6228b 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -506,7 +506,7 @@ namespace smt { mk_axiom(eqz, eq, true); mk_axiom(eqz, lower, false); - mk_axiom(eqz, upper, false); + mk_axiom(eqz, upper, !m_util.is_numeral(abs_divisor)); rational k; context& ctx = get_context(); (void)ctx; From 2b2f193f2bae61ee1921a86049eb47a66a71ed8a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Aug 2018 22:26:14 -0700 Subject: [PATCH 007/138] remove dependency on ARRAYSIZE for issue #1616 Signed-off-by: Nikolaj Bjorner --- src/api/api_datalog.cpp | 96 +++++++++++++++++++++++++++++++- src/api/api_qe.cpp | 40 ++++++------- src/ast/ast_smt_pp.cpp | 2 +- src/ast/format.cpp | 12 +--- src/ast/proofs/proof_checker.cpp | 2 +- src/smt/theory_arith_pp.h | 2 +- src/test/bit_blaster.cpp | 2 +- src/test/for_each_file.cpp | 3 +- src/util/stopwatch.h | 4 -- src/util/string_buffer.h | 13 +++-- src/util/util.h | 10 +++- 11 files changed, 137 insertions(+), 49 deletions(-) diff --git a/src/api/api_datalog.cpp b/src/api/api_datalog.cpp index b0a4def55..a95f1d8b1 100644 --- a/src/api/api_datalog.cpp +++ b/src/api/api_datalog.cpp @@ -623,6 +623,100 @@ extern "C" { to_fixedpoint_ref(d)->ctx().add_constraint(to_expr(e), lvl); } -#include "api_datalog_spacer.inc" + Z3_lbool Z3_API Z3_fixedpoint_query_from_lvl (Z3_context c, Z3_fixedpoint d, Z3_ast q, unsigned lvl) { + Z3_TRY; + LOG_Z3_fixedpoint_query_from_lvl (c, d, q, lvl); + RESET_ERROR_CODE(); + lbool r = l_undef; + unsigned timeout = to_fixedpoint(d)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); + unsigned rlimit = to_fixedpoint(d)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()); + { + scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); + cancel_eh eh(mk_c(c)->m().limit()); + api::context::set_interruptable si(*(mk_c(c)), eh); + scoped_timer timer(timeout, &eh); + try { + r = to_fixedpoint_ref(d)->ctx().query_from_lvl (to_expr(q), lvl); + } + catch (z3_exception& ex) { + mk_c(c)->handle_exception(ex); + r = l_undef; + } + to_fixedpoint_ref(d)->ctx().cleanup(); + } + return of_lbool(r); + Z3_CATCH_RETURN(Z3_L_UNDEF); + } + + Z3_ast Z3_API Z3_fixedpoint_get_ground_sat_answer(Z3_context c, Z3_fixedpoint d) { + Z3_TRY; + LOG_Z3_fixedpoint_get_ground_sat_answer(c, d); + RESET_ERROR_CODE(); + expr* e = to_fixedpoint_ref(d)->ctx().get_ground_sat_answer(); + mk_c(c)->save_ast_trail(e); + RETURN_Z3(of_expr(e)); + Z3_CATCH_RETURN(nullptr); + } + + Z3_ast_vector Z3_API Z3_fixedpoint_get_rules_along_trace( + Z3_context c, + Z3_fixedpoint d) + { + Z3_TRY; + LOG_Z3_fixedpoint_get_rules_along_trace(c, d); + ast_manager& m = mk_c(c)->m(); + Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, *mk_c(c), m); + mk_c(c)->save_object(v); + expr_ref_vector rules(m); + svector names; + + to_fixedpoint_ref(d)->ctx().get_rules_along_trace_as_formulas(rules, names); + for (unsigned i = 0; i < rules.size(); ++i) { + v->m_ast_vector.push_back(rules[i].get()); + } + RETURN_Z3(of_ast_vector(v)); + Z3_CATCH_RETURN(nullptr); + } + + Z3_symbol Z3_API Z3_fixedpoint_get_rule_names_along_trace( + Z3_context c, + Z3_fixedpoint d) + { + Z3_TRY; + LOG_Z3_fixedpoint_get_rule_names_along_trace(c, d); + ast_manager& m = mk_c(c)->m(); + Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, *mk_c(c), m); + mk_c(c)->save_object(v); + expr_ref_vector rules(m); + svector names; + std::stringstream ss; + + to_fixedpoint_ref(d)->ctx().get_rules_along_trace_as_formulas(rules, names); + for (unsigned i = 0; i < names.size(); ++i) { + ss << ";" << names[i].str(); + } + RETURN_Z3(of_symbol(symbol(ss.str().substr(1).c_str()))); + Z3_CATCH_RETURN(nullptr); + } + + void Z3_API Z3_fixedpoint_add_invariant(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred, Z3_ast property) { + Z3_TRY; + LOG_Z3_fixedpoint_add_invariant(c, d, pred, property); + RESET_ERROR_CODE(); + to_fixedpoint_ref(d)->ctx ().add_invariant(to_func_decl(pred), to_expr(property)); + Z3_CATCH; + } + + Z3_ast Z3_API Z3_fixedpoint_get_reachable(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred) { + Z3_TRY; + LOG_Z3_fixedpoint_get_reachable(c, d, pred); + RESET_ERROR_CODE(); + expr_ref r = to_fixedpoint_ref(d)->ctx().get_reachable(to_func_decl(pred)); + mk_c(c)->save_ast_trail(r); + RETURN_Z3(of_expr(r.get())); + Z3_CATCH_RETURN(nullptr); + } + + }; diff --git a/src/api/api_qe.cpp b/src/api/api_qe.cpp index 94e83144f..10ba9faa0 100644 --- a/src/api/api_qe.cpp +++ b/src/api/api_qe.cpp @@ -1,5 +1,5 @@ /*++ -Copyright (c) 2017 Arie Gurfinkel +Copyright (c) Microsoft Corporation, Arive Gurfinkel 2017 Module Name: @@ -70,39 +70,39 @@ extern "C" } Z3_ast Z3_API Z3_qe_model_project_skolem (Z3_context c, - Z3_model m, + Z3_model mdl, unsigned num_bounds, Z3_app const bound[], Z3_ast body, Z3_ast_map map) { Z3_TRY; - LOG_Z3_qe_model_project_skolem (c, m, num_bounds, bound, body, map); + LOG_Z3_qe_model_project_skolem (c, mdl, num_bounds, bound, body, map); RESET_ERROR_CODE(); - ast_manager& man = mk_c(c)->m (); - app_ref_vector vars(man); + ast_manager& m = mk_c(c)->m(); + app_ref_vector vars(m); if (!to_apps(num_bounds, bound, vars)) { RETURN_Z3(nullptr); } - expr_ref result (mk_c(c)->m ()); + expr_ref result (m); result = to_expr (body); - model_ref model (to_model_ref (m)); - expr_map emap (man); + model_ref model (to_model_ref (mdl)); + expr_map emap (m); - spacer::qe_project (mk_c(c)->m (), vars, result, model, emap); - mk_c(c)->save_ast_trail (result.get ()); + spacer::qe_project(m, vars, result, model, emap); + mk_c(c)->save_ast_trail(result); obj_map &map_z3 = to_ast_map_ref(map); - for (expr_map::iterator it = emap.begin(), end = emap.end(); it != end; ++it){ - man.inc_ref(&(it->get_key())); - man.inc_ref(it->get_value()); - map_z3.insert(&(it->get_key()), it->get_value()); + for (auto& kv : emap) { + m.inc_ref(kv.m_key); + m.inc_ref(kv.m_value); + map_z3.insert(kv.m_key, kv.m_value); } - return of_expr (result.get ()); + return of_expr (result); Z3_CATCH_RETURN(nullptr); } @@ -124,9 +124,9 @@ extern "C" expr_ref result (mk_c(c)->m ()); result = mk_and (lits); - mk_c(c)->save_ast_trail (result.get ()); + mk_c(c)->save_ast_trail (result); - return of_expr (result.get ()); + return of_expr (result); Z3_CATCH_RETURN(nullptr); } @@ -138,8 +138,8 @@ extern "C" ast_ref_vector &vVars = to_ast_vector_ref (vars); app_ref_vector vApps (mk_c(c)->m()); - for (unsigned i = 0; i < vVars.size (); ++i) { - app *a = to_app (vVars.get (i)); + for (ast* v : vVars) { + app * a = to_app(v); if (a->get_kind () != AST_APP) { SET_ERROR_CODE(Z3_INVALID_ARG, nullptr); RETURN_Z3(nullptr); @@ -162,7 +162,7 @@ extern "C" } } - mk_c(c)->save_ast_trail (result.get ()); + mk_c(c)->save_ast_trail (result); return of_expr (result); Z3_CATCH_RETURN(nullptr); } diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp index 2cce53e3a..5e2828adf 100644 --- a/src/ast/ast_smt_pp.cpp +++ b/src/ast/ast_smt_pp.cpp @@ -117,7 +117,7 @@ bool smt_renaming::all_is_legal(char const* s) { } smt_renaming::smt_renaming() { - for (unsigned i = 0; i < ARRAYSIZE(m_predef_names); ++i) { + for (unsigned i = 0; i < Z3_ARRAYSIZE(m_predef_names); ++i) { symbol s(m_predef_names[i]); m_translate.insert(s, sym_b(s, false)); m_rev_translate.insert(s, s); diff --git a/src/ast/format.cpp b/src/ast/format.cpp index 40df437ea..4aeaf183d 100644 --- a/src/ast/format.cpp +++ b/src/ast/format.cpp @@ -152,21 +152,13 @@ namespace format_ns { format * mk_int(ast_manager & m, int i) { char buffer[128]; -#ifdef _WINDOWS - sprintf_s(buffer, ARRAYSIZE(buffer), "%d", i); -#else - sprintf(buffer, "%d", i); -#endif + SPRINTF_D(buffer, i); return mk_string(m, buffer); } format * mk_unsigned(ast_manager & m, unsigned u) { char buffer[128]; -#ifdef _WINDOWS - sprintf_s(buffer, ARRAYSIZE(buffer), "%u", u); -#else - sprintf(buffer, "%u", u); -#endif + SPRINTF_U(buffer, u); return mk_string(m, buffer); } diff --git a/src/ast/proofs/proof_checker.cpp b/src/ast/proofs/proof_checker.cpp index baea760fa..08d69ff25 100644 --- a/src/ast/proofs/proof_checker.cpp +++ b/src/ast/proofs/proof_checker.cpp @@ -1248,7 +1248,7 @@ void proof_checker::dump_proof(proof const* pr) { void proof_checker::dump_proof(unsigned num_antecedents, expr * const * antecedents, expr * consequent) { char buffer[128]; #ifdef _WINDOWS - sprintf_s(buffer, ARRAYSIZE(buffer), "proof_lemma_%d.smt2", m_proof_lemma_id); + sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "proof_lemma_%d.smt2", m_proof_lemma_id); #else sprintf(buffer, "proof_lemma_%d.smt2", m_proof_lemma_id); #endif diff --git a/src/smt/theory_arith_pp.h b/src/smt/theory_arith_pp.h index 59d386292..b6ddaecf8 100644 --- a/src/smt/theory_arith_pp.h +++ b/src/smt/theory_arith_pp.h @@ -544,7 +544,7 @@ namespace smt { char buffer[128]; static int id = 0; #ifdef _WINDOWS - sprintf_s(buffer, ARRAYSIZE(buffer), "arith_%d.smt", id); + sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "arith_%d.smt", id); #else sprintf(buffer, "arith_%d.smt", id); #endif diff --git a/src/test/bit_blaster.cpp b/src/test/bit_blaster.cpp index c6853ae1e..b4c7a8e76 100644 --- a/src/test/bit_blaster.cpp +++ b/src/test/bit_blaster.cpp @@ -27,7 +27,7 @@ void mk_bits(ast_manager & m, char const * prefix, unsigned sz, expr_ref_vector for (unsigned i = 0; i < sz; ++i) { char buffer[128]; #ifdef _WINDOWS - sprintf_s(buffer, ARRAYSIZE(buffer), "%s%d.smt", prefix, i); + sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "%s%d.smt", prefix, i); #else sprintf(buffer, "%s%d.smt", prefix, i); #endif diff --git a/src/test/for_each_file.cpp b/src/test/for_each_file.cpp index eeec69156..7b7a45042 100644 --- a/src/test/for_each_file.cpp +++ b/src/test/for_each_file.cpp @@ -21,6 +21,7 @@ Revision History: #include #include #include +#include "util/util.h" #include "test/for_each_file.h" bool for_each_file(for_each_file_proc& proc, const char* base, const char* suffix) @@ -36,7 +37,7 @@ bool for_each_file(for_each_file_proc& proc, const char* base, const char* suffi while (h != INVALID_HANDLE_VALUE) { - StringCchPrintfA(buffer, ARRAYSIZE(buffer), "%s\\%s", base, data.cFileName); + StringCchPrintfA(buffer, Z3_ARRAYSIZE(buffer), "%s\\%s", base, data.cFileName); if (!proc(buffer)) { return false; diff --git a/src/util/stopwatch.h b/src/util/stopwatch.h index 83e03a2f7..c606824b4 100644 --- a/src/util/stopwatch.h +++ b/src/util/stopwatch.h @@ -23,8 +23,6 @@ Revision History: #if defined(_WINDOWS) || defined(_CYGWIN) // Does this redefinition work? -#define ARRAYSIZE_TEMP ARRAYSIZE -#undef ARRAYSIZE #include @@ -68,8 +66,6 @@ public: } }; -#undef ARRAYSIZE -#define ARRAYSIZE ARRAYSIZE_TEMP #undef max #undef min diff --git a/src/util/string_buffer.h b/src/util/string_buffer.h index 28ce84fc4..db49d4d82 100644 --- a/src/util/string_buffer.h +++ b/src/util/string_buffer.h @@ -43,6 +43,7 @@ class string_buffer { m_capacity = new_capacity; m_buffer = new_buffer; } + static const unsigned c_buffer_size = 24; public: string_buffer(): @@ -80,9 +81,9 @@ public: } void append(int n) { - char buffer[24]; + char buffer[c_buffer_size]; #ifdef _WINDOWS - sprintf_s(buffer, ARRAYSIZE(buffer), "%d", n); + sprintf_s(buffer, c_buffer_size, "%d", n); #else sprintf(buffer, "%d", n); #endif @@ -90,9 +91,9 @@ public: } void append(unsigned n) { - char buffer[24]; + char buffer[c_buffer_size]; #ifdef _WINDOWS - sprintf_s(buffer, ARRAYSIZE(buffer), "%d", n); + sprintf_s(buffer, c_buffer_size, "%d", n); #else sprintf(buffer, "%d", n); #endif @@ -100,9 +101,9 @@ public: } void append(long n) { - char buffer[24]; + char buffer[c_buffer_size]; #ifdef _WINDOWS - sprintf_s(buffer, ARRAYSIZE(buffer), "%ld", n); + sprintf_s(buffer, c_buffer_size, "%ld", n); #else sprintf(buffer, "%ld", n); #endif diff --git a/src/util/util.h b/src/util/util.h index 12fd2fe3b..4ccf76289 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -49,12 +49,18 @@ static_assert(sizeof(int64_t) == 8, "64 bits"); #ifdef _WINDOWS #define SSCANF sscanf_s #define SPRINTF sprintf_s +#define SPRINTF_D(_buffer_, _i_) sprintf_s(_buffer_, Z3_ARRAYSIZE(_buffer_), "%d", _i_) +#define SPRINTF_U(_buffer_, _u_) sprintf_s(_buffer_, Z3_ARRAYSIZE(_buffer_), "%u", _u_) #define _Exit exit #else #define SSCANF sscanf #define SPRINTF sprintf +#define SPRINTF_D(_buffer_, _i_) sprintf(_buffer_, "%d", _i_) +#define SPRINTF_U(_buffer_, _u_) sprintf(_buffer_, "%u", _u_) #endif + + #define VEC2PTR(_x_) ((_x_).size() ? &(_x_)[0] : 0) #ifdef _WINDOWS @@ -142,9 +148,7 @@ static inline uint64_t shift_left(uint64_t x, uint64_t y) { template char (*ArraySizer(T (&)[N]))[N]; // For determining the length of an array. See ARRAYSIZE() macro. This function is never actually called. -#ifndef ARRAYSIZE -#define ARRAYSIZE(a) sizeof(*ArraySizer(a)) -#endif +#define Z3_ARRAYSIZE(a) sizeof(*ArraySizer(a)) template void display(std::ostream & out, const IT & begin, const IT & end, const char * sep, bool & first) { From 056ec2d5c4ea49cd964e6faa7f7274e2975ac031 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Aug 2018 22:27:07 -0700 Subject: [PATCH 008/138] remove inc file Signed-off-by: Nikolaj Bjorner --- src/api/api_datalog_spacer.inc | 113 --------------------------------- 1 file changed, 113 deletions(-) delete mode 100644 src/api/api_datalog_spacer.inc diff --git a/src/api/api_datalog_spacer.inc b/src/api/api_datalog_spacer.inc deleted file mode 100644 index 1888d4b96..000000000 --- a/src/api/api_datalog_spacer.inc +++ /dev/null @@ -1,113 +0,0 @@ -/*++ -Copyright (c) 2017 Arie Gurfinkel - -Module Name: - - api_datalog_spacer.inc - -Abstract: - - Spacer-specific datalog API - -Author: - - Arie Gurfinkel (arie) - -Notes: - this file is included at the bottom of api_datalog.cpp - ---*/ - Z3_lbool Z3_API Z3_fixedpoint_query_from_lvl (Z3_context c, Z3_fixedpoint d, Z3_ast q, unsigned lvl) { - Z3_TRY; - LOG_Z3_fixedpoint_query_from_lvl (c, d, q, lvl); - RESET_ERROR_CODE(); - lbool r = l_undef; - unsigned timeout = to_fixedpoint(d)->m_params.get_uint("timeout", mk_c(c)->get_timeout()); - unsigned rlimit = to_fixedpoint(d)->m_params.get_uint("rlimit", mk_c(c)->get_rlimit()); - { - scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); - cancel_eh eh(mk_c(c)->m().limit()); - api::context::set_interruptable si(*(mk_c(c)), eh); - scoped_timer timer(timeout, &eh); - try { - r = to_fixedpoint_ref(d)->ctx().query_from_lvl (to_expr(q), lvl); - } - catch (z3_exception& ex) { - mk_c(c)->handle_exception(ex); - r = l_undef; - } - to_fixedpoint_ref(d)->ctx().cleanup(); - } - return of_lbool(r); - Z3_CATCH_RETURN(Z3_L_UNDEF); - } - - Z3_ast Z3_API Z3_fixedpoint_get_ground_sat_answer(Z3_context c, Z3_fixedpoint d) { - Z3_TRY; - LOG_Z3_fixedpoint_get_ground_sat_answer(c, d); - RESET_ERROR_CODE(); - expr* e = to_fixedpoint_ref(d)->ctx().get_ground_sat_answer(); - mk_c(c)->save_ast_trail(e); - RETURN_Z3(of_expr(e)); - Z3_CATCH_RETURN(nullptr); - } - - Z3_ast_vector Z3_API Z3_fixedpoint_get_rules_along_trace( - Z3_context c, - Z3_fixedpoint d) - { - Z3_TRY; - LOG_Z3_fixedpoint_get_rules_along_trace(c, d); - ast_manager& m = mk_c(c)->m(); - Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, *mk_c(c), m); - mk_c(c)->save_object(v); - expr_ref_vector rules(m); - svector names; - - to_fixedpoint_ref(d)->ctx().get_rules_along_trace_as_formulas(rules, names); - for (unsigned i = 0; i < rules.size(); ++i) { - v->m_ast_vector.push_back(rules[i].get()); - } - RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(nullptr); - } - - Z3_symbol Z3_API Z3_fixedpoint_get_rule_names_along_trace( - Z3_context c, - Z3_fixedpoint d) - { - Z3_TRY; - LOG_Z3_fixedpoint_get_rule_names_along_trace(c, d); - ast_manager& m = mk_c(c)->m(); - Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, *mk_c(c), m); - mk_c(c)->save_object(v); - expr_ref_vector rules(m); - svector names; - std::stringstream ss; - - to_fixedpoint_ref(d)->ctx().get_rules_along_trace_as_formulas(rules, names); - for (unsigned i = 0; i < names.size(); ++i) { - ss << ";" << names[i].str(); - } - RETURN_Z3(of_symbol(symbol(ss.str().substr(1).c_str()))); - Z3_CATCH_RETURN(nullptr); - } - - void Z3_API Z3_fixedpoint_add_invariant(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred, Z3_ast property) { - Z3_TRY; - LOG_Z3_fixedpoint_add_invariant(c, d, pred, property); - RESET_ERROR_CODE(); - to_fixedpoint_ref(d)->ctx ().add_invariant(to_func_decl(pred), to_expr(property)); - Z3_CATCH; - } - - Z3_ast Z3_API Z3_fixedpoint_get_reachable(Z3_context c, Z3_fixedpoint d, Z3_func_decl pred) { - Z3_TRY; - LOG_Z3_fixedpoint_get_reachable(c, d, pred); - RESET_ERROR_CODE(); - expr_ref r = to_fixedpoint_ref(d)->ctx().get_reachable(to_func_decl(pred)); - mk_c(c)->save_ast_trail(r); - RETURN_Z3(of_expr(r.get())); - Z3_CATCH_RETURN(nullptr); - } - From 12f9336fec018b092af57ef4db330842b7394f24 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 15 Aug 2018 22:44:07 -0700 Subject: [PATCH 009/138] disable unused macros Signed-off-by: Nikolaj Bjorner --- src/util/string_buffer.h | 15 ++++----------- src/util/util.h | 4 ++-- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/src/util/string_buffer.h b/src/util/string_buffer.h index db49d4d82..6b94e3686 100644 --- a/src/util/string_buffer.h +++ b/src/util/string_buffer.h @@ -43,6 +43,7 @@ class string_buffer { m_capacity = new_capacity; m_buffer = new_buffer; } + static const unsigned c_buffer_size = 24; public: @@ -82,28 +83,20 @@ public: void append(int n) { char buffer[c_buffer_size]; -#ifdef _WINDOWS - sprintf_s(buffer, c_buffer_size, "%d", n); -#else - sprintf(buffer, "%d", n); -#endif + SPRINTF_D(buffer, n); append(buffer); } void append(unsigned n) { char buffer[c_buffer_size]; -#ifdef _WINDOWS - sprintf_s(buffer, c_buffer_size, "%d", n); -#else - sprintf(buffer, "%d", n); -#endif + SPRINTF_U(buffer, n); append(buffer); } void append(long n) { char buffer[c_buffer_size]; #ifdef _WINDOWS - sprintf_s(buffer, c_buffer_size, "%ld", n); + sprintf_s(buffer, Z3_ARRAYSIZE(buffer), "%ld", n); #else sprintf(buffer, "%ld", n); #endif diff --git a/src/util/util.h b/src/util/util.h index 4ccf76289..b9f3bdc28 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -48,13 +48,13 @@ static_assert(sizeof(int64_t) == 8, "64 bits"); #ifdef _WINDOWS #define SSCANF sscanf_s -#define SPRINTF sprintf_s +// #define SPRINTF sprintf_s #define SPRINTF_D(_buffer_, _i_) sprintf_s(_buffer_, Z3_ARRAYSIZE(_buffer_), "%d", _i_) #define SPRINTF_U(_buffer_, _u_) sprintf_s(_buffer_, Z3_ARRAYSIZE(_buffer_), "%u", _u_) #define _Exit exit #else #define SSCANF sscanf -#define SPRINTF sprintf +// #define SPRINTF sprintf #define SPRINTF_D(_buffer_, _i_) sprintf(_buffer_, "%d", _i_) #define SPRINTF_U(_buffer_, _u_) sprintf(_buffer_, "%u", _u_) #endif From 5141f05e6dd7a2460aced4dc0b421889e0c83938 Mon Sep 17 00:00:00 2001 From: Simon Cruanes Date: Fri, 17 Aug 2018 14:58:44 -0500 Subject: [PATCH 010/138] fix(union-find): keep values and representative in consistent order the merge handlers should be called with r1,r2,v1,v2 or r2,r1,v2,v1 but not a mix --- src/util/union_find.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/util/union_find.h b/src/util/union_find.h index 9dd0b6fbd..f8ae0402f 100644 --- a/src/util/union_find.h +++ b/src/util/union_find.h @@ -122,8 +122,10 @@ public: TRACE("union_find", tout << "merging " << r1 << " " << r2 << "\n";); if (r1 == r2) return; - if (m_size[r1] > m_size[r2]) + if (m_size[r1] > m_size[r2]) { std::swap(r1, r2); + std::swap(v1, v2); + } m_ctx.merge_eh(r2, r1, v2, v1); m_find[r1] = r2; m_size[r2] += m_size[r1]; From 94ffa3963e935765ab3dfde139da56fa0ef8b2e7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 24 Aug 2018 16:54:22 +0200 Subject: [PATCH 011/138] fix #1800 by converting large integers to strings Signed-off-by: Nikolaj Bjorner --- examples/ml/ml_example.ml | 7 ++++--- src/api/ml/z3.ml | 21 +++++++++++++-------- src/api/ml/z3.mli | 6 ------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/examples/ml/ml_example.ml b/examples/ml/ml_example.ml index eb64f8ee8..5b4e6e9ed 100644 --- a/examples/ml/ml_example.ml +++ b/examples/ml/ml_example.ml @@ -65,8 +65,7 @@ let model_converter_test ( ctx : context ) = | None -> raise (TestFailedException "") | Some (m) -> Printf.printf "Solver says: %s\n" (string_of_status q) ; - Printf.printf "Model: \n%s\n" (Model.to_string m) ; - Printf.printf "Converted Model: \n%s\n" (Model.to_string (convert_model ar 0 m)) + Printf.printf "Model: \n%s\n" (Model.to_string m) ) (** @@ -330,12 +329,14 @@ let _ = let ss = (Symbol.mk_string ctx "mySymbol") in let bs = (Boolean.mk_sort ctx) in let ints = (Integer.mk_sort ctx) in - let rs = (Real.mk_sort ctx) in + let rs = (Real.mk_sort ctx) in + let v = (Arithmetic.Integer.mk_numeral_i ctx 8000000000) in Printf.printf "int symbol: %s\n" (Symbol.to_string is); Printf.printf "string symbol: %s\n" (Symbol.to_string ss); Printf.printf "bool sort: %s\n" (Sort.to_string bs); Printf.printf "int sort: %s\n" (Sort.to_string ints); Printf.printf "real sort: %s\n" (Sort.to_string rs); + Printf.printf "integer: %s\n" (Expr.to_string v); basic_tests ctx ; quantifier_example1 ctx ; fpa_example ctx ; diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index a676f8c43..25b437bc4 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -43,6 +43,14 @@ let mk_list f n = in mk_list' 0 [] +let check_int32 v = v = Int32.to_int (Int32.of_int v) + +let mk_int_expr ctx v ty = + if not (check_int32 v) then + Z3native.mk_numeral ctx (string_of_int v) ty + else + Z3native.mk_int ctx v ty + let mk_context (settings:(string * string) list) = let cfg = Z3native.mk_config () in let f e = Z3native.set_param_value cfg (fst e) (snd e) in @@ -531,7 +539,7 @@ end = struct let mk_fresh_const (ctx:context) (prefix:string) (range:Sort.sort) = Z3native.mk_fresh_const ctx prefix range let mk_app (ctx:context) (f:FuncDecl.func_decl) (args:expr list) = expr_of_func_app ctx f args let mk_numeral_string (ctx:context) (v:string) (ty:Sort.sort) = Z3native.mk_numeral ctx v ty - let mk_numeral_int (ctx:context) (v:int) (ty:Sort.sort) = Z3native.mk_int ctx v ty + let mk_numeral_int (ctx:context) (v:int) (ty:Sort.sort) = mk_int_expr ctx v ty let equal (a:expr) (b:expr) = AST.equal a b let compare (a:expr) (b:expr) = AST.compare a b end @@ -1036,7 +1044,7 @@ struct let mk_mod = Z3native.mk_mod let mk_rem = Z3native.mk_rem let mk_numeral_s (ctx:context) (v:string) = Z3native.mk_numeral ctx v (mk_sort ctx) - let mk_numeral_i (ctx:context) (v:int) = Z3native.mk_int ctx v (mk_sort ctx) + let mk_numeral_i (ctx:context) (v:int) = mk_int_expr ctx v (mk_sort ctx) let mk_int2real = Z3native.mk_int2real let mk_int2bv = Z3native.mk_int2bv end @@ -1061,11 +1069,13 @@ struct let mk_numeral_nd (ctx:context) (num:int) (den:int) = if den = 0 then raise (Error "Denominator is zero") + else if not (check_int32 num) || not (check_int32 den) then + raise (Error "numerals don't fit in 32 bits") else Z3native.mk_real ctx num den let mk_numeral_s (ctx:context) (v:string) = Z3native.mk_numeral ctx v (mk_sort ctx) - let mk_numeral_i (ctx:context) (v:int) = Z3native.mk_int ctx v (mk_sort ctx) + let mk_numeral_i (ctx:context) (v:int) = mk_int_expr ctx v (mk_sort ctx) let mk_is_integer = Z3native.mk_is_int let mk_real2int = Z3native.mk_real2int @@ -1155,11 +1165,6 @@ struct let is_bv_xor3 (x:expr) = (AST.is_app x) && (FuncDecl.get_decl_kind (Expr.get_func_decl x) = OP_XOR3) let get_size (x:Sort.sort) = Z3native.get_bv_sort_size (Sort.gc x) x - let get_int (x:expr) = - match Z3native.get_numeral_int (Expr.gc x) x with - | true, v -> v - | false, _ -> raise (Error "Conversion failed.") - let numeral_to_string (x:expr) = Z3native.get_numeral_string (Expr.gc x) x let mk_const (ctx:context) (name:Symbol.symbol) (size:int) = Expr.mk_const ctx name (mk_sort ctx size) diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index 9b424b508..f67966a0f 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -1152,9 +1152,6 @@ sig (** Create a new integer sort. *) val mk_sort : context -> Sort.sort - (** Retrieve the int value. *) - val get_int : Expr.expr -> int - (** Get a big_int from an integer numeral *) val get_big_int : Expr.expr -> Big_int.big_int @@ -1543,9 +1540,6 @@ sig (** The size of a bit-vector sort. *) val get_size : Sort.sort -> int - (** Retrieve the int value. *) - val get_int : Expr.expr -> int - (** Returns a string representation of a numeral. *) val numeral_to_string : Expr.expr -> string From 85e7b1845164f56f8c044e911ccc18d79d1c58d5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 1 Sep 2018 18:22:10 -0700 Subject: [PATCH 012/138] fix name to divisible, guard under smtlib2_compliant as sugguested in #1757 Signed-off-by: Nikolaj Bjorner --- src/ast/arith_decl_plugin.cpp | 10 ++++++---- src/opt/opt_context.cpp | 2 +- src/sat/ba_solver.cpp | 20 ++++++++++---------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/ast/arith_decl_plugin.cpp b/src/ast/arith_decl_plugin.cpp index db3604a99..cdf18875c 100644 --- a/src/ast/arith_decl_plugin.cpp +++ b/src/ast/arith_decl_plugin.cpp @@ -21,6 +21,7 @@ Revision History: #include "math/polynomial/algebraic_numbers.h" #include "util/id_gen.h" #include "ast/ast_smt2_pp.h" +#include "util/gparams.h" struct arith_decl_plugin::algebraic_numbers_wrapper { unsynch_mpq_manager m_qmanager; @@ -487,7 +488,7 @@ func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters if (arity != 1 || domain[0] != m_int_decl || num_parameters != 1 || !parameters[0].is_int()) { m_manager->raise_exception("invalid divides application. Expects integer parameter and one argument of sort integer"); } - return m_manager->mk_func_decl(symbol("divides"), 1, &m_int_decl, m_manager->mk_bool_sort(), + return m_manager->mk_func_decl(symbol("divisible"), 1, &m_int_decl, m_manager->mk_bool_sort(), func_decl_info(m_family_id, k, num_parameters, parameters)); } @@ -512,7 +513,7 @@ func_decl * arith_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters if (num_args != 1 || m_manager->get_sort(args[0]) != m_int_decl || num_parameters != 1 || !parameters[0].is_int()) { m_manager->raise_exception("invalid divides application. Expects integer parameter and one argument of sort integer"); } - return m_manager->mk_func_decl(symbol("divides"), 1, &m_int_decl, m_manager->mk_bool_sort(), + return m_manager->mk_func_decl(symbol("divisible"), 1, &m_int_decl, m_manager->mk_bool_sort(), func_decl_info(m_family_id, k, num_parameters, parameters)); } if (m_manager->int_real_coercions() && use_coercion(k)) { @@ -549,8 +550,9 @@ void arith_decl_plugin::get_op_names(svector& op_names, symbol con op_names.push_back(builtin_name("*",OP_MUL)); op_names.push_back(builtin_name("/",OP_DIV)); op_names.push_back(builtin_name("div",OP_IDIV)); - // clashes with user-defined functions - // op_names.push_back(builtin_name("divides",OP_IDIVIDES)); + if (gparams::get_value("smtlib2_compliant") == "true") { + op_names.push_back(builtin_name("divisible",OP_IDIVIDES)); + } op_names.push_back(builtin_name("rem",OP_REM)); op_names.push_back(builtin_name("mod",OP_MOD)); op_names.push_back(builtin_name("to_real",OP_TO_REAL)); diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index e5c0bcddb..5d4eb3fc5 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -351,7 +351,7 @@ namespace opt { void context::get_model_core(model_ref& mdl) { mdl = m_model; fix_model(mdl); - mdl->set_model_completion(true); + if (mdl) mdl->set_model_completion(true); TRACE("opt", tout << *mdl;); } diff --git a/src/sat/ba_solver.cpp b/src/sat/ba_solver.cpp index 4226b9791..de6635d09 100644 --- a/src/sat/ba_solver.cpp +++ b/src/sat/ba_solver.cpp @@ -1551,10 +1551,10 @@ namespace sat { if (k == 1 && lit == null_literal) { literal_vector _lits(lits); s().mk_clause(_lits.size(), _lits.c_ptr(), learned); - return 0; + return nullptr; } if (!learned && clausify(lit, lits.size(), lits.c_ptr(), k)) { - return 0; + return nullptr; } void * mem = m_allocator.allocate(card::get_obj_size(lits.size())); card* c = new (mem) card(next_id(), lit, lits, k); @@ -1615,7 +1615,7 @@ namespace sat { bool units = true; for (wliteral wl : wlits) units &= wl.first == 1; if (k == 0 && lit == null_literal) { - return 0; + return nullptr; } if (units || k == 1) { literal_vector lits; @@ -3405,7 +3405,7 @@ namespace sat { return; } for (wliteral l : p1) { - SASSERT(m_weights[l.second.index()] == 0); + SASSERT(m_weights.size() <= l.second.index() || m_weights[l.second.index()] == 0); m_weights.setx(l.second.index(), l.first, 0); mark_visited(l.second); } @@ -3837,8 +3837,8 @@ namespace sat { reset_active_var_set(); m_wlits.reset(); uint64_t sum = 0; - if (m_bound == 1) return 0; - if (m_overflow) return 0; + if (m_bound == 1) return nullptr; + if (m_overflow) return nullptr; for (bool_var v : m_active_vars) { int coeff = get_int_coeff(v); @@ -3850,7 +3850,7 @@ namespace sat { } if (m_overflow || sum >= UINT_MAX/2) { - return 0; + return nullptr; } else { return add_pb_ge(null_literal, m_wlits, m_bound, true); @@ -3905,7 +3905,7 @@ namespace sat { ++k; } if (k == 1) { - return 0; + return nullptr; } while (!m_wlits.empty()) { wliteral wl = m_wlits.back(); @@ -3928,7 +3928,7 @@ namespace sat { ++num_max_level; } } - if (m_overflow) return 0; + if (m_overflow) return nullptr; if (slack >= k) { #if 0 @@ -3937,7 +3937,7 @@ namespace sat { std::cout << "not asserting\n"; display(std::cout, m_A, true); #endif - return 0; + return nullptr; } // produce asserting cardinality constraint From e8a78ec6964cc8cb8476ef95f8bb23258f8773e5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 3 Sep 2018 10:24:01 -0700 Subject: [PATCH 013/138] remove std::max for #1752 Signed-off-by: Nikolaj Bjorner --- src/util/mpz.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/mpz.h b/src/util/mpz.h index 65f89f078..2df03666f 100644 --- a/src/util/mpz.h +++ b/src/util/mpz.h @@ -152,7 +152,7 @@ class mpz_manager { // make sure that n is a big number and has capacity equal to at least c. void allocate_if_needed(mpz & n, unsigned c) { - c = std::max(c, m_init_cell_capacity); + if (m_init_cell_capacity > c) c = m_init_cell_capacity; if (n.m_ptr == nullptr || capacity(n) < c) { deallocate(n); n.m_val = 1; From 533e9c5837f927359291e48c6684e1c6bf3836c8 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Wed, 1 Aug 2018 11:11:18 +0200 Subject: [PATCH 014/138] Expand equality literals when eq_prop is disabled When equality propagation is disabled for arithmetic, equality atoms are expanded into inequality for potentially better generalization with interpolation --- src/muz/spacer/spacer_context.cpp | 8 ++++++++ src/muz/spacer/spacer_context.h | 9 +++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index f0e86f1a5..c4e606b77 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1344,6 +1344,14 @@ lbool pred_transformer::is_reachable(pob& n, expr_ref_vector* core, expr_ref_vector post (m), reach_assumps (m); post.push_back (n.post ()); + flatten_and(post); + + // if equality propagation is disabled in arithmetic, expand + // equality literals into two inequalities to increase the space + // for interpolation + if (!ctx.use_eq_prop()) { + expand_literals(m, post); + } // populate reach_assumps if (n.level () > 0 && !m_all_init) { diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 48c27f96d..976259b6d 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -1027,11 +1027,12 @@ public: const fp_params &get_params() const { return m_params; } - bool use_native_mbp () {return m_use_native_mbp;} - bool use_ground_pob () {return m_ground_pob;} - bool use_instantiate () {return m_instantiate;} + bool use_eq_prop() {return m_use_eq_prop;} + bool use_native_mbp() {return m_use_native_mbp;} + bool use_ground_pob() {return m_ground_pob;} + bool use_instantiate() {return m_instantiate;} bool weak_abs() {return m_weak_abs;} - bool use_qlemmas () {return m_use_qlemmas;} + bool use_qlemmas() {return m_use_qlemmas;} bool use_euf_gen() {return m_use_euf_gen;} bool simplify_pob() {return m_simplify_pob;} bool use_ctp() {return m_use_ctp;} From 0035d9b8cb1afa61aa031bf11e2fe213c0f82d92 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 13 Aug 2018 15:01:16 -0400 Subject: [PATCH 015/138] Background external invariants Background external invariants are constraints that are assumed to be true of the system. This commit introduces a mode in which background invariants are used only duing inductive generalization and lemma pushing, but not during predecessor computation. It is believed that this will be more efficient used of background external invariants since they will not be able to disturb how predecessors are generalized and computed. Based on a patch by Jorge Navas --- src/muz/base/fp_params.pyg | 1 + src/muz/spacer/spacer_context.cpp | 116 +++++++++++++++++++++--------- src/muz/spacer/spacer_context.h | 73 ++++++++++++------- 3 files changed, 132 insertions(+), 58 deletions(-) diff --git a/src/muz/base/fp_params.pyg b/src/muz/base/fp_params.pyg index 8cee99540..18eb85662 100644 --- a/src/muz/base/fp_params.pyg +++ b/src/muz/base/fp_params.pyg @@ -177,5 +177,6 @@ def_module_params('fp', ('spacer.dump_threshold', DOUBLE, 5.0, 'Threshold in seconds on dumping benchmarks'), ('spacer.gpdr', BOOL, False, 'Use GPDR solving strategy for non-linear CHC'), ('spacer.gpdr.bfs', BOOL, True, 'Use BFS exploration strategy for expanding model search'), + ('spacer.use_bg_invs', BOOL, False, 'Enable external background invariants'), )) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index c4e606b77..617266779 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -493,7 +493,8 @@ lemma::lemma (ast_manager &manager, expr * body, unsigned lvl) : m_pob(nullptr), m_ctp(nullptr), m_lvl(lvl), m_init_lvl(m_lvl), m_bumped(0), m_weakness(WEAKNESS_MAX), - m_external(false), m_blocked(false) { + m_external(false), m_blocked(false), + m_background(false) { SASSERT(m_body); normalize(m_body, m_body); } @@ -505,7 +506,8 @@ lemma::lemma(pob_ref const &p) : m_pob(p), m_ctp(nullptr), m_lvl(p->level()), m_init_lvl(m_lvl), m_bumped(0), m_weakness(p->weakness()), - m_external(false), m_blocked(false) { + m_external(false), m_blocked(false), + m_background(false) { SASSERT(m_pob); m_pob->get_skolems(m_zks); add_binding(m_pob->get_binding()); @@ -519,8 +521,8 @@ lemma::lemma(pob_ref const &p, expr_ref_vector &cube, unsigned lvl) : m_pob(p), m_ctp(nullptr), m_lvl(p->level()), m_init_lvl(m_lvl), m_bumped(0), m_weakness(p->weakness()), - m_external(false), m_blocked(false) -{ + m_external(false), m_blocked(false), + m_background(false) { if (m_pob) { m_pob->get_skolems(m_zks); add_binding(m_pob->get_binding()); @@ -921,10 +923,10 @@ void pred_transformer::simplify_formulas() {m_frames.simplify_formulas ();} -expr_ref pred_transformer::get_formulas(unsigned level) const +expr_ref pred_transformer::get_formulas(unsigned level, bool bg) const { expr_ref_vector res(m); - m_frames.get_frame_geq_lemmas (level, res); + m_frames.get_frame_geq_lemmas (level, res, bg); return mk_and(res); } @@ -935,6 +937,7 @@ bool pred_transformer::propagate_to_next_level (unsigned src_level) /// \brief adds a lemma to the solver and to child solvers void pred_transformer::add_lemma_core(lemma* lemma, bool ground_only) { + SASSERT(!lemma->is_background()); unsigned lvl = lemma->level(); expr* l = lemma->get_expr(); SASSERT(!lemma->is_ground() || is_clause(m, l)); @@ -975,8 +978,9 @@ void pred_transformer::add_lemma_core(lemma* lemma, bool ground_only) next_level(lvl), ground_only); } } -bool pred_transformer::add_lemma (expr *e, unsigned lvl) { +bool pred_transformer::add_lemma (expr *e, unsigned lvl, bool bg) { lemma_ref lem = alloc(lemma, m, e, lvl); + lem->set_background(bg); return m_frames.add_lemma(lem.get()); } @@ -1217,15 +1221,18 @@ expr_ref pred_transformer::get_origin_summary (model &mdl, } -void pred_transformer::add_cover(unsigned level, expr* property) +void pred_transformer::add_cover(unsigned level, expr* property, bool bg) { + SASSERT(!bg || is_infty_level(level)); // replace bound variables by local constants. expr_ref result(property, m), v(m), c(m); expr_substitution sub(m); + proof_ref pr(m); + pr = m.mk_asserted(m.mk_true()); for (unsigned i = 0; i < sig_size(); ++i) { c = m.mk_const(pm.o2n(sig(i), 0)); v = m.mk_var(i, sig(i)->get_range()); - sub.insert(v, c); + sub.insert(v, c, pr); } scoped_ptr rep = mk_default_expr_replacer(m); rep->set_substitution(&sub); @@ -1236,13 +1243,38 @@ void pred_transformer::add_cover(unsigned level, expr* property) expr_ref_vector lemmas(m); flatten_and(result, lemmas); for (unsigned i = 0, sz = lemmas.size(); i < sz; ++i) { - add_lemma(lemmas.get(i), level); + add_lemma(lemmas.get(i), level, bg); } } void pred_transformer::propagate_to_infinity (unsigned level) {m_frames.propagate_to_infinity (level);} +// compute a conjunction of all background facts +void pred_transformer::get_pred_bg_invs(expr_ref_vector& out) { + expr_ref inv(m), tmp1(m), tmp2(m); + ptr_vector preds; + for (auto kv : m_pt_rules) { + expr* tag = kv.m_value->tag(); + datalog::rule const &r = kv.m_value->rule(); + find_predecessors (r, preds); + + for (unsigned i = 0, preds_sz = preds.size(); i < preds_sz; i++) { + func_decl* pre = preds[i]; + pred_transformer &pt = ctx.get_pred_transformer(pre); + const lemma_ref_vector &invs = pt.get_bg_invs(); + CTRACE("spacer", !invs.empty(), + tout << "add-bg-invariant: " << mk_pp (pre, m) << "\n";); + for (auto inv : invs) { + // tag -> inv1 ... tag -> invn + tmp1 = m.mk_implies(tag, inv->get_expr()); + pm.formula_n2o(tmp1, tmp2, i); + out.push_back(tmp2); + TRACE("spacer", tout << tmp2 << "\n";); + } + } + } +} /// \brief Returns true if the obligation is already blocked by current lemmas @@ -1480,7 +1512,7 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem, expr_ref lemma_expr(m); lemma_expr = lem->get_expr(); - expr_ref_vector conj(m), aux(m); + expr_ref_vector cand(m), aux(m), conj(m); expr_ref gnd_lemma(m); @@ -1490,8 +1522,8 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem, lemma_expr = gnd_lemma.get(); } - conj.push_back(mk_not(m, lemma_expr)); - flatten_and (conj); + cand.push_back(mk_not(m, lemma_expr)); + flatten_and (cand); prop_solver::scoped_level _sl(*m_solver, level); prop_solver::scoped_subset_core _sc (*m_solver, true); @@ -1502,9 +1534,12 @@ bool pred_transformer::is_invariant(unsigned level, lemma* lem, if (ctx.use_ctp()) {mdl_ref_ptr = &mdl;} m_solver->set_core(core); m_solver->set_model(mdl_ref_ptr); - expr * bg = m_extend_lit.get (); - lbool r = m_solver->check_assumptions (conj, aux, m_transition_clause, - 1, &bg, 1); + + conj.push_back(m_extend_lit); + if (ctx.use_bg_invs()) get_pred_bg_invs(conj); + + lbool r = m_solver->check_assumptions (cand, aux, m_transition_clause, + conj.size(), conj.c_ptr(), 1); if (r == l_false) { solver_level = m_solver->uses_level (); lem->reset_ctp(); @@ -1535,6 +1570,7 @@ bool pred_transformer::check_inductive(unsigned level, expr_ref_vector& state, m_solver->set_core(&core); m_solver->set_model (nullptr); expr_ref_vector aux (m); + if (ctx.use_bg_invs()) get_pred_bg_invs(conj); conj.push_back (m_extend_lit); lbool res = m_solver->check_assumptions (state, aux, m_transition_clause, @@ -1949,14 +1985,27 @@ void pred_transformer::update_solver_with_rfs(prop_solver *solver, } /// pred_transformer::frames - - bool pred_transformer::frames::add_lemma(lemma *new_lemma) { TRACE("spacer", tout << "add-lemma: " << pp_level(new_lemma->level()) << " " << m_pt.head()->get_name() << " " << mk_pp(new_lemma->get_expr(), m_pt.get_ast_manager()) << "\n";); + if (new_lemma->is_background()) { + SASSERT (is_infty_level(new_lemma->level())); + + for (auto &l : m_bg_invs) { + if (l->get_expr() == new_lemma->get_expr()) return false; + } + TRACE("spacer", tout << "add-external-lemma: " + << pp_level(new_lemma->level()) << " " + << m_pt.head()->get_name() << " " + << mk_pp(new_lemma->get_expr(), m_pt.get_ast_manager()) << "\n";); + + m_bg_invs.push_back(new_lemma); + return true; + } + unsigned i = 0; for (auto *old_lemma : m_lemmas) { if (old_lemma->get_expr() == new_lemma->get_expr()) { @@ -2303,6 +2352,7 @@ void context::updt_params() { m_use_restarts = m_params.spacer_restarts(); m_restart_initial_threshold = m_params.spacer_restart_initial_threshold(); m_pdr_bfs = m_params.spacer_gpdr_bfs(); + m_use_bg_invs = m_params.spacer_use_bg_invs(); if (m_use_gpdr) { // set options to be compatible with GPDR @@ -2431,36 +2481,36 @@ expr_ref context::get_cover_delta(int level, func_decl* p_orig, func_decl* p) if (m_rels.find(p, pt)) { return pt->get_cover_delta(p_orig, level); } else { - IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";); + IF_VERBOSE(10, verbose_stream() << "did not find predicate " + << p->get_name() << "\n";); return expr_ref(m.mk_true(), m); } } -void context::add_cover(int level, func_decl* p, expr* property) +void context::add_cover(int level, func_decl* p, expr* property, bool bg) { pred_transformer* pt = nullptr; if (!m_rels.find(p, pt)) { pt = alloc(pred_transformer, *this, get_manager(), p); m_rels.insert(p, pt); - IF_VERBOSE(10, verbose_stream() << "did not find predicate " << p->get_name() << "\n";); + IF_VERBOSE(10, verbose_stream() << "did not find predicate " + << p->get_name() << "\n";); } unsigned lvl = (level == -1)?infty_level():((unsigned)level); - pt->add_cover(lvl, property); + pt->add_cover(lvl, property, bg); } void context::add_invariant (func_decl *p, expr *property) -{add_cover (infty_level(), p, property);} +{add_cover (infty_level(), p, property, true);} -expr_ref context::get_reachable(func_decl *p) -{ +expr_ref context::get_reachable(func_decl *p) { pred_transformer* pt = nullptr; if (!m_rels.find(p, pt)) { return expr_ref(m.mk_false(), m); } return pt->get_reachable(); } -bool context::validate() -{ +bool context::validate() { if (!m_validate_result) { return true; } std::stringstream msg; @@ -2491,7 +2541,7 @@ bool context::validate() model_ref model; vector rs; model_converter_ref mc; - get_level_property(m_inductive_lvl, refs, rs); + get_level_property(m_inductive_lvl, refs, rs, use_bg_invs()); inductive_property ex(m, mc, rs); ex.to_model(model); var_subst vs(m, false); @@ -2632,13 +2682,13 @@ void context::init_lemma_generalizers() } void context::get_level_property(unsigned lvl, expr_ref_vector& res, - vector& rs) const { + vector& rs, bool with_bg) const { for (auto const& kv : m_rels) { pred_transformer* r = kv.m_value; if (r->head() == m_query_pred) { continue; } - expr_ref conj = r->get_formulas(lvl); + expr_ref conj = r->get_formulas(lvl, with_bg); m_pm.formula_n2o(0, false, conj); res.push_back(conj); ptr_vector sig(r->head()->get_arity(), r->sig()); @@ -2670,7 +2720,7 @@ lbool context::solve(unsigned from_lvl) IF_VERBOSE(1, { expr_ref_vector refs(m); vector rs; - get_level_property(m_inductive_lvl, refs, rs); + get_level_property(m_inductive_lvl, refs, rs, use_bg_invs()); model_converter_ref mc; inductive_property ex(m, mc, rs); verbose_stream() << ex.to_string(); @@ -2852,7 +2902,7 @@ model_ref context::get_model() model_ref model; expr_ref_vector refs(m); vector rs; - get_level_property(m_inductive_lvl, refs, rs); + get_level_property(m_inductive_lvl, refs, rs, use_bg_invs()); inductive_property ex(m, const_cast(m_mc), rs); ex.to_model (model); return model; @@ -2885,7 +2935,7 @@ expr_ref context::mk_unsat_answer() const { expr_ref_vector refs(m); vector rs; - get_level_property(m_inductive_lvl, refs, rs); + get_level_property(m_inductive_lvl, refs, rs, use_bg_invs()); inductive_property ex(m, const_cast(m_mc), rs); return ex.to_expr(); } diff --git a/src/muz/spacer/spacer_context.h b/src/muz/spacer/spacer_context.h index 976259b6d..0d8b2daf6 100644 --- a/src/muz/spacer/spacer_context.h +++ b/src/muz/spacer/spacer_context.h @@ -128,8 +128,9 @@ class lemma { unsigned m_init_lvl; // level at which lemma was created unsigned m_bumped:16; unsigned m_weakness:16; - unsigned m_external:1; - unsigned m_blocked:1; + unsigned m_external:1; // external lemma from another solver + unsigned m_blocked:1; // blocked by CTP + unsigned m_background:1; // background assumed fact void mk_expr_core(); void mk_cube_core(); @@ -163,6 +164,9 @@ public: void set_external(bool ext){m_external = ext;} bool external() { return m_external;} + void set_background(bool v) {m_background = v;} + bool is_background() {return m_background;} + bool is_blocked() {return m_blocked;} void set_blocked(bool v) {m_blocked=v;} @@ -222,6 +226,7 @@ class pred_transformer { pred_transformer &m_pt; // parent pred_transformer lemma_ref_vector m_pinned_lemmas; // all created lemmas lemma_ref_vector m_lemmas; // active lemmas + lemma_ref_vector m_bg_invs; // background (assumed) invariants unsigned m_size; // num of frames bool m_sorted; // true if m_lemmas is sorted by m_lt @@ -230,7 +235,8 @@ class pred_transformer { void sort (); public: - frames (pred_transformer &pt) : m_pt (pt), m_size(0), m_sorted (true) {} + frames (pred_transformer &pt) : m_pt (pt), + m_size(0), m_sorted (true) {} ~frames() {} void simplify_formulas (); @@ -245,17 +251,25 @@ class pred_transformer { } } } - void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out) const { + void get_frame_geq_lemmas (unsigned level, expr_ref_vector &out, + bool with_bg = false) const { for (auto &lemma : m_lemmas) { if (lemma->level() >= level) { out.push_back(lemma->get_expr()); } } + if (with_bg) { + for (auto &lemma : m_bg_invs) + out.push_back(lemma->get_expr()); + } } - unsigned size () const {return m_size;} - unsigned lemma_size () const {return m_lemmas.size ();} - void add_frame () {m_size++;} + const lemma_ref_vector& get_bg_invs() const {return m_bg_invs;} + unsigned size() const {return m_size;} + unsigned lemma_size() const {return m_lemmas.size ();} + unsigned bg_invs_size() const {return m_bg_invs.size();} + + void add_frame() {m_size++;} void inherit_frames (frames &other) { for (auto &other_lemma : other.m_lemmas) { lemma_ref new_lemma = alloc(lemma, m_pt.get_ast_manager(), @@ -265,6 +279,7 @@ class pred_transformer { add_lemma(new_lemma.get()); } m_sorted = false; + m_bg_invs.append(other.m_bg_invs); } bool add_lemma (lemma *new_lemma); @@ -418,6 +433,11 @@ class pred_transformer { app_ref mk_fresh_rf_tag (); + // get tagged formulae of all of the background invariants for all of the + // predecessors of the current transformer + void get_pred_bg_invs(expr_ref_vector &out); + const lemma_ref_vector &get_bg_invs() const {return m_frames.get_bg_invs();} + public: pred_transformer(context& ctx, manager& pm, func_decl* head); ~pred_transformer() {} @@ -448,7 +468,7 @@ public: } unsigned get_num_levels() const {return m_frames.size ();} expr_ref get_cover_delta(func_decl* p_orig, int level); - void add_cover(unsigned level, expr* property); + void add_cover(unsigned level, expr* property, bool bg = false); expr_ref get_reachable(); std::ostream& display(std::ostream& strm) const; @@ -484,7 +504,7 @@ public: bool propagate_to_next_level(unsigned level); void propagate_to_infinity(unsigned level); /// \brief Add a lemma to the current context and all users - bool add_lemma(expr * lemma, unsigned lvl); + bool add_lemma(expr * e, unsigned lvl, bool bg); bool add_lemma(lemma* lem) {return m_frames.add_lemma(lem);} expr* get_reach_case_var (unsigned idx) const; bool has_rfs () const { return !m_reach_facts.empty () ;} @@ -527,7 +547,7 @@ public: bool check_inductive(unsigned level, expr_ref_vector& state, unsigned& assumes_level, unsigned weakness = UINT_MAX); - expr_ref get_formulas(unsigned level) const; + expr_ref get_formulas(unsigned level, bool bg = false) const; void simplify_formulas(); @@ -958,6 +978,7 @@ class context { bool m_simplify_formulas_pre; bool m_simplify_formulas_post; bool m_pdr_bfs; + bool m_use_bg_invs; unsigned m_push_pob_max_depth; unsigned m_max_level; unsigned m_restart_initial_threshold; @@ -992,7 +1013,8 @@ class context { // Generate inductive property void get_level_property(unsigned lvl, expr_ref_vector& res, - vector & rs) const; + vector & rs, + bool with_bg = false) const; // Initialization @@ -1027,19 +1049,20 @@ public: const fp_params &get_params() const { return m_params; } - bool use_eq_prop() {return m_use_eq_prop;} - bool use_native_mbp() {return m_use_native_mbp;} - bool use_ground_pob() {return m_ground_pob;} - bool use_instantiate() {return m_instantiate;} - bool weak_abs() {return m_weak_abs;} - bool use_qlemmas() {return m_use_qlemmas;} - bool use_euf_gen() {return m_use_euf_gen;} - bool simplify_pob() {return m_simplify_pob;} - bool use_ctp() {return m_use_ctp;} - bool use_inc_clause() {return m_use_inc_clause;} - unsigned blast_term_ite_inflation() {return m_blast_term_ite_inflation;} - bool elim_aux() {return m_elim_aux;} - bool reach_dnf() {return m_reach_dnf;} + bool use_eq_prop() const {return m_use_eq_prop;} + bool use_native_mbp() const {return m_use_native_mbp;} + bool use_ground_pob() const {return m_ground_pob;} + bool use_instantiate() const {return m_instantiate;} + bool weak_abs() const {return m_weak_abs;} + bool use_qlemmas() const {return m_use_qlemmas;} + bool use_euf_gen() const {return m_use_euf_gen;} + bool simplify_pob() const {return m_simplify_pob;} + bool use_ctp() const {return m_use_ctp;} + bool use_inc_clause() const {return m_use_inc_clause;} + unsigned blast_term_ite_inflation() const {return m_blast_term_ite_inflation;} + bool elim_aux() const {return m_elim_aux;} + bool reach_dnf() const {return m_reach_dnf;} + bool use_bg_invs() const {return m_use_bg_invs;} ast_manager& get_ast_manager() const {return m;} manager& get_manager() {return m_pm;} @@ -1082,7 +1105,7 @@ public: unsigned get_num_levels(func_decl* p); expr_ref get_cover_delta(int level, func_decl* p_orig, func_decl* p); - void add_cover(int level, func_decl* pred, expr* property); + void add_cover(int level, func_decl* pred, expr* property, bool bg = false); expr_ref get_reachable (func_decl* p); void add_invariant (func_decl *pred, expr* property); model_ref get_model(); From 3a01fd791b0b06ccf2439eaab8bce828dbbd7dbc Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 12 Jul 2018 22:38:29 +0300 Subject: [PATCH 016/138] Replace rule API --- src/muz/base/dl_rule_set.cpp | 59 +++++++++++++++++++++++------------- src/muz/base/dl_rule_set.h | 13 +++++--- 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/muz/base/dl_rule_set.cpp b/src/muz/base/dl_rule_set.cpp index 5114fdca4..80af80266 100644 --- a/src/muz/base/dl_rule_set.cpp +++ b/src/muz/base/dl_rule_set.cpp @@ -31,7 +31,7 @@ namespace datalog { rule_dependencies::rule_dependencies(const rule_dependencies & o, bool reversed): m_context(o.m_context) { if (reversed) { - for (auto & kv : o) { + for (auto & kv : o) { func_decl * pred = kv.m_key; item_set & orig_items = *kv.get_value(); @@ -114,8 +114,8 @@ namespace datalog { app* a = to_app(e); d = a->get_decl(); if (m_context.is_predicate(d)) { - // insert d and ensure the invariant - // that every predicate is present as + // insert d and ensure the invariant + // that every predicate is present as // a key in m_data s.insert(d); ensure_key(d); @@ -148,7 +148,7 @@ namespace datalog { item_set& itms = *kv.get_value(); set_intersection(itms, allowed); } - for (func_decl* f : to_remove) + for (func_decl* f : to_remove) remove_m_data_entry(f); } @@ -253,18 +253,18 @@ namespace datalog { // // ----------------------------------- - rule_set::rule_set(context & ctx) - : m_context(ctx), - m_rule_manager(ctx.get_rule_manager()), - m_rules(m_rule_manager), + rule_set::rule_set(context & ctx) + : m_context(ctx), + m_rule_manager(ctx.get_rule_manager()), + m_rules(m_rule_manager), m_deps(ctx), m_stratifier(nullptr), m_refs(ctx.get_manager()) { } - rule_set::rule_set(const rule_set & other) - : m_context(other.m_context), - m_rule_manager(other.m_rule_manager), + rule_set::rule_set(const rule_set & other) + : m_context(other.m_context), + m_rule_manager(other.m_rule_manager), m_rules(m_rule_manager), m_deps(other.m_context), m_stratifier(nullptr), @@ -353,10 +353,27 @@ namespace datalog { break; \ } \ } \ - + DEL_VECTOR(*rules); DEL_VECTOR(m_rules); - } + } + + void rule_set::replace_rule(rule * r, rule * other) { + TRACE("dl", r->display(m_context, tout << "replace:");); + func_decl* d = r->get_decl(); + rule_vector* rules = m_head2rules.find(d); +#define REPLACE_VECTOR(_v) \ + for (unsigned i = (_v).size(); i > 0; ) { \ + --i; \ + if ((_v)[i] == r) { \ + (_v)[i] = other; \ + break; \ + } \ + } \ + + REPLACE_VECTOR(*rules); + REPLACE_VECTOR(m_rules); + } void rule_set::ensure_closed() { if (!is_closed()) { @@ -365,7 +382,7 @@ namespace datalog { } bool rule_set::close() { - SASSERT(!is_closed()); //the rule_set is not already closed + SASSERT(!is_closed()); //the rule_set is not already closed m_deps.populate(*this); m_stratifier = alloc(rule_stratifier, m_deps); if (!stratified_negation()) { @@ -426,7 +443,7 @@ namespace datalog { inherit_predicates(src); } - const rule_vector & rule_set::get_predicate_rules(func_decl * pred) const { + const rule_vector & rule_set::get_predicate_rules(func_decl * pred) const { decl2rules::obj_map_entry * e = m_head2rules.find_core(pred); if (!e) { return m_empty_rule_vector; @@ -519,7 +536,7 @@ namespace datalog { out << "\n"; non_empty = false; } - + for (func_decl * first : *strat) { const func_decl_set & deps = m_deps.get_deps(first); for (func_decl * dep : deps) { @@ -545,8 +562,8 @@ namespace datalog { unsigned rule_stratifier::get_predicate_strat(func_decl * pred) const { unsigned num; if (!m_pred_strat_nums.find(pred, num)) { - //the number of the predicate is not stored, therefore it did not appear - //in the algorithm and therefore it does not depend on anything and nothing + //the number of the predicate is not stored, therefore it did not appear + //in the algorithm and therefore it does not depend on anything and nothing //depends on it. So it is safe to assign zero strate to it, although it is //not strictly true. num = 0; @@ -641,7 +658,7 @@ namespace datalog { } - // We put components whose indegree is zero to m_strats and assign its + // We put components whose indegree is zero to m_strats and assign its // m_components entry to zero. unsigned comp_cnt = m_components.size(); for (unsigned i = 0; i < comp_cnt; i++) { @@ -680,7 +697,7 @@ namespace datalog { strats_index++; } //we have managed to topologicaly order all the components - SASSERT(std::find_if(m_components.begin(), m_components.end(), + SASSERT(std::find_if(m_components.begin(), m_components.end(), std::bind1st(std::not_equal_to(), (item_set*)0)) == m_components.end()); //reverse the strats array, so that the only the later components would depend on earlier ones @@ -713,7 +730,7 @@ namespace datalog { } out << "\n"; } - + } }; diff --git a/src/muz/base/dl_rule_set.h b/src/muz/base/dl_rule_set.h index 736dd8888..e870e369c 100644 --- a/src/muz/base/dl_rule_set.h +++ b/src/muz/base/dl_rule_set.h @@ -77,7 +77,7 @@ namespace datalog { \brief Number of predicates that depend on \c f. */ unsigned out_degree(func_decl * f) const; - + /** \brief If the rependency graph is acyclic, put all elements into \c res ordered so that elements can depend only on elements that are before them. @@ -131,7 +131,7 @@ namespace datalog { it must exist for the whole lifetime of the \c stratifier object. */ rule_stratifier(const rule_dependencies & deps) - : m_deps(deps), m_next_preorder(0) + : m_deps(deps), m_next_preorder(0) { process(); } @@ -145,7 +145,7 @@ namespace datalog { const comp_vector & get_strats() const { return m_strats; } unsigned get_predicate_strat(func_decl * pred) const; - + void display( std::ostream & out ) const; }; @@ -203,6 +203,10 @@ namespace datalog { \brief Remove rule \c r from the rule set. */ void del_rule(rule * r); + /** + \brief Replace a rule \c r with the rule \c other + */ + void replace_rule(rule * r, rule * other); /** \brief Add all rules from a different rule_set. @@ -276,8 +280,7 @@ namespace datalog { inline std::ostream& operator<<(std::ostream& out, rule_set const& r) { r.display(out); return out; } - + }; #endif /* DL_RULE_SET_H_ */ - From 84001225966ab74178c111e31fb9fb1be453b1dc Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Thu, 12 Jul 2018 22:38:46 +0300 Subject: [PATCH 017/138] mk_synchronize rule transformation --- src/muz/transforms/CMakeLists.txt | 1 + src/muz/transforms/dl_mk_synchronize.cpp | 378 +++++++++++++++++++++++ src/muz/transforms/dl_mk_synchronize.h | 98 ++++++ 3 files changed, 477 insertions(+) create mode 100644 src/muz/transforms/dl_mk_synchronize.cpp create mode 100644 src/muz/transforms/dl_mk_synchronize.h diff --git a/src/muz/transforms/CMakeLists.txt b/src/muz/transforms/CMakeLists.txt index e92aa1c2f..62272450c 100644 --- a/src/muz/transforms/CMakeLists.txt +++ b/src/muz/transforms/CMakeLists.txt @@ -25,6 +25,7 @@ z3_add_component(transforms dl_mk_array_eq_rewrite.cpp dl_mk_array_instantiation.cpp dl_mk_elim_term_ite.cpp + dl_mk_synchronize.cpp COMPONENT_DEPENDENCIES dataflow hilbert diff --git a/src/muz/transforms/dl_mk_synchronize.cpp b/src/muz/transforms/dl_mk_synchronize.cpp new file mode 100644 index 000000000..f691b69d8 --- /dev/null +++ b/src/muz/transforms/dl_mk_synchronize.cpp @@ -0,0 +1,378 @@ +/*++ +Copyright (c) 2017-2018 Saint-Petersburg State University + +Module Name: + + dl_mk_synchronize.h + +Abstract: + + Rule transformer that attempts to merge recursive iterations + relaxing the shape of the inductive invariant. + +Author: + + Dmitry Mordvinov (dvvrd) 2017-05-24 + Lidiia Chernigovskaia (LChernigovskaya) 2017-10-20 + +Revision History: + +--*/ +#include "muz/transforms/dl_mk_synchronize.h" + +namespace datalog { + + mk_synchronize::mk_synchronize(context& ctx, unsigned priority): + rule_transformer::plugin(priority, false), + m_ctx(ctx), + m(ctx.get_manager()), + rm(ctx.get_rule_manager()) + {} + + bool mk_synchronize::is_recursive_app(rule & r, app * app) const { + func_decl* head_decl = r.get_head()->get_decl(); + func_decl* app_decl = app->get_decl(); + if (head_decl == app_decl) { + return true; + } + rule_stratifier::comp_vector const & strata = m_stratifier->get_strats(); + unsigned num_of_stratum = m_stratifier->get_predicate_strat(head_decl); + return strata[num_of_stratum]->contains(app_decl); + } + + bool mk_synchronize::has_recursive_premise(app * app) const { + func_decl* app_decl = app->get_decl(); + if (m_deps->get_deps(app_decl).contains(app_decl)) { + return true; + } + rule_stratifier::comp_vector const & strata = m_stratifier->get_strats(); + unsigned num_of_stratum = m_stratifier->get_predicate_strat(app_decl); + return strata[num_of_stratum]->size() > 1; + } + + ptr_vector mk_synchronize::add_merged_decls(ptr_vector & apps) { + unsigned n = apps.size(); + ptr_vector merged_decls; + merged_decls.resize(n); + ptr_vector app_decls; + app_decls.resize(n); + for (unsigned j = 0; j < n; ++j) { + app_decls[j] = apps[j]->get_decl(); + } + rule_stratifier::comp_vector const & strata = m_stratifier->get_strats(); + for (unsigned j = 0; j < n; ++j) { + unsigned num_of_stratum = m_stratifier->get_predicate_strat(app_decls[j]); + merged_decls[j] = strata[num_of_stratum]; + } + return merged_decls; + } + + void mk_synchronize::add_new_rel_symbols(unsigned idx, ptr_vector const & decls, + ptr_vector & decls_buf, bool & was_added) { + if (idx >= decls.size()) { + string_buffer<> buffer; + ptr_vector domain; + ptr_vector::const_iterator it = decls_buf.begin(), end = decls_buf.end(); + for (; it != end; ++it) { + buffer << (*it)->get_name(); + buffer << "!!"; + domain.append((*it)->get_arity(), (*it)->get_domain()); + } + + symbol new_name = symbol(buffer.c_str()); + + if (!cache.contains(new_name)) { + was_added = true; + func_decl* orig = decls_buf[0]; + func_decl* product_pred = m_ctx.mk_fresh_head_predicate(new_name, + symbol::null, domain.size(), domain.c_ptr(), orig); + cache.insert(new_name, product_pred); + } + return; + } + + rule_stratifier::item_set const & pred_decls = *decls[idx]; + for (rule_stratifier::item_set::iterator it = pred_decls.begin(); it != pred_decls.end(); ++it) { + decls_buf[idx] = *it; + add_new_rel_symbols(idx + 1, decls, decls_buf, was_added); + } + } + + void mk_synchronize::replace_applications(rule & r, rule_set & rules, ptr_vector & apps) { + app* replacing = product_application(apps); + + ptr_vector new_tail; + svector new_tail_neg; + unsigned n = r.get_tail_size() - apps.size() + 1; + unsigned tail_idx = 0; + new_tail.resize(n); + new_tail_neg.resize(n); + new_tail[0] = replacing; + new_tail_neg[0] = false; + + for (unsigned i = 0; i < r.get_positive_tail_size(); ++i) { + app* tail = r.get_tail(i); + if (!apps.contains(tail)) { + ++tail_idx; + new_tail[tail_idx] = tail; + new_tail_neg[tail_idx] = false; + } + } + for (unsigned i = r.get_positive_tail_size(); i < r.get_uninterpreted_tail_size(); ++i) { + ++tail_idx; + new_tail[tail_idx] = r.get_tail(i); + new_tail_neg[tail_idx] = true; + } + for (unsigned i = r.get_uninterpreted_tail_size(); i < r.get_tail_size(); ++i) { + ++tail_idx; + new_tail[tail_idx] = r.get_tail(i); + new_tail_neg[tail_idx] = false; + } + + rule_ref new_rule(rm); + new_rule = rm.mk(r.get_head(), tail_idx + 1, + new_tail.c_ptr(), new_tail_neg.c_ptr(), symbol::null, false); + rules.replace_rule(&r, new_rule.get()); + } + + rule_ref mk_synchronize::rename_bound_vars_in_rule(rule * r, unsigned & var_idx) { + ptr_vector sorts; + r->get_vars(m, sorts); + expr_ref_vector revsub(m); + revsub.resize(sorts.size()); + for (unsigned i = 0; i < sorts.size(); ++i) { + if (sorts[i]) { + revsub[i] = m.mk_var(var_idx++, sorts[i]); + } + } + + rule_ref new_rule(rm); + new_rule = rm.mk(r); + rm.substitute(new_rule, revsub.size(), revsub.c_ptr()); + return new_rule; + } + + vector mk_synchronize::rename_bound_vars(ptr_vector const & heads, + rule_set & rules) { + vector result; + unsigned var_idx = 0; + for (unsigned i = 0; i < heads.size(); ++i) { + rule_ref_vector dst_vector(rm); + for (rule_stratifier::item_set::iterator it = heads[i]->begin(); it != heads[i]->end(); ++it) { + func_decl * head = *it; + rule_vector const & src_rules = rules.get_predicate_rules(head); + for (unsigned j = 0; j < src_rules.size(); ++j) { + rule_ref new_rule = rename_bound_vars_in_rule(src_rules[j], var_idx); + dst_vector.push_back(new_rule.get()); + } + } + result.push_back(dst_vector); + } + return result; + } + + void mk_synchronize::add_rec_tail(vector< ptr_vector > & recursive_calls, ptr_vector & new_tail, + svector & new_tail_neg, unsigned & tail_idx) { + int max_size = recursive_calls[0].size(); + unsigned n = recursive_calls.size(); + for (unsigned i = 0; i < n; ++i) { + if (recursive_calls[i].size() > max_size) { + max_size = recursive_calls[i].size(); + } + } + for (unsigned j = 0; j < max_size; ++j) { + ptr_vector merged_recursive_calls; + merged_recursive_calls.resize(n); + for (unsigned i = 0; i < n; ++i) { + unsigned cur_size = recursive_calls[i].size(); + j < cur_size ? merged_recursive_calls[i] = recursive_calls[i][j]: + merged_recursive_calls[i] = recursive_calls[i][cur_size - 1]; + } + ++tail_idx; + new_tail[tail_idx] = product_application(merged_recursive_calls); + new_tail_neg[tail_idx] = false; + } + } + + void mk_synchronize::add_non_rec_tail(rule & r, ptr_vector & new_tail, svector & new_tail_neg, + unsigned & tail_idx) { + for (unsigned i = 0; i < r.get_positive_tail_size(); ++i) { + app* tail = r.get_tail(i); + if (!is_recursive_app(r, tail)) { + ++tail_idx; + new_tail[tail_idx] = tail; + new_tail_neg[tail_idx] = false; + } + } + for (unsigned i = r.get_positive_tail_size(); i < r.get_uninterpreted_tail_size(); ++i) { + ++tail_idx; + new_tail[tail_idx] = r.get_tail(i); + new_tail_neg[tail_idx] = true; + } + for (unsigned i = r.get_uninterpreted_tail_size(); i < r.get_tail_size(); ++i) { + ++tail_idx; + new_tail[tail_idx] = r.get_tail(i); + new_tail_neg[tail_idx] = r.is_neg_tail(i); + } + } + + app* mk_synchronize::product_application(ptr_vector const &apps) { + ptr_vector::const_iterator it = apps.begin(), end = apps.end(); + unsigned args_num = 0; + string_buffer<> buffer; + + for (; it != end; ++it) { + buffer << (*it)->get_decl()->get_name(); + buffer << "!!"; + args_num += (*it)->get_num_args(); + } + + symbol name = symbol(buffer.c_str()); + SASSERT(cache.contains(name)); + func_decl * pred = cache[name]; + + ptr_vector args; + args.resize(args_num); + it = apps.begin(); + for (unsigned args_idx = 0; it != end; ++it) { + app* a = *it; + for (unsigned i = 0; i < a->get_num_args(); ++i, ++args_idx) { + args[args_idx] = a->get_arg(i); + } + } + + return m.mk_app(pred, args_num, args.c_ptr()); + } + + rule_ref mk_synchronize::product_rule(rule_ref_vector const & rules) { + unsigned n = rules.size(); + + string_buffer<> buffer; + bool first_rule = true; + for (rule_ref_vector::iterator it = rules.begin(); it != rules.end(); ++it, first_rule = false) { + if (!first_rule) { + buffer << "+"; + } + buffer << (*it)->name(); + } + + ptr_vector heads; + heads.resize(n); + for (unsigned i = 0; i < n; ++i) { + heads[i] = rules[i]->get_head(); + } + app* product_head = product_application(heads); + unsigned product_tail_length = 0; + bool has_recursion = false; + vector< ptr_vector > recursive_calls; + recursive_calls.resize(n); + for (unsigned i = 0; i < n; ++i) { + rule& rule = *rules[i]; + product_tail_length += rule.get_tail_size(); + for (unsigned j = 0; j < rule.get_positive_tail_size(); ++j) { + app* tail = rule.get_tail(j); + if (is_recursive_app(rule, tail)) { + has_recursion = true; + recursive_calls[i].push_back(tail); + } + } + if (recursive_calls[i].empty()) { + recursive_calls[i].push_back(rule.get_head()); + } + } + + ptr_vector new_tail; + svector new_tail_neg; + new_tail.resize(product_tail_length); + new_tail_neg.resize(product_tail_length); + unsigned tail_idx = -1; + if (has_recursion) { + add_rec_tail(recursive_calls, new_tail, new_tail_neg, tail_idx); + } + + for (rule_vector::const_iterator it = rules.begin(); it != rules.end(); ++it) { + rule& rule = **it; + add_non_rec_tail(rule, new_tail, new_tail_neg, tail_idx); + } + + rule_ref new_rule(rm); + new_rule = rm.mk(product_head, tail_idx + 1, + new_tail.c_ptr(), new_tail_neg.c_ptr(), symbol(buffer.c_str()), false); + rm.fix_unbound_vars(new_rule, false); + return new_rule; + } + + void mk_synchronize::merge_rules(unsigned idx, rule_ref_vector & buf, vector const & merged_rules, + rule_set & all_rules) { + if (idx >= merged_rules.size()) { + rule_ref product = product_rule(buf); + all_rules.add_rule(product.get()); + return; + } + + rule_ref_vector const & pred_rules = merged_rules[idx]; + for (rule_ref_vector::iterator it = pred_rules.begin(); it != pred_rules.end(); ++it) { + buf[idx] = *it; + merge_rules(idx + 1, buf, merged_rules, all_rules); + } + } + + void mk_synchronize::merge_applications(rule & r, rule_set & rules) { + ptr_vector non_recursive_applications; + obj_hashtable apps; + for (unsigned i = 0; i < r.get_positive_tail_size(); ++i) { + app* application = r.get_tail(i); + if (!is_recursive_app(r, application) && has_recursive_premise(application)) { + apps.insert(application); + } + } + if (apps.size() < 2) { + return; + } + + for (obj_hashtable::iterator it = apps.begin(); it != apps.end(); it++) { + non_recursive_applications.push_back(*it); + } + + ptr_vector merged_decls = add_merged_decls(non_recursive_applications); + + unsigned n = non_recursive_applications.size(); + ptr_vector decls_buf; + decls_buf.resize(n); + bool was_added = false; + add_new_rel_symbols(0, merged_decls, decls_buf, was_added); + if (was_added){ + rule_ref_vector rules_buf(rm); + rules_buf.resize(n); + vector renamed_rules = rename_bound_vars(merged_decls, rules); + merge_rules(0, rules_buf, renamed_rules, rules); + } + + replace_applications(r, rules, non_recursive_applications); + m_deps->populate(rules); + m_stratifier = alloc(rule_stratifier, *m_deps); + } + + rule_set * mk_synchronize::operator()(rule_set const & source) { + rule_set* rules = alloc(rule_set, m_ctx); + rules->inherit_predicates(source); + + rule_set::iterator it = source.begin(), end = source.end(); + for (; it != end; ++it) { + rules->add_rule(*it); + } + + m_deps = alloc(rule_dependencies, m_ctx); + m_deps->populate(*rules); + m_stratifier = alloc(rule_stratifier, *m_deps); + + unsigned current_rule = 0; + while (current_rule < rules->get_num_rules()) { + rule *r = rules->get_rule(current_rule); + merge_applications(*r, *rules); + ++current_rule; + } + return rules; + } + +}; diff --git a/src/muz/transforms/dl_mk_synchronize.h b/src/muz/transforms/dl_mk_synchronize.h new file mode 100644 index 000000000..e4ce7cad1 --- /dev/null +++ b/src/muz/transforms/dl_mk_synchronize.h @@ -0,0 +1,98 @@ +/*++ +Copyright (c) 2017-2018 Saint-Petersburg State University + +Module Name: + + dl_mk_synchronize.h + +Abstract: + + Rule transformer that attempts to merge recursive iterations + relaxing the shape of the inductive invariant. + +Example: + + Q(z) :- A(x), B(y), phi1(x,y,z). + A(x) :- A(x'), phi2(x,x'). + A(x) :- phi3(x). + B(y) :- C(y'), phi4(y,y'). + C(y) :- B(y'), phi5(y,y'). + B(y) :- phi6(y). + + Transformed clauses: + + Q(z) :- AB(x,y), phi1(x,y,z). + AB(x,y) :- AC(x',y'), phi2(x,x'), phi4(y,y'). + AC(x,y) :- AB(x',y'), phi2(x,x'), phi5(y,y'). + AB(x,y) :- AC(x, y'), phi3(x), phi4(y,y'). + AC(x,y) :- AB(x, y'), phi3(x), phi5(y,y'). + AB(x,y) :- AB(x',y), phi2(x,x'), phi6(y). + AB(x,y) :- phi3(x), phi6(y). + +Author: + + Dmitry Mordvinov (dvvrd) 2017-05-24 + Lidiia Chernigovskaia (LChernigovskaya) 2017-10-20 + +Revision History: + +--*/ +#ifndef DL_MK_SYNCHRONIZE_H_ +#define DL_MK_SYNCHRONIZE_H_ + +#include"muz/base/dl_context.h" +#include"muz/base/dl_rule_set.h" +#include"util/uint_set.h" +#include"muz/base/dl_rule_transformer.h" +#include"muz/transforms/dl_mk_rule_inliner.h" + +namespace datalog { + + /** + \brief Implements a synchronous product transformation. + */ + class mk_synchronize : public rule_transformer::plugin { + context& m_ctx; + ast_manager& m; + rule_manager& rm; + + scoped_ptr m_deps; + scoped_ptr m_stratifier; + map cache; + + bool is_recursive_app(rule & r, app * app) const; + bool has_recursive_premise(app * app) const; + + ptr_vector add_merged_decls(ptr_vector & apps); + void add_new_rel_symbols(unsigned idx, ptr_vector const & decls, + ptr_vector & buf, bool & was_added); + + void replace_applications(rule & r, rule_set & rules, ptr_vector & apps); + + rule_ref rename_bound_vars_in_rule(rule * r, unsigned & var_idx); + vector rename_bound_vars(ptr_vector const & heads, rule_set & rules); + + void add_rec_tail(vector< ptr_vector > & recursive_calls, ptr_vector & new_tail, + svector & new_tail_neg, unsigned & tail_idx); + void add_non_rec_tail(rule & r, ptr_vector & new_tail, svector & new_tail_neg, + unsigned & tail_idx); + + app* product_application(ptr_vector const & apps); + rule_ref product_rule(rule_ref_vector const & rules); + + void merge_rules(unsigned idx, rule_ref_vector & buf, + vector const & merged_rules, rule_set & all_rules); + void merge_applications(rule & r, rule_set & rules); + + public: + /** + \brief Create synchronous product transformer. + */ + mk_synchronize(context & ctx, unsigned priority = 22500); + + rule_set * operator()(rule_set const & source); + }; + +}; + +#endif /* DL_MK_SYNCHRONIZE_H_ */ From 0516e6f21f9a99931f4cea789e4cf031e49656b5 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 24 Jul 2018 11:45:40 -0400 Subject: [PATCH 018/138] Integrating synchronize pass --- src/muz/transforms/dl_mk_synchronize.cpp | 174 +++++++++++------------ src/muz/transforms/dl_mk_synchronize.h | 30 ++-- 2 files changed, 105 insertions(+), 99 deletions(-) diff --git a/src/muz/transforms/dl_mk_synchronize.cpp b/src/muz/transforms/dl_mk_synchronize.cpp index f691b69d8..268c994d1 100644 --- a/src/muz/transforms/dl_mk_synchronize.cpp +++ b/src/muz/transforms/dl_mk_synchronize.cpp @@ -22,6 +22,8 @@ Revision History: namespace datalog { + typedef mk_synchronize::item_set_vector item_set_vector; + mk_synchronize::mk_synchronize(context& ctx, unsigned priority): rule_transformer::plugin(priority, false), m_ctx(ctx), @@ -29,15 +31,16 @@ namespace datalog { rm(ctx.get_rule_manager()) {} - bool mk_synchronize::is_recursive_app(rule & r, app * app) const { - func_decl* head_decl = r.get_head()->get_decl(); - func_decl* app_decl = app->get_decl(); - if (head_decl == app_decl) { - return true; - } - rule_stratifier::comp_vector const & strata = m_stratifier->get_strats(); - unsigned num_of_stratum = m_stratifier->get_predicate_strat(head_decl); - return strata[num_of_stratum]->contains(app_decl); + bool mk_synchronize::is_recursive(rule &r, func_decl &decl) const { + func_decl *hdecl = r.get_head()->get_decl(); + // AG: shouldn't decl appear in the body? + if (hdecl == &decl) return true; + auto & strata = m_stratifier->get_strats(); + unsigned num_of_stratum = m_stratifier->get_predicate_strat(hdecl); + return strata[num_of_stratum]->contains(&decl); + } + bool mk_synchronize::is_recursive(rule &r, expr &e) const { + return is_app(&e) && is_recursive(r, *to_app(&e)->get_decl()); } bool mk_synchronize::has_recursive_premise(app * app) const { @@ -50,33 +53,29 @@ namespace datalog { return strata[num_of_stratum]->size() > 1; } - ptr_vector mk_synchronize::add_merged_decls(ptr_vector & apps) { - unsigned n = apps.size(); - ptr_vector merged_decls; - merged_decls.resize(n); - ptr_vector app_decls; - app_decls.resize(n); - for (unsigned j = 0; j < n; ++j) { - app_decls[j] = apps[j]->get_decl(); - } - rule_stratifier::comp_vector const & strata = m_stratifier->get_strats(); - for (unsigned j = 0; j < n; ++j) { - unsigned num_of_stratum = m_stratifier->get_predicate_strat(app_decls[j]); - merged_decls[j] = strata[num_of_stratum]; + item_set_vector mk_synchronize::add_merged_decls(ptr_vector & apps) { + unsigned sz = apps.size(); + item_set_vector merged_decls; + merged_decls.resize(sz); + auto & strata = m_stratifier->get_strats(); + for (unsigned j = 0; j < sz; ++j) { + unsigned nos; + nos = m_stratifier->get_predicate_strat(apps[j]->get_decl()); + merged_decls[j] = strata[nos]; } return merged_decls; } - void mk_synchronize::add_new_rel_symbols(unsigned idx, ptr_vector const & decls, - ptr_vector & decls_buf, bool & was_added) { + void mk_synchronize::add_new_rel_symbols(unsigned idx, + item_set_vector const & decls, + ptr_vector & decls_buf, + bool & was_added) { if (idx >= decls.size()) { string_buffer<> buffer; ptr_vector domain; - ptr_vector::const_iterator it = decls_buf.begin(), end = decls_buf.end(); - for (; it != end; ++it) { - buffer << (*it)->get_name(); - buffer << "!!"; - domain.append((*it)->get_arity(), (*it)->get_domain()); + for (auto &d : decls_buf) { + buffer << d->get_name() << "!!"; + domain.append(d->get_arity(), d->get_domain()); } symbol new_name = symbol(buffer.c_str()); @@ -84,6 +83,7 @@ namespace datalog { if (!cache.contains(new_name)) { was_added = true; func_decl* orig = decls_buf[0]; + // AG : is this ref counted func_decl* product_pred = m_ctx.mk_fresh_head_predicate(new_name, symbol::null, domain.size(), domain.c_ptr(), orig); cache.insert(new_name, product_pred); @@ -91,15 +91,16 @@ namespace datalog { return; } - rule_stratifier::item_set const & pred_decls = *decls[idx]; - for (rule_stratifier::item_set::iterator it = pred_decls.begin(); it != pred_decls.end(); ++it) { - decls_buf[idx] = *it; + // AG: why recursive? + for (auto &p : *decls[idx]) { + decls_buf[idx] = p; add_new_rel_symbols(idx + 1, decls, decls_buf, was_added); } } - void mk_synchronize::replace_applications(rule & r, rule_set & rules, ptr_vector & apps) { - app* replacing = product_application(apps); + void mk_synchronize::replace_applications(rule & r, rule_set & rules, + ptr_vector & apps) { + app_ref replacing = product_application(apps); ptr_vector new_tail; svector new_tail_neg; @@ -135,7 +136,10 @@ namespace datalog { rules.replace_rule(&r, new_rule.get()); } - rule_ref mk_synchronize::rename_bound_vars_in_rule(rule * r, unsigned & var_idx) { + rule_ref mk_synchronize::rename_bound_vars_in_rule(rule * r, + unsigned & var_idx) { + // AG: shift all variables in a rule so that lowest var index is var_idx? + // AG: update var_idx in the process? ptr_vector sorts; r->get_vars(m, sorts); expr_ref_vector revsub(m); @@ -152,17 +156,18 @@ namespace datalog { return new_rule; } - vector mk_synchronize::rename_bound_vars(ptr_vector const & heads, - rule_set & rules) { + vector mk_synchronize::rename_bound_vars(item_set_vector const & heads, + rule_set & rules) { + // AG: is every item_set in heads corresponds to rules that are merged? + // AG: why are bound variables renamed in the first place? + // AG: the data structure seems too complex vector result; unsigned var_idx = 0; - for (unsigned i = 0; i < heads.size(); ++i) { + for (auto item : heads) { rule_ref_vector dst_vector(rm); - for (rule_stratifier::item_set::iterator it = heads[i]->begin(); it != heads[i]->end(); ++it) { - func_decl * head = *it; - rule_vector const & src_rules = rules.get_predicate_rules(head); - for (unsigned j = 0; j < src_rules.size(); ++j) { - rule_ref new_rule = rename_bound_vars_in_rule(src_rules[j], var_idx); + for (auto *head : *item) { + for (auto *r : rules.get_predicate_rules(head)) { + rule_ref new_rule = rename_bound_vars_in_rule(r, var_idx); dst_vector.push_back(new_rule.get()); } } @@ -171,9 +176,11 @@ namespace datalog { return result; } - void mk_synchronize::add_rec_tail(vector< ptr_vector > & recursive_calls, ptr_vector & new_tail, - svector & new_tail_neg, unsigned & tail_idx) { - int max_size = recursive_calls[0].size(); + void mk_synchronize::add_rec_tail(vector< ptr_vector > & recursive_calls, + app_ref_vector & new_tail, + svector & new_tail_neg, + unsigned & tail_idx) { + int max_size = 0; unsigned n = recursive_calls.size(); for (unsigned i = 0; i < n; ++i) { if (recursive_calls[i].size() > max_size) { @@ -194,11 +201,12 @@ namespace datalog { } } - void mk_synchronize::add_non_rec_tail(rule & r, ptr_vector & new_tail, svector & new_tail_neg, - unsigned & tail_idx) { + void mk_synchronize::add_non_rec_tail(rule & r, app_ref_vector & new_tail, + svector & new_tail_neg, + unsigned & tail_idx) { for (unsigned i = 0; i < r.get_positive_tail_size(); ++i) { app* tail = r.get_tail(i); - if (!is_recursive_app(r, tail)) { + if (!is_recursive(r, *tail)) { ++tail_idx; new_tail[tail_idx] = tail; new_tail_neg[tail_idx] = false; @@ -216,15 +224,14 @@ namespace datalog { } } - app* mk_synchronize::product_application(ptr_vector const &apps) { - ptr_vector::const_iterator it = apps.begin(), end = apps.end(); + app_ref mk_synchronize::product_application(ptr_vector const &apps) { unsigned args_num = 0; string_buffer<> buffer; - for (; it != end; ++it) { - buffer << (*it)->get_decl()->get_name(); - buffer << "!!"; - args_num += (*it)->get_num_args(); + // AG: factor out into mk_name + for (auto *app : apps) { + buffer << app->get_decl()->get_name() << "!!"; + args_num += app->get_num_args(); } symbol name = symbol(buffer.c_str()); @@ -233,15 +240,13 @@ namespace datalog { ptr_vector args; args.resize(args_num); - it = apps.begin(); - for (unsigned args_idx = 0; it != end; ++it) { - app* a = *it; - for (unsigned i = 0; i < a->get_num_args(); ++i, ++args_idx) { - args[args_idx] = a->get_arg(i); - } + unsigned idx = 0; + for (auto *a : apps) { + for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i, ++idx) + args[idx] = a->get_arg(i); } - return m.mk_app(pred, args_num, args.c_ptr()); + return app_ref(m.mk_app(pred, args_num, args.c_ptr()), m); } rule_ref mk_synchronize::product_rule(rule_ref_vector const & rules) { @@ -261,7 +266,7 @@ namespace datalog { for (unsigned i = 0; i < n; ++i) { heads[i] = rules[i]->get_head(); } - app* product_head = product_application(heads); + app_ref product_head = product_application(heads); unsigned product_tail_length = 0; bool has_recursion = false; vector< ptr_vector > recursive_calls; @@ -271,7 +276,7 @@ namespace datalog { product_tail_length += rule.get_tail_size(); for (unsigned j = 0; j < rule.get_positive_tail_size(); ++j) { app* tail = rule.get_tail(j); - if (is_recursive_app(rule, tail)) { + if (is_recursive(rule, *tail)) { has_recursion = true; recursive_calls[i].push_back(tail); } @@ -281,7 +286,7 @@ namespace datalog { } } - ptr_vector new_tail; + app_ref_vector new_tail(m); svector new_tail_neg; new_tail.resize(product_tail_length); new_tail_neg.resize(product_tail_length); @@ -302,41 +307,36 @@ namespace datalog { return new_rule; } - void mk_synchronize::merge_rules(unsigned idx, rule_ref_vector & buf, vector const & merged_rules, - rule_set & all_rules) { + void mk_synchronize::merge_rules(unsigned idx, rule_ref_vector & buf, + vector const & merged_rules, + rule_set & all_rules) { if (idx >= merged_rules.size()) { rule_ref product = product_rule(buf); all_rules.add_rule(product.get()); return; } - rule_ref_vector const & pred_rules = merged_rules[idx]; - for (rule_ref_vector::iterator it = pred_rules.begin(); it != pred_rules.end(); ++it) { - buf[idx] = *it; + for (auto *r : merged_rules[idx]) { + buf[idx] = r; merge_rules(idx + 1, buf, merged_rules, all_rules); } } void mk_synchronize::merge_applications(rule & r, rule_set & rules) { - ptr_vector non_recursive_applications; + ptr_vector non_recursive_preds; obj_hashtable apps; for (unsigned i = 0; i < r.get_positive_tail_size(); ++i) { - app* application = r.get_tail(i); - if (!is_recursive_app(r, application) && has_recursive_premise(application)) { - apps.insert(application); + app* t = r.get_tail(i); + if (!is_recursive(r, *t) && has_recursive_premise(t)) { + apps.insert(t); } } - if (apps.size() < 2) { - return; - } + if (apps.size() < 2) return; + for (auto *a : apps) non_recursive_preds.push_back(a); - for (obj_hashtable::iterator it = apps.begin(); it != apps.end(); it++) { - non_recursive_applications.push_back(*it); - } + item_set_vector merged_decls = add_merged_decls(non_recursive_preds); - ptr_vector merged_decls = add_merged_decls(non_recursive_applications); - - unsigned n = non_recursive_applications.size(); + unsigned n = non_recursive_preds.size(); ptr_vector decls_buf; decls_buf.resize(n); bool was_added = false; @@ -348,7 +348,7 @@ namespace datalog { merge_rules(0, rules_buf, renamed_rules, rules); } - replace_applications(r, rules, non_recursive_applications); + replace_applications(r, rules, non_recursive_preds); m_deps->populate(rules); m_stratifier = alloc(rule_stratifier, *m_deps); } @@ -357,10 +357,7 @@ namespace datalog { rule_set* rules = alloc(rule_set, m_ctx); rules->inherit_predicates(source); - rule_set::iterator it = source.begin(), end = source.end(); - for (; it != end; ++it) { - rules->add_rule(*it); - } + for (auto *r : source) { rules->add_rule(r); } m_deps = alloc(rule_dependencies, m_ctx); m_deps->populate(*rules); @@ -372,6 +369,7 @@ namespace datalog { merge_applications(*r, *rules); ++current_rule; } + return rules; } diff --git a/src/muz/transforms/dl_mk_synchronize.h b/src/muz/transforms/dl_mk_synchronize.h index e4ce7cad1..767f09f6e 100644 --- a/src/muz/transforms/dl_mk_synchronize.h +++ b/src/muz/transforms/dl_mk_synchronize.h @@ -52,6 +52,9 @@ namespace datalog { \brief Implements a synchronous product transformation. */ class mk_synchronize : public rule_transformer::plugin { + public: + typedef ptr_vector item_set_vector; + private: context& m_ctx; ast_manager& m; rule_manager& rm; @@ -59,25 +62,30 @@ namespace datalog { scoped_ptr m_deps; scoped_ptr m_stratifier; map cache; + bool is_recursive(rule &r, func_decl &decl) const; + bool is_recursive(rule &r, expr &e) const; - bool is_recursive_app(rule & r, app * app) const; bool has_recursive_premise(app * app) const; - ptr_vector add_merged_decls(ptr_vector & apps); - void add_new_rel_symbols(unsigned idx, ptr_vector const & decls, - ptr_vector & buf, bool & was_added); + item_set_vector add_merged_decls(ptr_vector & apps); + void add_new_rel_symbols(unsigned idx, item_set_vector const & decls, + ptr_vector & buf, bool & was_added); - void replace_applications(rule & r, rule_set & rules, ptr_vector & apps); + void replace_applications(rule & r, rule_set & rules, + ptr_vector & apps); rule_ref rename_bound_vars_in_rule(rule * r, unsigned & var_idx); - vector rename_bound_vars(ptr_vector const & heads, rule_set & rules); + vector rename_bound_vars(item_set_vector const & heads, + rule_set & rules); - void add_rec_tail(vector< ptr_vector > & recursive_calls, ptr_vector & new_tail, - svector & new_tail_neg, unsigned & tail_idx); - void add_non_rec_tail(rule & r, ptr_vector & new_tail, svector & new_tail_neg, - unsigned & tail_idx); + void add_rec_tail(vector< ptr_vector > & recursive_calls, + app_ref_vector & new_tail, + svector & new_tail_neg, unsigned & tail_idx); + void add_non_rec_tail(rule & r, app_ref_vector & new_tail, + svector & new_tail_neg, + unsigned & tail_idx); - app* product_application(ptr_vector const & apps); + app_ref product_application(ptr_vector const & apps); rule_ref product_rule(rule_ref_vector const & rules); void merge_rules(unsigned idx, rule_ref_vector & buf, From 24044429a792008622ee49b505f0edd4a635f882 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 20 Aug 2018 15:24:11 -0400 Subject: [PATCH 019/138] Rename cache to m_cache --- src/muz/transforms/dl_mk_synchronize.cpp | 8 ++++---- src/muz/transforms/dl_mk_synchronize.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/muz/transforms/dl_mk_synchronize.cpp b/src/muz/transforms/dl_mk_synchronize.cpp index 268c994d1..af2a6f8d3 100644 --- a/src/muz/transforms/dl_mk_synchronize.cpp +++ b/src/muz/transforms/dl_mk_synchronize.cpp @@ -80,13 +80,13 @@ namespace datalog { symbol new_name = symbol(buffer.c_str()); - if (!cache.contains(new_name)) { + if (!m_cache.contains(new_name)) { was_added = true; func_decl* orig = decls_buf[0]; // AG : is this ref counted func_decl* product_pred = m_ctx.mk_fresh_head_predicate(new_name, symbol::null, domain.size(), domain.c_ptr(), orig); - cache.insert(new_name, product_pred); + m_cache.insert(new_name, product_pred); } return; } @@ -235,8 +235,8 @@ namespace datalog { } symbol name = symbol(buffer.c_str()); - SASSERT(cache.contains(name)); - func_decl * pred = cache[name]; + SASSERT(m_cache.contains(name)); + func_decl * pred = m_cache[name]; ptr_vector args; args.resize(args_num); diff --git a/src/muz/transforms/dl_mk_synchronize.h b/src/muz/transforms/dl_mk_synchronize.h index 767f09f6e..50d748bfa 100644 --- a/src/muz/transforms/dl_mk_synchronize.h +++ b/src/muz/transforms/dl_mk_synchronize.h @@ -61,7 +61,7 @@ namespace datalog { scoped_ptr m_deps; scoped_ptr m_stratifier; - map cache; + map m_cache; bool is_recursive(rule &r, func_decl &decl) const; bool is_recursive(rule &r, expr &e) const; From 7bff74dec08a4ce700672c0fd3b03f3edc5a47ab Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Mon, 20 Aug 2018 16:11:19 -0400 Subject: [PATCH 020/138] Minor pass on synchronize transform --- src/muz/transforms/dl_mk_synchronize.cpp | 40 ++++++++++++------------ src/muz/transforms/dl_mk_synchronize.h | 36 ++++++++++++++++++--- 2 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/muz/transforms/dl_mk_synchronize.cpp b/src/muz/transforms/dl_mk_synchronize.cpp index af2a6f8d3..348c9b5de 100644 --- a/src/muz/transforms/dl_mk_synchronize.cpp +++ b/src/muz/transforms/dl_mk_synchronize.cpp @@ -19,6 +19,7 @@ Revision History: --*/ #include "muz/transforms/dl_mk_synchronize.h" +#include namespace datalog { @@ -39,9 +40,6 @@ namespace datalog { unsigned num_of_stratum = m_stratifier->get_predicate_strat(hdecl); return strata[num_of_stratum]->contains(&decl); } - bool mk_synchronize::is_recursive(rule &r, expr &e) const { - return is_app(&e) && is_recursive(r, *to_app(&e)->get_decl()); - } bool mk_synchronize::has_recursive_premise(app * app) const { func_decl* app_decl = app->get_decl(); @@ -83,7 +81,6 @@ namespace datalog { if (!m_cache.contains(new_name)) { was_added = true; func_decl* orig = decls_buf[0]; - // AG : is this ref counted func_decl* product_pred = m_ctx.mk_fresh_head_predicate(new_name, symbol::null, domain.size(), domain.c_ptr(), orig); m_cache.insert(new_name, product_pred); @@ -91,7 +88,8 @@ namespace datalog { return; } - // AG: why recursive? + // -- compute Cartesian product of decls, and create a new + // -- predicate for each element of the product for (auto &p : *decls[idx]) { decls_buf[idx] = p; add_new_rel_symbols(idx + 1, decls, decls_buf, was_added); @@ -180,20 +178,20 @@ namespace datalog { app_ref_vector & new_tail, svector & new_tail_neg, unsigned & tail_idx) { - int max_size = 0; + unsigned max_sz = 0; + for (auto &rc : recursive_calls) + max_sz= std::max(rc.size(), max_sz); + unsigned n = recursive_calls.size(); - for (unsigned i = 0; i < n; ++i) { - if (recursive_calls[i].size() > max_size) { - max_size = recursive_calls[i].size(); - } - } - for (unsigned j = 0; j < max_size; ++j) { - ptr_vector merged_recursive_calls; + ptr_vector merged_recursive_calls; + + for (unsigned j = 0; j < max_sz; ++j) { + merged_recursive_calls.reset(); merged_recursive_calls.resize(n); for (unsigned i = 0; i < n; ++i) { - unsigned cur_size = recursive_calls[i].size(); - j < cur_size ? merged_recursive_calls[i] = recursive_calls[i][j]: - merged_recursive_calls[i] = recursive_calls[i][cur_size - 1]; + unsigned sz = recursive_calls[i].size(); + merged_recursive_calls[i] = + j < sz ? recursive_calls[i][j] : recursive_calls[i][sz - 1]; } ++tail_idx; new_tail[tail_idx] = product_application(merged_recursive_calls); @@ -204,7 +202,7 @@ namespace datalog { void mk_synchronize::add_non_rec_tail(rule & r, app_ref_vector & new_tail, svector & new_tail_neg, unsigned & tail_idx) { - for (unsigned i = 0; i < r.get_positive_tail_size(); ++i) { + for (unsigned i = 0, sz = r.get_positive_tail_size(); i < sz; ++i) { app* tail = r.get_tail(i); if (!is_recursive(r, *tail)) { ++tail_idx; @@ -212,12 +210,14 @@ namespace datalog { new_tail_neg[tail_idx] = false; } } - for (unsigned i = r.get_positive_tail_size(); i < r.get_uninterpreted_tail_size(); ++i) { + for (unsigned i = r.get_positive_tail_size(), + sz = r.get_uninterpreted_tail_size() ; i < sz; ++i) { ++tail_idx; new_tail[tail_idx] = r.get_tail(i); new_tail_neg[tail_idx] = true; } - for (unsigned i = r.get_uninterpreted_tail_size(); i < r.get_tail_size(); ++i) { + for (unsigned i = r.get_uninterpreted_tail_size(), + sz = r.get_tail_size(); i < sz; ++i) { ++tail_idx; new_tail[tail_idx] = r.get_tail(i); new_tail_neg[tail_idx] = r.is_neg_tail(i); @@ -254,7 +254,7 @@ namespace datalog { string_buffer<> buffer; bool first_rule = true; - for (rule_ref_vector::iterator it = rules.begin(); it != rules.end(); ++it, first_rule = false) { + for (auto it = rules.begin(); it != rules.end(); ++it, first_rule = false) { if (!first_rule) { buffer << "+"; } diff --git a/src/muz/transforms/dl_mk_synchronize.h b/src/muz/transforms/dl_mk_synchronize.h index 50d748bfa..6f4b3ca40 100644 --- a/src/muz/transforms/dl_mk_synchronize.h +++ b/src/muz/transforms/dl_mk_synchronize.h @@ -61,16 +61,45 @@ namespace datalog { scoped_ptr m_deps; scoped_ptr m_stratifier; - map m_cache; - bool is_recursive(rule &r, func_decl &decl) const; - bool is_recursive(rule &r, expr &e) const; + // symbol table that maps new predicate names to corresponding + // func_decl + map m_cache; + + /// returns true if decl is recursive in the given rule + /// requires that decl appears in the tail of r + bool is_recursive(rule &r, func_decl &decl) const; + bool is_recursive(rule &r, expr &e) const { + SASSERT(is_app(&e)); + return is_app(&e) && is_recursive(r, *to_app(&e)->get_decl()); + } + + /// returns true if the top-level predicate of app is recursive bool has_recursive_premise(app * app) const; item_set_vector add_merged_decls(ptr_vector & apps); + + /** + Compute Cartesian product of decls and create a new + predicate for each element. For example, if decls is + + ( (a, b), (c, d) ) ) + + create new predicates: a!!c, a!!d, b!!c, and b!!d + */ void add_new_rel_symbols(unsigned idx, item_set_vector const & decls, ptr_vector & buf, bool & was_added); + /** + Convert vector of predicate apps into a single app. For example, + (Foo a) (Bar b) becomes (Foo!!Bar a b) + */ + app_ref product_application(ptr_vector const & apps); + + /** + Replace a given rule by a rule in which conjunction of + predicates is replaced by a single product predicate + */ void replace_applications(rule & r, rule_set & rules, ptr_vector & apps); @@ -85,7 +114,6 @@ namespace datalog { svector & new_tail_neg, unsigned & tail_idx); - app_ref product_application(ptr_vector const & apps); rule_ref product_rule(rule_ref_vector const & rules); void merge_rules(unsigned idx, rule_ref_vector & buf, From 5d2f682f7a0730e87b631d2b97e102f5b82e9ff2 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 4 Sep 2018 15:29:14 -0400 Subject: [PATCH 021/138] Enable proof mode in add_cover --- src/muz/spacer/spacer_context.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index 617266779..dd6656954 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -2489,6 +2489,8 @@ expr_ref context::get_cover_delta(int level, func_decl* p_orig, func_decl* p) void context::add_cover(int level, func_decl* p, expr* property, bool bg) { + scoped_proof _pf_(m); + pred_transformer* pt = nullptr; if (!m_rels.find(p, pt)) { pt = alloc(pred_transformer, *this, get_manager(), p); From f67346d16e6abe41d5a0e2b664103914a1be1d74 Mon Sep 17 00:00:00 2001 From: Arie Gurfinkel Date: Tue, 4 Sep 2018 21:48:07 -0400 Subject: [PATCH 022/138] Fix is_infty_level to treat 2^16-1 as infinity --- src/muz/spacer/spacer_prop_solver.cpp | 1 + src/muz/spacer/spacer_util.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/muz/spacer/spacer_prop_solver.cpp b/src/muz/spacer/spacer_prop_solver.cpp index 004ea6754..4899ad692 100644 --- a/src/muz/spacer/spacer_prop_solver.cpp +++ b/src/muz/spacer/spacer_prop_solver.cpp @@ -94,6 +94,7 @@ void prop_solver::add_level() void prop_solver::ensure_level(unsigned lvl) { + if (is_infty_level(lvl)) return; while (lvl >= level_cnt()) { add_level(); } diff --git a/src/muz/spacer/spacer_util.h b/src/muz/spacer/spacer_util.h index 8da574d0e..0328d8640 100644 --- a/src/muz/spacer/spacer_util.h +++ b/src/muz/spacer/spacer_util.h @@ -48,7 +48,8 @@ namespace spacer { } inline bool is_infty_level(unsigned lvl) { - return lvl == infty_level (); + // XXX: level is 16 bits in class pob + return lvl >= 65535; } inline unsigned next_level(unsigned lvl) { From b09035565a093ff046e69c624f9e038e2cd53468 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Wed, 5 Sep 2018 19:04:11 -0400 Subject: [PATCH 023/138] canonicalize encoding of string constants/symbols --- src/ast/seq_decl_plugin.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ast/seq_decl_plugin.cpp b/src/ast/seq_decl_plugin.cpp index 07c6fd06a..06f30cbdf 100644 --- a/src/ast/seq_decl_plugin.cpp +++ b/src/ast/seq_decl_plugin.cpp @@ -842,7 +842,9 @@ void seq_decl_plugin::get_sort_names(svector & sort_names, symbol } app* seq_decl_plugin::mk_string(symbol const& s) { - parameter param(s); + zstring canonStr(s.bare_str()); + symbol canonSym(canonStr.encode().c_str()); + parameter param(canonSym); func_decl* f = m_manager->mk_const_decl(m_stringc_sym, m_string, func_decl_info(m_family_id, OP_STRING_CONST, 1, ¶m)); return m_manager->mk_const(f); From 211210338a4fac1a26073c6a2a7edf180c2b9ddc Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Fri, 7 Sep 2018 22:00:25 -0700 Subject: [PATCH 024/138] bound vars Signed-off-by: Lev Nachmanson --- src/smt/theory_lra.cpp | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 267412da6..0f07eac7e 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -154,6 +154,7 @@ class theory_lra::imp { ast_manager& m; theory_arith_params& m_arith_params; arith_util a; + bool m_has_int; arith_eq_adapter m_arith_eq_adapter; vector m_columns; @@ -624,6 +625,7 @@ class theory_lra::imp { } if (result == UINT_MAX) { result = m_solver->add_var(v, is_int(v)); + m_has_int |= is_int(v); m_theory_var2var_index.setx(v, result, UINT_MAX); m_var_index2theory_var.setx(result, v, UINT_MAX); m_var_trail.push_back(v); @@ -798,6 +800,7 @@ public: th(th), m(m), m_arith_params(ap), a(m), + m_has_int(false), m_arith_eq_adapter(th, ap, a), m_internalize_head(0), m_not_handled(0), @@ -1414,15 +1417,40 @@ public: return atom; } + bool all_variables_have_bounds() { + if (!m_has_int) { + return true; + } + unsigned nv = th.get_num_vars(); + bool added_bound = false; + for (unsigned v = 0; v < nv; ++v) { + lp::constraint_index ci; + rational bound; + lp::var_index vi = m_theory_var2var_index[v]; + if (!has_upper_bound(vi, ci, bound) && !has_lower_bound(vi, ci, bound)) { + lp::lar_term term; + term.add_monomial(rational::one(), vi); + app_ref b = mk_bound(term, rational::zero(), false); + TRACE("arith", tout << "added bound " << b << "\n";); + added_bound = true; + } + } + return !added_bound; + } + lbool check_lia() { if (m.canceled()) { TRACE("arith", tout << "canceled\n";); return l_undef; } + if (!all_variables_have_bounds()) { + return l_false; + } lp::lar_term term; lp::mpq k; lp::explanation ex; // TBD, this should be streamlined accross different explanations bool upper; + std::cout << "."; switch(m_lia->check(term, k, ex, upper)) { case lp::lia_move::sat: return l_true; @@ -2918,7 +2946,9 @@ public: for (auto const& kv : coeffs) { g = gcd(g, kv.m_value); } - if (!g.is_one() && !g.is_zero()) { + if (g.is_zero()) + return rational::one(); + if (!g.is_one()) { for (auto& kv : coeffs) { kv.m_value /= g; } From 67a2a26009f145fdcf010d99814807cedcc4a7a7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 9 Sep 2018 14:26:46 -0700 Subject: [PATCH 025/138] fixing bound detection (#86) * fixing bound detection Signed-off-by: Nikolaj Bjorner * check-idiv bounds Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 236 +++++++++++++++++++++++++++++++++++------ 1 file changed, 202 insertions(+), 34 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 0f07eac7e..f1ffbf60c 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -129,6 +129,7 @@ class theory_lra::imp { struct scope { unsigned m_bounds_lim; + unsigned m_idiv_lim; unsigned m_asserted_qhead; unsigned m_asserted_atoms_lim; unsigned m_underspecified_lim; @@ -230,6 +231,7 @@ class theory_lra::imp { svector m_asserted_atoms; expr* m_not_handled; ptr_vector m_underspecified; + ptr_vector m_idiv_terms; unsigned_vector m_var_trail; vector > m_use_list; // bounds where variables are used. @@ -443,6 +445,7 @@ class theory_lra::imp { } else if (a.is_idiv(n, n1, n2)) { if (!a.is_numeral(n2, r) || r.is_zero()) found_not_handled(n); + m_idiv_terms.push_back(n); app * mod = a.mk_mod(n1, n2); ctx().internalize(mod, false); if (ctx().relevancy()) ctx().add_relevancy_dependency(n, mod); @@ -452,6 +455,7 @@ class theory_lra::imp { if (!is_num) { found_not_handled(n); } +#if 0 else { app_ref div(a.mk_idiv(n1, n2), m); mk_enode(div); @@ -462,7 +466,8 @@ class theory_lra::imp { // abs(r) > v >= 0 assert_idiv_mod_axioms(u, v, w, r); } - if (!ctx().relevancy() && !is_num) mk_idiv_mod_axioms(n1, n2); +#endif + if (!ctx().relevancy()) mk_idiv_mod_axioms(n1, n2); } else if (a.is_rem(n, n1, n2)) { if (!a.is_numeral(n2, r) || r.is_zero()) found_not_handled(n); @@ -803,7 +808,7 @@ public: m_has_int(false), m_arith_eq_adapter(th, ap, a), m_internalize_head(0), - m_not_handled(0), + m_not_handled(nullptr), m_asserted_qhead(0), m_assume_eq_head(0), m_num_conflicts(0), @@ -913,6 +918,7 @@ public: scope& s = m_scopes.back(); s.m_bounds_lim = m_bounds_trail.size(); s.m_asserted_qhead = m_asserted_qhead; + s.m_idiv_lim = m_idiv_terms.size(); s.m_asserted_atoms_lim = m_asserted_atoms.size(); s.m_not_handled = m_not_handled; s.m_underspecified_lim = m_underspecified.size(); @@ -938,6 +944,7 @@ public: } m_theory_var2var_index[m_var_trail[i]] = UINT_MAX; } + m_idiv_terms.shrink(m_scopes[old_size].m_idiv_lim); m_asserted_atoms.shrink(m_scopes[old_size].m_asserted_atoms_lim); m_asserted_qhead = m_scopes[old_size].m_asserted_qhead; m_underspecified.shrink(m_scopes[old_size].m_underspecified_lim); @@ -1033,37 +1040,74 @@ public: add_def_constraint(m_solver->add_var_bound(vi, lp::LE, rational::zero())); add_def_constraint(m_solver->add_var_bound(get_var_index(v), lp::GE, rational::zero())); add_def_constraint(m_solver->add_var_bound(get_var_index(v), lp::LT, abs(r))); + TRACE("arith", m_solver->print_constraints(tout << term << "\n");); } void mk_idiv_mod_axioms(expr * p, expr * q) { if (a.is_zero(q)) { return; } + TRACE("arith", tout << expr_ref(p, m) << " " << expr_ref(q, m) << "\n";); // if q is zero, then idiv and mod are uninterpreted functions. expr_ref div(a.mk_idiv(p, q), m); expr_ref mod(a.mk_mod(p, q), m); expr_ref zero(a.mk_int(0), m); - literal q_ge_0 = mk_literal(a.mk_ge(q, zero)); - literal q_le_0 = mk_literal(a.mk_le(q, zero)); - // literal eqz = th.mk_eq(q, zero, false); literal eq = th.mk_eq(a.mk_add(a.mk_mul(q, div), mod), p, false); literal mod_ge_0 = mk_literal(a.mk_ge(mod, zero)); - // q >= 0 or p = (p mod q) + q * (p div q) - // q <= 0 or p = (p mod q) + q * (p div q) - // q >= 0 or (p mod q) >= 0 - // q <= 0 or (p mod q) >= 0 - // q <= 0 or (p mod q) < q - // q >= 0 or (p mod q) < -q - // enable_trace("mk_bool_var"); - mk_axiom(q_ge_0, eq); - mk_axiom(q_le_0, eq); - mk_axiom(q_ge_0, mod_ge_0); - mk_axiom(q_le_0, mod_ge_0); - mk_axiom(q_le_0, ~mk_literal(a.mk_ge(a.mk_sub(mod, q), zero))); - mk_axiom(q_ge_0, ~mk_literal(a.mk_ge(a.mk_add(mod, q), zero))); - rational k; - if (m_arith_params.m_arith_enum_const_mod && a.is_numeral(q, k) && - k.is_pos() && k < rational(8)) { + literal div_ge_0 = mk_literal(a.mk_ge(div, zero)); + literal div_le_0 = mk_literal(a.mk_le(div, zero)); + literal p_ge_0 = mk_literal(a.mk_ge(p, zero)); + literal p_le_0 = mk_literal(a.mk_le(p, zero)); + + rational k(0); + expr_ref upper(m); + + if (a.is_numeral(q, k)) { + if (k.is_pos()) { + upper = a.mk_numeral(k - 1, true); + } + else if (k.is_neg()) { + upper = a.mk_numeral(-k - 1, true); + } + } + else { + k = rational::zero(); + } + + if (!k.is_zero()) { + mk_axiom(eq); + mk_axiom(mod_ge_0); + mk_axiom(mk_literal(a.mk_le(mod, upper))); + if (k.is_pos()) { + mk_axiom(~p_ge_0, div_ge_0); + mk_axiom(~p_le_0, div_le_0); + } + else { + mk_axiom(~p_ge_0, div_le_0); + mk_axiom(~p_le_0, div_ge_0); + } + } + else { + // q >= 0 or p = (p mod q) + q * (p div q) + // q <= 0 or p = (p mod q) + q * (p div q) + // q >= 0 or (p mod q) >= 0 + // q <= 0 or (p mod q) >= 0 + // q <= 0 or (p mod q) < q + // q >= 0 or (p mod q) < -q + literal q_ge_0 = mk_literal(a.mk_ge(q, zero)); + literal q_le_0 = mk_literal(a.mk_le(q, zero)); + mk_axiom(q_ge_0, eq); + mk_axiom(q_le_0, eq); + mk_axiom(q_ge_0, mod_ge_0); + mk_axiom(q_le_0, mod_ge_0); + mk_axiom(q_le_0, ~mk_literal(a.mk_ge(a.mk_sub(mod, q), zero))); + mk_axiom(q_ge_0, ~mk_literal(a.mk_ge(a.mk_add(mod, q), zero))); + mk_axiom(q_le_0, ~p_ge_0, div_ge_0); + mk_axiom(q_le_0, ~p_le_0, div_le_0); + mk_axiom(q_ge_0, ~p_ge_0, div_le_0); + mk_axiom(q_ge_0, ~p_le_0, div_ge_0); + } + if (m_arith_params.m_arith_enum_const_mod && k.is_pos() && k < rational(8)) { unsigned _k = k.get_unsigned(); literal_buffer lits; for (unsigned j = 0; j < _k; ++j) { @@ -1211,10 +1255,9 @@ public: } void init_variable_values() { + reset_variable_values(); if (!m.canceled() && m_solver.get() && th.get_num_vars() > 0) { - reset_variable_values(); m_solver->get_model(m_variable_values); - TRACE("arith", display(tout);); } } @@ -1317,6 +1360,7 @@ public: } final_check_status final_check_eh() { + IF_VERBOSE(2, verbose_stream() << "final-check\n"); m_use_nra_model = false; lbool is_sat = l_true; if (m_solver->get_status() != lp::lp_status::OPTIMAL) { @@ -1331,7 +1375,7 @@ public: } if (assume_eqs()) { return FC_CONTINUE; - } + } switch (check_lia()) { case l_true: @@ -1343,7 +1387,7 @@ public: st = FC_CONTINUE; break; } - + switch (check_nra()) { case l_true: break; @@ -1422,20 +1466,126 @@ public: return true; } unsigned nv = th.get_num_vars(); - bool added_bound = false; + bool all_bounded = true; for (unsigned v = 0; v < nv; ++v) { - lp::constraint_index ci; - rational bound; lp::var_index vi = m_theory_var2var_index[v]; - if (!has_upper_bound(vi, ci, bound) && !has_lower_bound(vi, ci, bound)) { + if (!m_solver->is_term(vi) && !var_has_bound(vi, true) && !var_has_bound(vi, false)) { lp::lar_term term; term.add_monomial(rational::one(), vi); - app_ref b = mk_bound(term, rational::zero(), false); + app_ref b = mk_bound(term, rational::zero(), true); TRACE("arith", tout << "added bound " << b << "\n";); - added_bound = true; + IF_VERBOSE(2, verbose_stream() << "bound: " << b << "\n"); + all_bounded = false; } } - return !added_bound; + return all_bounded; + } + + /** + * n = (div p q) + * + * (div p q) * q + (mod p q) = p, (mod p q) >= 0 + * + * 0 < q => (p/q <= v(p)/v(q) => n <= floor(v(p)/v(q))) + * 0 < q => (v(p)/v(q) <= p/q => v(p)/v(q) - 1 < n) + * + */ + bool check_idiv_bounds() { + if (m_idiv_terms.empty()) { + return true; + } + bool all_divs_valid = true; + init_variable_values(); + for (expr* n : m_idiv_terms) { + expr* p = nullptr, *q = nullptr; + VERIFY(a.is_idiv(n, p, q)); + theory_var v = mk_var(n); + theory_var v1 = mk_var(p); + theory_var v2 = mk_var(q); + rational r = get_value(v); + rational r1 = get_value(v1); + rational r2 = get_value(v2); + rational r3; + if (r2.is_zero()) { + continue; + } + if (r1.is_int() && r2.is_int() && r == div(r1, r2)) { + continue; + } + if (r2.is_neg()) { + // TBD + continue; + } + + if (a.is_numeral(q, r3)) { + + SASSERT(r3 == r2 && r2.is_int()); + // p <= r1 => n <= div(r1, r2) + // r1 <= p => div(r1, r2) <= n + literal p_le_r1 = mk_literal(a.mk_le(p, a.mk_numeral(ceil(r1), true))); + literal p_ge_r1 = mk_literal(a.mk_ge(p, a.mk_numeral(floor(r1), true))); + literal n_le_div = mk_literal(a.mk_le(n, a.mk_numeral(div(ceil(r1), r2), true))); + literal n_ge_div = mk_literal(a.mk_ge(n, a.mk_numeral(div(floor(r1), r2), true))); + mk_axiom(~p_le_r1, n_le_div); + mk_axiom(~p_ge_r1, n_ge_div); + + all_divs_valid = false; + + TRACE("arith", + literal_vector lits; + lits.push_back(~p_le_r1); + lits.push_back(n_le_div); + ctx().display_literals_verbose(tout, lits) << "\n"; + lits[0] = ~p_ge_r1; + lits[1] = n_ge_div; + ctx().display_literals_verbose(tout, lits) << "\n";); + continue; + } + + if (!r1.is_int() || !r2.is_int()) { + // std::cout << r1 << " " << r2 << " " << r << " " << expr_ref(n, m) << "\n"; + // TBD + // r1 = 223/4, r2 = 2, r = 219/8 + // take ceil(r1), floor(r1), ceil(r2), floor(r2), for floor(r2) > 0 + // then + // p/q <= ceil(r1)/floor(r2) => n <= div(ceil(r1), floor(r2)) + // p/q >= floor(r1)/ceil(r2) => n >= div(floor(r1), ceil(r2)) + continue; + } + + + all_divs_valid = false; + + + // + // p/q <= r1/r2 => n <= div(r1, r2) + // <=> + // p*r2 <= q*r1 => n <= div(r1, r2) + // + // p/q >= r1/r2 => n >= div(r1, r2) + // <=> + // p*r2 >= r1*q => n >= div(r1, r2) + // + expr_ref zero(a.mk_int(0), m); + expr_ref divc(a.mk_numeral(div(r1, r2), true), m); + expr_ref pqr(a.mk_sub(a.mk_mul(a.mk_numeral(r2, true), p), a.mk_mul(a.mk_numeral(r1, true), q)), m); + literal pq_lhs = ~mk_literal(a.mk_le(pqr, zero)); + literal pq_rhs = ~mk_literal(a.mk_ge(pqr, zero)); + literal n_le_div = mk_literal(a.mk_le(n, divc)); + literal n_ge_div = mk_literal(a.mk_ge(n, divc)); + mk_axiom(pq_lhs, n_le_div); + mk_axiom(pq_rhs, n_ge_div); + TRACE("arith", + literal_vector lits; + lits.push_back(pq_lhs); + lits.push_back(n_le_div); + ctx().display_literals_verbose(tout, lits) << "\n"; + lits[0] = pq_rhs; + lits[1] = n_ge_div; + ctx().display_literals_verbose(tout, lits) << "\n";); + } + + return all_divs_valid; } lbool check_lia() { @@ -1444,19 +1594,24 @@ public: return l_undef; } if (!all_variables_have_bounds()) { + TRACE("arith", tout << "not all variables have bounds\n";); + return l_false; + } + if (!check_idiv_bounds()) { + TRACE("arith", tout << "idiv bounds check\n";); return l_false; } lp::lar_term term; lp::mpq k; lp::explanation ex; // TBD, this should be streamlined accross different explanations bool upper; - std::cout << "."; switch(m_lia->check(term, k, ex, upper)) { case lp::lia_move::sat: return l_true; case lp::lia_move::branch: { TRACE("arith", tout << "branch\n";); app_ref b = mk_bound(term, k, !upper); + IF_VERBOSE(2, verbose_stream() << "branch " << b << "\n";); // branch on term >= k + 1 // branch on term <= k // TBD: ctx().force_phase(ctx().get_literal(b)); @@ -1469,6 +1624,7 @@ public: ++m_stats.m_gomory_cuts; // m_explanation implies term <= k app_ref b = mk_bound(term, k, !upper); + IF_VERBOSE(2, verbose_stream() << "cut " << b << "\n";); m_eqs.reset(); m_core.reset(); m_params.reset(); @@ -2411,6 +2567,18 @@ public: } } + bool var_has_bound(lp::var_index vi, bool is_lower) { + bool is_strict = false; + rational b; + lp::constraint_index ci; + if (is_lower) { + return m_solver->has_lower_bound(vi, ci, b, is_strict); + } + else { + return m_solver->has_upper_bound(vi, ci, b, is_strict); + } + } + bool has_upper_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, false); } bool has_lower_bound(lp::var_index vi, lp::constraint_index& ci, rational const& bound) { return has_bound(vi, ci, bound, true); } @@ -2981,7 +3149,7 @@ public: } if (!ctx().b_internalized(b)) { fm.hide(b->get_decl()); - bool_var bv = ctx().mk_bool_var(b); + bool_var bv = ctx().mk_bool_var(b); ctx().set_var_theory(bv, get_id()); // ctx().set_enode_flag(bv, true); lp_api::bound_kind bkind = lp_api::bound_kind::lower_t; From fae66671d8b7edf22e1508796f063caf3d0b9497 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 10 Sep 2018 08:57:35 -0700 Subject: [PATCH 026/138] fix #1817 Signed-off-by: Nikolaj Bjorner --- src/smt/smt_internalizer.cpp | 41 +++++++++++++++++------------------- src/smt/theory_arith_core.h | 1 - src/smt/theory_lra.cpp | 8 +++---- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/smt/smt_internalizer.cpp b/src/smt/smt_internalizer.cpp index 1414cb522..618450f9d 100644 --- a/src/smt/smt_internalizer.cpp +++ b/src/smt/smt_internalizer.cpp @@ -216,13 +216,17 @@ namespace smt { SASSERT(m_manager.is_bool(n)); if (is_gate(m_manager, n)) { switch(to_app(n)->get_decl_kind()) { - case OP_AND: - UNREACHABLE(); + case OP_AND: { + for (expr * arg : *to_app(n)) { + internalize(arg, true); + literal lit = get_literal(arg); + mk_root_clause(1, &lit, pr); + } + break; + } case OP_OR: { literal_buffer lits; - unsigned num = to_app(n)->get_num_args(); - for (unsigned i = 0; i < num; i++) { - expr * arg = to_app(n)->get_arg(i); + for (expr * arg : *to_app(n)) { internalize(arg, true); lits.push_back(get_literal(arg)); } @@ -294,8 +298,7 @@ namespace smt { sort * s = m_manager.get_sort(n->get_arg(0)); sort_ref u(m_manager.mk_fresh_sort("distinct-elems"), m_manager); func_decl_ref f(m_manager.mk_fresh_func_decl("distinct-aux-f", "", 1, &s, u), m_manager); - for (unsigned i = 0; i < num_args; i++) { - expr * arg = n->get_arg(i); + for (expr * arg : *n) { app_ref fapp(m_manager.mk_app(f, arg), m_manager); app_ref val(m_manager.mk_fresh_const("unique-value", u), m_manager); enode * e = mk_enode(val, false, false, true); @@ -826,9 +829,7 @@ namespace smt { void context::internalize_uninterpreted(app * n) { SASSERT(!e_internalized(n)); // process args - unsigned num = n->get_num_args(); - for (unsigned i = 0; i < num; i++) { - expr * arg = n->get_arg(i); + for (expr * arg : *n) { internalize(arg, false); SASSERT(e_internalized(arg)); } @@ -1542,10 +1543,9 @@ namespace smt { void context::add_and_rel_watches(app * n) { if (relevancy()) { relevancy_eh * eh = m_relevancy_propagator->mk_and_relevancy_eh(n); - unsigned num = n->get_num_args(); - for (unsigned i = 0; i < num; i++) { + for (expr * arg : *n) { // if one child is assigned to false, the and-parent must be notified - literal l = get_literal(n->get_arg(i)); + literal l = get_literal(arg); add_rel_watch(~l, eh); } } @@ -1554,10 +1554,9 @@ namespace smt { void context::add_or_rel_watches(app * n) { if (relevancy()) { relevancy_eh * eh = m_relevancy_propagator->mk_or_relevancy_eh(n); - unsigned num = n->get_num_args(); - for (unsigned i = 0; i < num; i++) { + for (expr * arg : *n) { // if one child is assigned to true, the or-parent must be notified - literal l = get_literal(n->get_arg(i)); + literal l = get_literal(arg); add_rel_watch(l, eh); } } @@ -1588,9 +1587,8 @@ namespace smt { TRACE("mk_and_cnstr", tout << "l: "; display_literal(tout, l); tout << "\n";); literal_buffer buffer; buffer.push_back(l); - unsigned num_args = n->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - literal l_arg = get_literal(n->get_arg(i)); + for (expr * arg : *n) { + literal l_arg = get_literal(arg); TRACE("mk_and_cnstr", tout << "l_arg: "; display_literal(tout, l_arg); tout << "\n";); mk_gate_clause(~l, l_arg); buffer.push_back(~l_arg); @@ -1602,9 +1600,8 @@ namespace smt { literal l = get_literal(n); literal_buffer buffer; buffer.push_back(~l); - unsigned num_args = n->get_num_args(); - for (unsigned i = 0; i < num_args; i++) { - literal l_arg = get_literal(n->get_arg(i)); + for (expr * arg : *n) { + literal l_arg = get_literal(arg); mk_gate_clause(l, ~l_arg); buffer.push_back(l_arg); } diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index f96b6228b..67271ad4f 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -484,7 +484,6 @@ namespace smt { void theory_arith::mk_idiv_mod_axioms(expr * dividend, expr * divisor) { if (!m_util.is_zero(divisor)) { ast_manager & m = get_manager(); - bool is_numeral = m_util.is_numeral(divisor); // if divisor is zero, then idiv and mod are uninterpreted functions. expr_ref div(m), mod(m), zero(m), abs_divisor(m), one(m); expr_ref eqz(m), eq(m), lower(m), upper(m); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 267412da6..b7f8549fb 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -692,9 +692,7 @@ class theory_lra::imp { ++m_stats.m_add_rows; } - void internalize_eq(theory_var v1, theory_var v2) { - enode* n1 = get_enode(v1); - enode* n2 = get_enode(v2); + void internalize_eq(theory_var v1, theory_var v2) { app_ref term(m.mk_fresh_const("eq", a.mk_real()), m); scoped_internalize_state st(*this); st.vars().push_back(v1); @@ -707,8 +705,8 @@ class theory_lra::imp { add_def_constraint(m_solver->add_var_bound(vi, lp::GE, rational::zero())); TRACE("arith", { - expr* o1 = n1->get_owner(); - expr* o2 = n2->get_owner(); + expr* o1 = get_enode(v1)->get_owner(); + expr* o2 = get_enode(v2)->get_owner(); tout << "v" << v1 << " = " << "v" << v2 << ": " << mk_pp(o1, m) << " = " << mk_pp(o2, m) << "\n"; }); From 8068c64cab45f53fb95d807fe6a68941b2cf2985 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 10 Sep 2018 11:02:38 -0700 Subject: [PATCH 027/138] avoid using not initialized variables in theory_lra Signed-off-by: Lev Nachmanson --- src/smt/theory_lra.cpp | 4 +++- src/util/lp/lp_settings.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index f1ffbf60c..b0ba51129 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1465,10 +1465,12 @@ public: if (!m_has_int) { return true; } - unsigned nv = th.get_num_vars(); + unsigned nv = std::min(th.get_num_vars(), m_theory_var2var_index.size()); bool all_bounded = true; for (unsigned v = 0; v < nv; ++v) { lp::var_index vi = m_theory_var2var_index[v]; + if (vi == UINT_MAX) + continue; if (!m_solver->is_term(vi) && !var_has_bound(vi, true) && !var_has_bound(vi, false)) { lp::lar_term term; term.add_monomial(rational::one(), vi); diff --git a/src/util/lp/lp_settings.h b/src/util/lp/lp_settings.h index dd19df23a..71be7258a 100644 --- a/src/util/lp/lp_settings.h +++ b/src/util/lp/lp_settings.h @@ -357,7 +357,7 @@ public: } #ifdef Z3DEBUG - static unsigned ddd; // used for debugging +static unsigned ddd; // used for debugging #endif }; // end of lp_settings class From 813b9063417fcaef8dd4068f8e5d9f849f68724b Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 10 Sep 2018 13:43:29 -0700 Subject: [PATCH 028/138] do not bound all free vars Signed-off-by: Lev Nachmanson --- src/smt/theory_lra.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index b0ba51129..9c6d3f89a 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1461,7 +1461,7 @@ public: return atom; } - bool all_variables_have_bounds() { + bool make_sure_all_vars_have_bounds() { if (!m_has_int) { return true; } @@ -1595,10 +1595,6 @@ public: TRACE("arith", tout << "canceled\n";); return l_undef; } - if (!all_variables_have_bounds()) { - TRACE("arith", tout << "not all variables have bounds\n";); - return l_false; - } if (!check_idiv_bounds()) { TRACE("arith", tout << "idiv bounds check\n";); return l_false; From a37d05d54b9ca10d4c613a4bb3a980f1bb0c1c4a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 10 Sep 2018 13:53:44 -0700 Subject: [PATCH 029/138] fix #1819 Signed-off-by: Nikolaj Bjorner --- src/smt/theory_arith_int.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/smt/theory_arith_int.h b/src/smt/theory_arith_int.h index ee3bd5e2e..afe527a98 100644 --- a/src/smt/theory_arith_int.h +++ b/src/smt/theory_arith_int.h @@ -396,7 +396,9 @@ namespace smt { for (; it != end; ++it) { if (!it->is_dead() && it->m_var != b && is_free(it->m_var)) { theory_var v = it->m_var; - expr * bound = m_util.mk_ge(get_enode(v)->get_owner(), m_util.mk_numeral(rational::zero(), is_int(v))); + expr* e = get_enode(v)->get_owner(); + bool _is_int = m_util.is_int(e); + expr * bound = m_util.mk_ge(e, m_util.mk_numeral(rational::zero(), _is_int)); context & ctx = get_context(); ctx.internalize(bound, true); ctx.mark_as_relevant(bound); From e818b7bd2732c87dbbee30b17e6563b9c652427c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 10 Sep 2018 15:15:00 -0700 Subject: [PATCH 030/138] fix #1812 Signed-off-by: Nikolaj Bjorner --- src/ast/ast.cpp | 6 +++++ src/ast/ast.h | 1 + src/smt/smt_model_checker.cpp | 45 +++++++++++++++++++++++++++++++---- src/smt/smt_model_checker.h | 3 +++ 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 11a15492c..65d3c7821 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -1656,6 +1656,12 @@ bool ast_manager::are_distinct(expr* a, expr* b) const { return false; } +func_decl* ast_manager::get_rec_fun_decl(quantifier* q) const { + SASSERT(is_rec_fun_def(q)); + return to_app(to_app(q->get_pattern(0))->get_arg(0))->get_decl(); +} + + void ast_manager::register_plugin(family_id id, decl_plugin * plugin) { SASSERT(m_plugins.get(id, 0) == 0); m_plugins.setx(id, plugin, 0); diff --git a/src/ast/ast.h b/src/ast/ast.h index 89df04961..c1193dfbd 100644 --- a/src/ast/ast.h +++ b/src/ast/ast.h @@ -1632,6 +1632,7 @@ public: bool is_rec_fun_def(quantifier* q) const { return q->get_qid() == m_rec_fun; } bool is_lambda_def(quantifier* q) const { return q->get_qid() == m_lambda_def; } + func_decl* get_rec_fun_decl(quantifier* q) const; symbol const& rec_fun_qid() const { return m_rec_fun; } diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index 0fea4d13d..c3af41dcf 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -47,6 +47,7 @@ namespace smt { m_model_finder(mf), m_max_cexs(1), m_iteration_idx(0), + m_has_rec_fun(false), m_curr_model(nullptr), m_pinned_exprs(m) { } @@ -351,9 +352,7 @@ namespace smt { bool model_checker::check_rec_fun(quantifier* q, bool strict_rec_fun) { TRACE("model_checker", tout << mk_pp(q, m) << "\n";); SASSERT(q->get_num_patterns() == 2); // first pattern is the function, second is the body. - expr* fn = to_app(q->get_pattern(0))->get_arg(0); - SASSERT(is_app(fn)); - func_decl* f = to_app(fn)->get_decl(); + func_decl* f = m.get_rec_fun_decl(q); expr_ref_vector args(m); unsigned num_decls = q->get_num_decls(); @@ -433,7 +432,7 @@ namespace smt { TRACE("model_checker", tout << "model checker result: " << (num_failures == 0) << "\n";); m_max_cexs += m_params.m_mbqi_max_cexs; - if (num_failures == 0 && !m_context->validate_model()) { + if (num_failures == 0 && (!m_context->validate_model() || has_rec_under_quantifiers())) { num_failures = 1; // this time force expanding recursive function definitions // that are not forced true in the current model. @@ -450,6 +449,43 @@ namespace smt { return num_failures == 0; } + struct has_rec_fun_proc { + obj_hashtable& m_rec_funs; + bool m_has_rec_fun; + + bool has_rec_fun() const { return m_has_rec_fun; } + + has_rec_fun_proc(obj_hashtable& rec_funs): + m_rec_funs(rec_funs), + m_has_rec_fun(false) {} + + void operator()(app* fn) { + m_has_rec_fun |= m_rec_funs.contains(fn->get_decl()); + } + void operator()(expr*) {} + }; + + bool model_checker::has_rec_under_quantifiers() { + if (!m_has_rec_fun) { + return false; + } + obj_hashtable rec_funs; + for (quantifier * q : *m_qm) { + if (m.is_rec_fun_def(q)) { + rec_funs.insert(m.get_rec_fun_decl(q)); + } + } + expr_fast_mark1 visited; + has_rec_fun_proc proc(rec_funs); + for (quantifier * q : *m_qm) { + if (!m.is_rec_fun_def(q)) { + quick_for_each_expr(proc, visited, q); + if (proc.has_rec_fun()) return true; + } + } + return false; + } + // // (repeated from defined_names.cpp) // NB. The pattern for lambdas is incomplete. @@ -479,6 +515,7 @@ namespace smt { } found_relevant = true; if (m.is_rec_fun_def(q)) { + m_has_rec_fun = true; if (!check_rec_fun(q, strict_rec_fun)) { TRACE("model_checker", tout << "checking recursive function failed\n";); num_failures++; diff --git a/src/smt/smt_model_checker.h b/src/smt/smt_model_checker.h index 40a58efea..57edf3034 100644 --- a/src/smt/smt_model_checker.h +++ b/src/smt/smt_model_checker.h @@ -51,8 +51,10 @@ namespace smt { scoped_ptr m_aux_context; // Auxiliary context used for model checking quantifiers. unsigned m_max_cexs; unsigned m_iteration_idx; + bool m_has_rec_fun; proto_model * m_curr_model; obj_map m_value2expr; + friend class instantiation_set; void init_aux_context(); @@ -64,6 +66,7 @@ namespace smt { bool add_blocking_clause(model * cex, expr_ref_vector & sks); bool check(quantifier * q); bool check_rec_fun(quantifier* q, bool strict_rec_fun); + bool has_rec_under_quantifiers(); void check_quantifiers(bool strict_rec_fun, bool& found_relevant, unsigned& num_failures); struct instance { From f810a5d8c33363533bd9713f6a7e1482e9986163 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Mon, 10 Sep 2018 15:22:48 -0700 Subject: [PATCH 031/138] remove an assert Signed-off-by: Lev Nachmanson --- src/util/lp/lar_solver.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp index 654eb7017..d4db0bc91 100644 --- a/src/util/lp/lar_solver.cpp +++ b/src/util/lp/lar_solver.cpp @@ -1964,7 +1964,6 @@ void lar_solver::update_boxed_column_type_and_bound(var_index j, lconstraint_kin if (up < m_mpq_lar_core_solver.m_r_lower_bounds[j]) { m_status = lp_status::INFEASIBLE; - lp_assert(false); m_infeasible_column_index = j; } else { From 18bec88a8af07410c8b0e3bec9e89aa5d54556c6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 10 Sep 2018 15:52:02 -0700 Subject: [PATCH 032/138] purify non-constant terms by default to enforce theory #1820 Signed-off-by: Nikolaj Bjorner --- src/opt/opt_context.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 5d4eb3fc5..b1296f71e 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -1084,11 +1084,7 @@ namespace opt { } term = m_arith.mk_add(args.size(), args.c_ptr()); } - else if (m_arith.is_arith_expr(term) && !is_mul_const(term)) { - TRACE("opt", tout << "Purifying " << term << "\n";); - term = purify(fm, term); - } - else if (m.is_ite(term)) { + else if (m.is_ite(term) || !is_mul_const(term)) { TRACE("opt", tout << "Purifying " << term << "\n";); term = purify(fm, term); } From 36a14a354a7a7b6bacdadc0e0af38ba18aeaa4fd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 11 Sep 2018 03:14:31 -0700 Subject: [PATCH 033/138] disable dotnet in ci script. It seems to get turned on even if dotnet bindings are not requested Signed-off-by: Nikolaj Bjorner --- contrib/ci/scripts/build_z3_cmake.sh | 22 +++++++++++----------- src/api/python/z3/z3.py | 2 ++ src/smt/smt_farkas_util.cpp | 12 +++++++++--- src/test/pb2bv.cpp | 12 ++++++++++++ 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/contrib/ci/scripts/build_z3_cmake.sh b/contrib/ci/scripts/build_z3_cmake.sh index c1014d5d5..b12e90aa8 100755 --- a/contrib/ci/scripts/build_z3_cmake.sh +++ b/contrib/ci/scripts/build_z3_cmake.sh @@ -78,17 +78,17 @@ else fi # .NET bindings? -if [ "X${DOTNET_BINDINGS}" = "X1" ]; then - ADDITIONAL_Z3_OPTS+=( \ - '-DBUILD_DOTNET_BINDINGS=ON' \ - '-DINSTALL_DOTNET_BINDINGS=ON' \ - ) -else - ADDITIONAL_Z3_OPTS+=( \ - '-DBUILD_DOTNET_BINDINGS=OFF' \ - '-DINSTALL_DOTNET_BINDINGS=OFF' \ - ) -fi +#if [ "X${DOTNET_BINDINGS}" = "X1" ]; then +# ADDITIONAL_Z3_OPTS+=( \ +# '-DBUILD_DOTNET_BINDINGS=ON' \ +# '-DINSTALL_DOTNET_BINDINGS=ON' \ +# ) +#else +# ADDITIONAL_Z3_OPTS+=( \ +# '-DBUILD_DOTNET_BINDINGS=OFF' \ +# '-DINSTALL_DOTNET_BINDINGS=OFF' \ +# ) +#fi # Java bindings? if [ "X${JAVA_BINDINGS}" = "X1" ]; then diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 0eeeeaecc..e0769c1fd 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -2176,6 +2176,8 @@ class ArithRef(ExprRef): >>> (x * y).sort() Real """ + if isinstance(other, BoolRef): + return If(other, self, 0) a, b = _coerce_exprs(self, other) return ArithRef(_mk_bin(Z3_mk_mul, a, b), self.ctx) diff --git a/src/smt/smt_farkas_util.cpp b/src/smt/smt_farkas_util.cpp index ff415ad0c..f7aaea61b 100644 --- a/src/smt/smt_farkas_util.cpp +++ b/src/smt/smt_farkas_util.cpp @@ -312,6 +312,13 @@ namespace smt { } expr_ref farkas_util::get() { + TRACE("arith", + for (unsigned i = 0; i < m_coeffs.size(); ++i) { + tout << m_coeffs[i] << " * (" << mk_pp(m_ineqs[i].get(), m) << ") "; + } + tout << "\n"; + ); + m_normalize_factor = rational::one(); expr_ref res(m); if (m_coeffs.empty()) { @@ -330,13 +337,12 @@ namespace smt { partition_ineqs(); expr_ref_vector lits(m); unsigned lo = 0; - for (unsigned i = 0; i < m_his.size(); ++i) { - unsigned hi = m_his[i]; + for (unsigned hi : m_his) { lits.push_back(extract_consequence(lo, hi)); lo = hi; } bool_rewriter(m).mk_or(lits.size(), lits.c_ptr(), res); - IF_VERBOSE(2, { if (lits.size() > 1) { verbose_stream() << "combined lemma: " << mk_pp(res, m) << "\n"; } }); + IF_VERBOSE(2, { if (lits.size() > 1) { verbose_stream() << "combined lemma: " << res << "\n"; } }); } else { res = extract_consequence(0, m_coeffs.size()); diff --git a/src/test/pb2bv.cpp b/src/test/pb2bv.cpp index 493d81bb7..623f7c9f1 100644 --- a/src/test/pb2bv.cpp +++ b/src/test/pb2bv.cpp @@ -18,6 +18,7 @@ Copyright (c) 2015 Microsoft Corporation #include "ast/rewriter/th_rewriter.h" #include "tactic/fd_solver/fd_solver.h" #include "solver/solver.h" +#include "ast/arith_decl_plugin.h" static void test1() { ast_manager m; @@ -194,9 +195,20 @@ static void test3() { } } +static void test4() { + ast_manager m; + reg_decl_plugins(m); + arith_util arith(m); + expr_ref a(m.mk_const(symbol("a"), arith.mk_int()), m); + expr_ref b(m.mk_const(symbol("b"), arith.mk_int()), m); + expr_ref eq(m.mk_eq(a,b), m); + std::cout << "is_atom: " << is_atom(m, eq) << "\n"; +} + void tst_pb2bv() { test1(); test2(); test3(); + test4(); } From ef310648aebfbac16695ae6b6f6a1244674d7648 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 11 Sep 2018 03:50:49 -0700 Subject: [PATCH 034/138] re-enable dotnet, ci got broken. Related #1815 Signed-off-by: Nikolaj Bjorner --- contrib/ci/scripts/build_z3_cmake.sh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/contrib/ci/scripts/build_z3_cmake.sh b/contrib/ci/scripts/build_z3_cmake.sh index b12e90aa8..c1014d5d5 100755 --- a/contrib/ci/scripts/build_z3_cmake.sh +++ b/contrib/ci/scripts/build_z3_cmake.sh @@ -78,17 +78,17 @@ else fi # .NET bindings? -#if [ "X${DOTNET_BINDINGS}" = "X1" ]; then -# ADDITIONAL_Z3_OPTS+=( \ -# '-DBUILD_DOTNET_BINDINGS=ON' \ -# '-DINSTALL_DOTNET_BINDINGS=ON' \ -# ) -#else -# ADDITIONAL_Z3_OPTS+=( \ -# '-DBUILD_DOTNET_BINDINGS=OFF' \ -# '-DINSTALL_DOTNET_BINDINGS=OFF' \ -# ) -#fi +if [ "X${DOTNET_BINDINGS}" = "X1" ]; then + ADDITIONAL_Z3_OPTS+=( \ + '-DBUILD_DOTNET_BINDINGS=ON' \ + '-DINSTALL_DOTNET_BINDINGS=ON' \ + ) +else + ADDITIONAL_Z3_OPTS+=( \ + '-DBUILD_DOTNET_BINDINGS=OFF' \ + '-DINSTALL_DOTNET_BINDINGS=OFF' \ + ) +fi # Java bindings? if [ "X${JAVA_BINDINGS}" = "X1" ]; then From 3bf072557ea865a29238b5dde2d49a6f39709028 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 11 Sep 2018 04:14:28 -0700 Subject: [PATCH 035/138] disable branches when arguments are non-integral #1824 Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 13d1ab53e..d079f31ed 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1517,6 +1517,17 @@ public: continue; } + if (!r1.is_int() || !r2.is_int()) { + // std::cout << r1 << " " << r2 << " " << r << " " << expr_ref(n, m) << "\n"; + // TBD + // r1 = 223/4, r2 = 2, r = 219/8 + // take ceil(r1), floor(r1), ceil(r2), floor(r2), for floor(r2) > 0 + // then + // p/q <= ceil(r1)/floor(r2) => n <= div(ceil(r1), floor(r2)) + // p/q >= floor(r1)/ceil(r2) => n >= div(floor(r1), ceil(r2)) + continue; + } + if (a.is_numeral(q, r3)) { SASSERT(r3 == r2 && r2.is_int()); @@ -1542,16 +1553,6 @@ public: continue; } - if (!r1.is_int() || !r2.is_int()) { - // std::cout << r1 << " " << r2 << " " << r << " " << expr_ref(n, m) << "\n"; - // TBD - // r1 = 223/4, r2 = 2, r = 219/8 - // take ceil(r1), floor(r1), ceil(r2), floor(r2), for floor(r2) > 0 - // then - // p/q <= ceil(r1)/floor(r2) => n <= div(ceil(r1), floor(r2)) - // p/q >= floor(r1)/ceil(r2) => n >= div(floor(r1), ceil(r2)) - continue; - } all_divs_valid = false; From 4ffd8603755e0bab901c54405605e35aff1fb0ef Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 11 Sep 2018 11:31:19 -0700 Subject: [PATCH 036/138] narrowing incorrect lemma generation Signed-off-by: Nikolaj Bjorner --- src/smt/smt_context_pp.cpp | 2 ++ src/smt/theory_lra.cpp | 2 +- src/solver/mus.cpp | 11 +++++------ 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/smt/smt_context_pp.cpp b/src/smt/smt_context_pp.cpp index 2a46fd07f..fb67d91d6 100644 --- a/src/smt/smt_context_pp.cpp +++ b/src/smt/smt_context_pp.cpp @@ -426,6 +426,7 @@ namespace smt { std::stringstream strm; strm << "lemma_" << (++m_lemma_id) << ".smt2"; std::ofstream out(strm.str()); + TRACE("lemma", tout << strm.str() << "\n";); display_lemma_as_smt_problem(out, num_antecedents, antecedents, consequent, logic); out.close(); return m_lemma_id; @@ -466,6 +467,7 @@ namespace smt { std::stringstream strm; strm << "lemma_" << (++m_lemma_id) << ".smt2"; std::ofstream out(strm.str()); + TRACE("lemma", tout << strm.str() << "\n";); display_lemma_as_smt_problem(out, num_antecedents, antecedents, num_eq_antecedents, eq_antecedents, consequent, logic); out.close(); return m_lemma_id; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index d079f31ed..a67fead38 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1512,7 +1512,7 @@ public: if (r1.is_int() && r2.is_int() && r == div(r1, r2)) { continue; } - if (r2.is_neg()) { + if (r2.is_neg() || r1.is_neg()) { // TBD continue; } diff --git a/src/solver/mus.cpp b/src/solver/mus.cpp index 094b27ed3..4ae93a52e 100644 --- a/src/solver/mus.cpp +++ b/src/solver/mus.cpp @@ -217,13 +217,12 @@ struct mus::imp { } expr_set mss_set; - for (unsigned i = 0; i < mss.size(); ++i) { - mss_set.insert(mss[i]); + for (expr* e : mss) { + mss_set.insert(e); } - expr_set::iterator it = min_core.begin(), end = min_core.end(); - for (; it != end; ++it) { - if (mss_set.contains(*it) && min_lit != *it) { - unknown.push_back(*it); + for (expr * e : min_core) { + if (mss_set.contains(e) && min_lit != e) { + unknown.push_back(e); } } core_literal = min_lit; From 6ea4aff622fbf4ba35879d403fc3062c322e8dcd Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Sep 2018 10:47:50 -0700 Subject: [PATCH 037/138] add validation code for cuts, fix missing unit propagation Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 186 +++++++++++++++++++++++++++++-------- src/util/lp/lar_solver.cpp | 29 +++--- src/util/lp/lar_solver.h | 30 +++--- 3 files changed, 183 insertions(+), 62 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index a67fead38..604d7fb4b 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -684,7 +684,7 @@ class theory_lra::imp { m_constraint_sources.setx(index, inequality_source, null_source); m_inequalities.setx(index, lit, null_literal); ++m_stats.m_add_rows; - TRACE("arith", m_solver->print_constraint(index, tout); tout << "\n";); + TRACE("arith", m_solver->print_constraint(index, tout) << "\n";); } void add_def_constraint(lp::constraint_index index) { @@ -787,7 +787,7 @@ class theory_lra::imp { } m_var_trail.push_back(v); TRACE("arith_verbose", tout << "v" << v << " := " << mk_pp(term, m) << " slack: " << vi << " scopes: " << m_scopes.size() << "\n"; - m_solver->print_term(m_solver->get_term(vi), tout); tout << "\n";); + m_solver->print_term(m_solver->get_term(vi), tout) << "\n";); } rational val; if (a.is_numeral(term, val)) { @@ -1423,6 +1423,18 @@ public: u_map coeffs; term2coeffs(term, coeffs, rational::one(), offset); offset.neg(); + TRACE("arith", + m_solver->print_term(term, tout << "term: ") << "\n"; + for (auto const& kv : coeffs) { + tout << "v" << kv.m_key << " * " << kv.m_value << "\n"; + } + tout << offset << "\n"; + rational g(0); + for (auto const& kv : coeffs) { + g = gcd(g, kv.m_value); + } + tout << "gcd: " << g << "\n"; + ); if (is_int) { // 3x + 6y >= 5 -> x + 3y >= 5/3, then x + 3y >= 2 // 3x + 6y <= 5 -> x + 3y <= 1 @@ -1430,10 +1442,12 @@ public: rational g = gcd_reduce(coeffs); if (!g.is_one()) { if (lower_bound) { - offset = div(offset + g - rational::one(), g); + TRACE("arith", tout << "lower: " << offset << " / " << g << " = " << offset / g << " >= " << ceil(offset / g) << "\n";); + offset = ceil(offset / g); } else { - offset = div(offset, g); + TRACE("arith", tout << "upper: " << offset << " / " << g << " = " << offset / g << " <= " << floor(offset / g) << "\n";); + offset = floor(offset / g); } } } @@ -1453,7 +1467,7 @@ public: } TRACE("arith", tout << t << ": " << atom << "\n"; - m_solver->print_term(term, tout << "bound atom: "); tout << (lower_bound?" >= ":" <= ") << k << "\n";); + m_solver->print_term(term, tout << "bound atom: ") << (lower_bound?" >= ":" <= ") << k << "\n";); ctx().internalize(atom, true); ctx().mark_as_relevant(atom.get()); return atom; @@ -1531,22 +1545,34 @@ public: if (a.is_numeral(q, r3)) { SASSERT(r3 == r2 && r2.is_int()); - // p <= r1 => n <= div(r1, r2) - // r1 <= p => div(r1, r2) <= n - literal p_le_r1 = mk_literal(a.mk_le(p, a.mk_numeral(ceil(r1), true))); - literal p_ge_r1 = mk_literal(a.mk_ge(p, a.mk_numeral(floor(r1), true))); - literal n_le_div = mk_literal(a.mk_le(n, a.mk_numeral(div(ceil(r1), r2), true))); - literal n_ge_div = mk_literal(a.mk_ge(n, a.mk_numeral(div(floor(r1), r2), true))); + SASSERT(r1.is_int() && r3.is_int()); + rational div_r = div(r1, r2); + // p <= q * div(r1, q) + q - 1 => div(p, q) <= div(r1, r2) + // p >= q * div(r1, q) => div(r1, q) <= div(p, q) + rational mul(1); + rational hi = r2 * div_r + r2 - 1; + rational lo = r2 * div_r; + expr *n1 = nullptr, *n2 = nullptr; + if (a.is_mul(p, n1, n2) && is_numeral(n1, mul) && mul.is_pos()) { + p = n2; + hi = floor(hi/mul); + lo = ceil(lo/mul); + } + literal p_le_r1 = mk_literal(a.mk_le(p, a.mk_numeral(hi, true))); + literal p_ge_r1 = mk_literal(a.mk_ge(p, a.mk_numeral(lo, true))); + literal n_le_div = mk_literal(a.mk_le(n, a.mk_numeral(div_r, true))); + literal n_ge_div = mk_literal(a.mk_ge(n, a.mk_numeral(div_r, true))); mk_axiom(~p_le_r1, n_le_div); mk_axiom(~p_ge_r1, n_ge_div); all_divs_valid = false; TRACE("arith", + tout << r1 << " div " << r2 << " = " << r3 << "\n"; literal_vector lits; lits.push_back(~p_le_r1); lits.push_back(n_le_div); - ctx().display_literals_verbose(tout, lits) << "\n"; + ctx().display_literals_verbose(tout, lits) << "\n\n"; lits[0] = ~p_ge_r1; lits[1] = n_ge_div; ctx().display_literals_verbose(tout, lits) << "\n";); @@ -1589,6 +1615,90 @@ public: return all_divs_valid; } + expr_ref var2expr(lp::var_index v) { + std::ostringstream name; + name << "v" << m_solver->local2external(v); + return expr_ref(m.mk_const(symbol(name.str().c_str()), a.mk_int()), m); + } + + expr_ref multerm(rational const& r, expr* e) { + if (r.is_one()) return expr_ref(e, m); + return expr_ref(a.mk_mul(a.mk_numeral(r, true), e), m); + } + + expr_ref term2expr(lp::lar_term const& term) { + expr_ref t(m); + expr_ref_vector ts(m); + for (auto const& p : term) { + lp::var_index wi = p.var(); + if (m_solver->is_term(wi)) { + ts.push_back(multerm(p.coeff(), term2expr(m_solver->get_term(wi)))); + } + else { + ts.push_back(multerm(p.coeff(), var2expr(wi))); + } + } + if (ts.size() == 1) { + t = ts.back(); + } + else { + t = a.mk_add(ts.size(), ts.c_ptr()); + } + return t; + } + + expr_ref constraint2fml(lp::constraint_index ci) { + lp::lar_base_constraint const& c = *m_solver->constraints()[ci]; + expr_ref fml(m); + expr_ref_vector ts(m); + rational rhs = c.m_right_side; + for (auto cv : c.get_left_side_coefficients()) { + ts.push_back(multerm(cv.first, var2expr(cv.second))); + } + switch (c.m_kind) { + case lp::LE: fml = a.mk_le(a.mk_add(ts.size(), ts.c_ptr()), a.mk_numeral(rhs, true)); break; + case lp::LT: fml = a.mk_lt(a.mk_add(ts.size(), ts.c_ptr()), a.mk_numeral(rhs, true)); break; + case lp::GE: fml = a.mk_ge(a.mk_add(ts.size(), ts.c_ptr()), a.mk_numeral(rhs, true)); break; + case lp::GT: fml = a.mk_gt(a.mk_add(ts.size(), ts.c_ptr()), a.mk_numeral(rhs, true)); break; + case lp::EQ: fml = m.mk_eq(a.mk_add(ts.size(), ts.c_ptr()), a.mk_numeral(rhs, true)); break; + } + return fml; + } + + void dump_cut_lemma(std::ostream& out, lp::lar_term const& term, lp::mpq const& k, lp::explanation const& ex, bool upper) { + m_solver->print_term(term, out << "bound: "); + out << (upper?" <= ":" >= ") << k << "\n"; + for (auto const& p : term) { + lp::var_index wi = p.var(); + out << p.coeff() << " * "; + if (m_solver->is_term(wi)) { + m_solver->print_term(m_solver->get_term(wi), out) << "\n"; + } + else { + out << "v" << m_solver->local2external(wi) << "\n"; + } + } + for (auto const& ev : ex.m_explanation) { + m_solver->print_constraint(ev.second, out << ev.first << ": "); + } + expr_ref_vector fmls(m); + for (auto const& ev : ex.m_explanation) { + fmls.push_back(constraint2fml(ev.second)); + } + expr_ref t(m); + t = term2expr(term); + if (upper) + fmls.push_back(m.mk_not(a.mk_ge(t, a.mk_numeral(k, true)))); + else + fmls.push_back(m.mk_not(a.mk_le(t, a.mk_numeral(k, true)))); + ast_pp_util visitor(m); + visitor.collect(fmls); + + visitor.display_decls(out); + visitor.display_asserts(out, fmls, true); + out << "(check-sat)\n"; + } + lbool check_lia() { if (m.canceled()) { TRACE("arith", tout << "canceled\n";); @@ -1602,6 +1712,7 @@ public: lp::mpq k; lp::explanation ex; // TBD, this should be streamlined accross different explanations bool upper; + m_explanation.reset(); switch(m_lia->check(term, k, ex, upper)) { case lp::lia_move::sat: return l_true; @@ -1621,7 +1732,8 @@ public: ++m_stats.m_gomory_cuts; // m_explanation implies term <= k app_ref b = mk_bound(term, k, !upper); - IF_VERBOSE(2, verbose_stream() << "cut " << b << "\n";); + IF_VERBOSE(2, verbose_stream() << "cut " << b << "\n"); + TRACE("arith", dump_cut_lemma(tout, term, k, ex, upper);); m_eqs.reset(); m_core.reset(); m_params.reset(); @@ -1638,6 +1750,7 @@ public: return l_false; } case lp::lia_move::conflict: + TRACE("arith", tout << "conflict\n";); // ex contains unsat core m_explanation = ex.m_explanation; set_conflict1(); @@ -2243,18 +2356,18 @@ public: SASSERT(!bounds.empty()); if (bounds.size() == 1) return; if (m_unassigned_bounds[v] == 0) return; - + bool v_is_int = is_int(v); literal lit1(bv, !is_true); literal lit2 = null_literal; bool find_glb = (is_true == (k == lp_api::lower_t)); + TRACE("arith", tout << "find_glb: " << find_glb << " is_true: " << is_true << " k: " << k << " is_lower: " << (k == lp_api::lower_t) << "\n";); if (find_glb) { rational glb; - lp_api::bound* lb = 0; - for (unsigned i = 0; i < bounds.size(); ++i) { - lp_api::bound* b2 = bounds[i]; + lp_api::bound* lb = nullptr; + for (lp_api::bound* b2 : bounds) { if (b2 == &b) continue; rational const& val2 = b2->get_value(); - if ((is_true ? val2 < val : val2 <= val) && (!lb || glb < val2)) { + if (((is_true || v_is_int) ? val2 < val : val2 <= val) && (!lb || glb < val2)) { lb = b2; glb = val2; } @@ -2265,12 +2378,11 @@ public: } else { rational lub; - lp_api::bound* ub = 0; - for (unsigned i = 0; i < bounds.size(); ++i) { - lp_api::bound* b2 = bounds[i]; + lp_api::bound* ub = nullptr; + for (lp_api::bound* b2 : bounds) { if (b2 == &b) continue; rational const& val2 = b2->get_value(); - if ((is_true ? val < val2 : val <= val2) && (!ub || val2 < lub)) { + if (((is_true || v_is_int) ? val < val2 : val <= val2) && (!ub || val2 < lub)) { ub = b2; lub = val2; } @@ -2288,7 +2400,7 @@ public: m_params.reset(); m_core.reset(); m_eqs.reset(); - m_core.push_back(lit2); + m_core.push_back(lit1); m_params.push_back(parameter(symbol("farkas"))); m_params.push_back(parameter(rational(1))); m_params.push_back(parameter(rational(1))); @@ -2799,7 +2911,7 @@ public: m_todo_terms.push_back(std::make_pair(vi, rational::one())); TRACE("arith", tout << "v" << v << " := w" << vi << "\n"; - m_solver->print_term(m_solver->get_term(vi), tout); tout << "\n";); + m_solver->print_term(m_solver->get_term(vi), tout) << "\n";); m_nra->am().set(r, 0); while (!m_todo_terms.empty()) { @@ -2807,13 +2919,13 @@ public: vi = m_todo_terms.back().first; m_todo_terms.pop_back(); lp::lar_term const& term = m_solver->get_term(vi); - TRACE("arith", m_solver->print_term(term, tout); tout << "\n";); + TRACE("arith", m_solver->print_term(term, tout) << "\n";); scoped_anum r1(m_nra->am()); rational c1 = term.m_v * wcoeff; m_nra->am().set(r1, c1.to_mpq()); m_nra->am().add(r, r1, r); for (auto const & arg : term) { - lp::var_index wi = m_solver->local2external(arg.var()); + lp::var_index wi = arg.var(); c1 = arg.coeff() * wcoeff; if (m_solver->is_term(wi)) { m_todo_terms.push_back(std::make_pair(wi, c1)); @@ -3108,17 +3220,17 @@ public: rational gcd_reduce(u_map& coeffs) { rational g(0); - for (auto const& kv : coeffs) { - g = gcd(g, kv.m_value); - } - if (g.is_zero()) - return rational::one(); - if (!g.is_one()) { - for (auto& kv : coeffs) { - kv.m_value /= g; - } - } - return g; + for (auto const& kv : coeffs) { + g = gcd(g, kv.m_value); + } + if (g.is_zero()) + return rational::one(); + if (!g.is_one()) { + for (auto& kv : coeffs) { + kv.m_value /= g; + } + } + return g; } app_ref mk_obj(theory_var v) { diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp index d4db0bc91..1340d1826 100644 --- a/src/util/lp/lar_solver.cpp +++ b/src/util/lp/lar_solver.cpp @@ -71,7 +71,7 @@ bool lar_solver::sizes_are_correct() const { } -void lar_solver::print_implied_bound(const implied_bound& be, std::ostream & out) const { +std::ostream& lar_solver::print_implied_bound(const implied_bound& be, std::ostream & out) const { out << "implied bound\n"; unsigned v = be.m_j; if (is_term(v)) { @@ -83,6 +83,7 @@ void lar_solver::print_implied_bound(const implied_bound& be, std::ostream & out } out << " " << lconstraint_kind_string(be.kind()) << " " << be.m_bound << std::endl; out << "end of implied bound" << std::endl; + return out; } bool lar_solver::implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const { @@ -1208,39 +1209,41 @@ std::string lar_solver::get_variable_name(var_index vi) const { } // ********** print region start -void lar_solver::print_constraint(constraint_index ci, std::ostream & out) const { +std::ostream& lar_solver::print_constraint(constraint_index ci, std::ostream & out) const { if (ci >= m_constraints.size()) { out << "constraint " << T_to_string(ci) << " is not found"; out << std::endl; - return; + return out; } - print_constraint(m_constraints[ci], out); + return print_constraint(m_constraints[ci], out); } -void lar_solver::print_constraints(std::ostream& out) const { +std::ostream& lar_solver::print_constraints(std::ostream& out) const { out << "number of constraints = " << m_constraints.size() << std::endl; for (auto c : m_constraints) { print_constraint(c, out); } + return out; } -void lar_solver::print_terms(std::ostream& out) const { +std::ostream& lar_solver::print_terms(std::ostream& out) const { for (auto it : m_terms) { print_term(*it, out); out << "\n"; } + return out; } -void lar_solver::print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const { +std::ostream& lar_solver::print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const { print_linear_combination_of_column_indices(c->get_left_side_coefficients(), out); mpq free_coeff = c->get_free_coeff_of_left_side(); if (!is_zero(free_coeff)) out << " + " << free_coeff; - + return out; } -void lar_solver::print_term(lar_term const& term, std::ostream & out) const { +std::ostream& lar_solver::print_term(lar_term const& term, std::ostream & out) const { if (!numeric_traits::is_zero(term.m_v)) { out << term.m_v << " + "; } @@ -1263,14 +1266,15 @@ void lar_solver::print_term(lar_term const& term, std::ostream & out) const { out << T_to_string(val); out << this->get_column_name(p.var()); } - + return out; } -void lar_solver::print_term_as_indices(lar_term const& term, std::ostream & out) const { +std::ostream& lar_solver::print_term_as_indices(lar_term const& term, std::ostream & out) const { if (!numeric_traits::is_zero(term.m_v)) { out << term.m_v << " + "; } print_linear_combination_of_column_indices_only(term.coeffs_as_vector(), out); + return out; } mpq lar_solver::get_left_side_val(const lar_base_constraint & cns, const std::unordered_map & var_map) const { @@ -1284,9 +1288,10 @@ mpq lar_solver::get_left_side_val(const lar_base_constraint & cns, const std::u return ret; } -void lar_solver::print_constraint(const lar_base_constraint * c, std::ostream & out) const { +std::ostream& lar_solver::print_constraint(const lar_base_constraint * c, std::ostream & out) const { print_left_side_of_constraint(c, out); out << " " << lconstraint_kind_string(c->m_kind) << " " << c->m_right_side << std::endl; + return out; } void lar_solver::fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector& column_list) { diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index 283c13c38..9afb70c72 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -208,7 +208,10 @@ public: void update_lower_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); void update_fixed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci); + //end of init region + + lp_settings & settings(); lp_settings const & settings() const; @@ -227,9 +230,7 @@ public: bool use_lu() const; bool sizes_are_correct() const; - - void print_implied_bound(const implied_bound& be, std::ostream & out) const; - + bool implied_bound_is_correctly_explained(implied_bound const & be, const vector> & explanation) const; void analyze_new_bounds_on_row( @@ -436,30 +437,33 @@ public: int inf_sign) const; - void get_model(std::unordered_map & variable_values) const; void get_model_do_not_care_about_diff_vars(std::unordered_map & variable_values) const; std::string get_variable_name(var_index vi) const; - // ********** print region start - void print_constraint(constraint_index ci, std::ostream & out) const; + // print utilities - void print_constraints(std::ostream& out) const ; + std::ostream& print_constraint(constraint_index ci, std::ostream & out) const; - void print_terms(std::ostream& out) const; + std::ostream& print_constraints(std::ostream& out) const ; - void print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const; + std::ostream& print_terms(std::ostream& out) const; - void print_term(lar_term const& term, std::ostream & out) const; + std::ostream& print_left_side_of_constraint(const lar_base_constraint * c, std::ostream & out) const; - void print_term_as_indices(lar_term const& term, std::ostream & out) const; + std::ostream& print_term(lar_term const& term, std::ostream & out) const; + std::ostream& print_term_as_indices(lar_term const& term, std::ostream & out) const; + + std::ostream& print_constraint(const lar_base_constraint * c, std::ostream & out) const; + + std::ostream& print_implied_bound(const implied_bound& be, std::ostream & out) const; + + mpq get_left_side_val(const lar_base_constraint & cns, const std::unordered_map & var_map) const; - void print_constraint(const lar_base_constraint * c, std::ostream & out) const; - void fill_var_set_for_random_update(unsigned sz, var_index const * vars, vector& column_list); void random_update(unsigned sz, var_index const * vars); From 2a8d207bf454d32e1892658f5c039704e6c1fa7e Mon Sep 17 00:00:00 2001 From: Daniel Selsam Date: Thu, 13 Sep 2018 14:31:52 -0700 Subject: [PATCH 038/138] remove duplicate method definitions --- src/api/python/z3/z3.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index e0769c1fd..73339d478 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -6626,14 +6626,6 @@ class Solver(Z3PPObject): def proof(self): """Return a proof for the last `check()`. Proof construction must be enabled.""" return _to_expr_ref(Z3_solver_get_proof(self.ctx.ref(), self.solver), self.ctx) - - def from_file(self, filename): - """Parse assertions from a file""" - Z3_solver_from_file(self.ctx.ref(), self.solver, filename) - - def from_string(self, s): - """Parse assertions from a string""" - Z3_solver_from_string(self.ctx.ref(), self.solver, s) def assertions(self): """Return an AST vector containing all added constraints. From 78950fde17f76bbf22ecaa6b4521b3d7e7c45e29 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 13 Sep 2018 19:05:45 -0700 Subject: [PATCH 039/138] initialize solver before parse is invoked. Fixes issue reported by Selsam Signed-off-by: Nikolaj Bjorner --- src/api/api_solver.cpp | 1 + src/smt/theory_lra.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 9ad51aaf4..fc42acbb9 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -179,6 +179,7 @@ extern "C" { LOG_Z3_solver_from_file(c, s, file_name); char const* ext = get_extension(file_name); std::ifstream is(file_name); + init_solver(c, s); if (!is) { SET_ERROR_CODE(Z3_FILE_ACCESS_ERROR, nullptr); } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 604d7fb4b..f8e2f9fe1 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1685,15 +1685,15 @@ public: for (auto const& ev : ex.m_explanation) { fmls.push_back(constraint2fml(ev.second)); } - expr_ref t(m); - t = term2expr(term); - if (upper) + expr_ref t(term2expr(term), m); + if (upper) { fmls.push_back(m.mk_not(a.mk_ge(t, a.mk_numeral(k, true)))); - else + } + else { fmls.push_back(m.mk_not(a.mk_le(t, a.mk_numeral(k, true)))); + } ast_pp_util visitor(m); visitor.collect(fmls); - visitor.display_decls(out); visitor.display_asserts(out, fmls, true); out << "(check-sat)\n"; From e705e5a3094a0ffde405d32afaae9d26492fe6fe Mon Sep 17 00:00:00 2001 From: Lev Date: Thu, 13 Sep 2018 11:38:22 -0700 Subject: [PATCH 040/138] branch on inf basic in gomory Signed-off-by: Lev --- src/util/lp/gomory.h | 46 ++++++++++++++++++++++++++++++++++++++ src/util/lp/int_solver.cpp | 17 +++----------- src/util/lp/int_solver.h | 1 - 3 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 src/util/lp/gomory.h diff --git a/src/util/lp/gomory.h b/src/util/lp/gomory.h new file mode 100644 index 000000000..87879f9f1 --- /dev/null +++ b/src/util/lp/gomory.h @@ -0,0 +1,46 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +#include "util/lp/lar_term.h" +#include "util/lp/lia_move.h" +#include "util/lp/explanation.h" + +namespace lp { +class gomory { + lar_term & m_t; // the term to return in the cut + mpq & m_k; // the right side of the cut + explanation& m_ex; // the conflict explanation + bool & m_upper; // we have a cut m_t*x <= k if m_upper is true nad m_t*x >= k otherwise + unsigned m_basic_inf_int_j; // a basis column which has to be an integer but has a not integral value + const row_strip& m_row +public : + gomory(lar_term & m_t, + mpq & m_k, + explanation& m_ex, + bool & m_upper, + unsigned m_basic_inf_int_j ) : + m_t(t), + m_k(k), + m_ex(ex), + m_upper(upper), + m_basic_inf_int_j(j) { + } +}; +} diff --git a/src/util/lp/int_solver.cpp b/src/util/lp/int_solver.cpp index a77c202a0..c416c63f3 100644 --- a/src/util/lp/int_solver.cpp +++ b/src/util/lp/int_solver.cpp @@ -102,6 +102,8 @@ bool int_solver::is_gomory_cut_target(const row_strip& row) { for (const auto & p : row) { j = p.var(); if (is_base(j)) continue; + if (is_free(j)) + return false; if (!at_bound(j)) return false; if (!is_zero(get_value(j).y)) { @@ -350,24 +352,11 @@ lia_move int_solver::mk_gomory_cut( unsigned inf_col, const row_strip & row return lia_move::cut; } -int int_solver::find_free_var_in_gomory_row(const row_strip& row) { - unsigned j; - for (const auto & p : row) { - j = p.var(); - if (!is_base(j) && is_free(j)) - return static_cast(j); - } - return -1; -} - lia_move int_solver::proceed_with_gomory_cut(unsigned j) { const row_strip& row = m_lar_solver->get_row(row_of_basic_column(j)); - if (-1 != find_free_var_in_gomory_row(row)) - return lia_move::undef; - if (!is_gomory_cut_target(row)) - return lia_move::undef; + return create_branch_on_column(j); *m_upper = true; return mk_gomory_cut(j, row); diff --git a/src/util/lp/int_solver.h b/src/util/lp/int_solver.h index ec708918d..82fcb6eb4 100644 --- a/src/util/lp/int_solver.h +++ b/src/util/lp/int_solver.h @@ -107,7 +107,6 @@ private: lia_move report_conflict_from_gomory_cut(); void adjust_term_and_k_for_some_ints_case_gomory(mpq& lcm_den); lia_move proceed_with_gomory_cut(unsigned j); - int find_free_var_in_gomory_row(const row_strip& ); bool is_gomory_cut_target(const row_strip&); bool at_bound(unsigned j) const; bool at_low(unsigned j) const; From 5dee39721a73d60c67395a78451aecaf481be2f0 Mon Sep 17 00:00:00 2001 From: Lev Date: Fri, 14 Sep 2018 11:52:14 -0700 Subject: [PATCH 041/138] rebase Signed-off-by: Lev --- src/util/lp/bound_propagator.cpp | 4 ---- src/util/lp/lar_constraints.h | 2 +- src/util/lp/lar_solver.cpp | 25 ++++++++++--------------- src/util/lp/lar_solver.h | 8 +++----- src/util/lp/lar_term.h | 9 +++------ 5 files changed, 17 insertions(+), 31 deletions(-) diff --git a/src/util/lp/bound_propagator.cpp b/src/util/lp/bound_propagator.cpp index c4fa2aefa..a5c7c976a 100644 --- a/src/util/lp/bound_propagator.cpp +++ b/src/util/lp/bound_propagator.cpp @@ -17,10 +17,6 @@ const impq & bound_propagator::get_upper_bound(unsigned j) const { } void bound_propagator::try_add_bound(mpq v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) { j = m_lar_solver.adjust_column_index_to_term_index(j); - if (m_lar_solver.is_term(j)) { - // lp treats terms as not having a free coefficient, restoring it below for the outside consumption - v += m_lar_solver.get_term(j).m_v; - } lconstraint_kind kind = is_low? GE : LE; if (strict) diff --git a/src/util/lp/lar_constraints.h b/src/util/lp/lar_constraints.h index ac15028bb..e06c6d1c7 100644 --- a/src/util/lp/lar_constraints.h +++ b/src/util/lp/lar_constraints.h @@ -75,7 +75,7 @@ struct lar_term_constraint: public lar_base_constraint { } unsigned size() const override { return m_term->size();} lar_term_constraint(const lar_term *t, lconstraint_kind kind, const mpq& right_side) : lar_base_constraint(kind, right_side), m_term(t) { } - mpq get_free_coeff_of_left_side() const override { return m_term->m_v;} + mpq get_free_coeff_of_left_side() const override { return zero_of_type();} }; diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp index 1340d1826..bc4c17d65 100644 --- a/src/util/lp/lar_solver.cpp +++ b/src/util/lp/lar_solver.cpp @@ -137,7 +137,6 @@ bool lar_solver::implied_bound_is_correctly_explained(implied_bound const & be, kind = static_cast(-kind); } rs_of_evidence /= ratio; - rs_of_evidence += t->m_v * ratio; } return kind == be.kind() && rs_of_evidence == be.m_bound; @@ -613,7 +612,6 @@ void lar_solver::substitute_terms_in_linear_expression(const vector(); for (auto const& cv : t) { impq const& r = get_column_value(cv.var()); if (!numeric_traits::is_zero(r.y)) return false; @@ -1497,7 +1495,7 @@ bool lar_solver::term_is_int(const lar_term * t) const { for (auto const & p : t->m_coeffs) if (! (column_is_int(p.first) && p.second.is_int())) return false; - return t->m_v.is_int(); + return true; } bool lar_solver::var_is_int(var_index v) const { @@ -1598,9 +1596,8 @@ void lar_solver::add_new_var_to_core_fields_for_mpq(bool register_in_basis) { } -var_index lar_solver::add_term_undecided(const vector> & coeffs, - const mpq &m_v) { - push_and_register_term(new lar_term(coeffs, m_v)); +var_index lar_solver::add_term_undecided(const vector> & coeffs) { + push_and_register_term(new lar_term(coeffs)); return m_terms_start_index + m_terms.size() - 1; } @@ -1643,12 +1640,11 @@ void lar_solver::push_and_register_term(lar_term* t) { } // terms -var_index lar_solver::add_term(const vector> & coeffs, - const mpq &m_v) { +var_index lar_solver::add_term(const vector> & coeffs) { if (strategy_is_undecided()) - return add_term_undecided(coeffs, m_v); + return add_term_undecided(coeffs); - push_and_register_term(new lar_term(coeffs, m_v)); + push_and_register_term(new lar_term(coeffs)); unsigned adjusted_term_index = m_terms.size() - 1; var_index ret = m_terms_start_index + adjusted_term_index; if (use_tableau() && !coeffs.empty()) { @@ -1743,9 +1739,8 @@ void lar_solver::add_var_bound_on_constraint_for_term(var_index j, lconstraint_k // lp_assert(!term_is_int(m_terms[adjusted_term_index]) || right_side.is_int()); unsigned term_j; if (m_var_register.external_is_used(j, term_j)) { - mpq rs = right_side - m_terms[adjusted_term_index]->m_v; m_constraints.push_back(new lar_term_constraint(m_terms[adjusted_term_index], kind, right_side)); - update_column_type_and_bound(term_j, kind, rs, ci); + update_column_type_and_bound(term_j, kind, right_side, ci); } else { add_constraint_from_term_and_create_new_column_row(j, m_terms[adjusted_term_index], kind, right_side); @@ -1756,7 +1751,7 @@ constraint_index lar_solver::add_constraint(const vector> left_side; mpq rs = -right_side_parm; substitute_terms_in_linear_expression(left_side_with_terms, left_side, rs); - unsigned term_index = add_term(left_side, zero_of_type()); + unsigned term_index = add_term(left_side); constraint_index ci = m_constraints.size(); add_var_bound_on_constraint_for_term(term_index, kind_par, -rs, ci); return ci; @@ -1767,7 +1762,7 @@ void lar_solver::add_constraint_from_term_and_create_new_column_row(unsigned ter add_row_from_term_no_constraint(term, term_j); unsigned j = A_r().column_count() - 1; - update_column_type_and_bound(j, kind, right_side - term->m_v, m_constraints.size()); + update_column_type_and_bound(j, kind, right_side, m_constraints.size()); m_constraints.push_back(new lar_term_constraint(term, kind, right_side)); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); } diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index 9afb70c72..2e85513ff 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -164,13 +164,11 @@ public: // terms - var_index add_term(const vector> & coeffs, - const mpq &m_v); + var_index add_term(const vector> & coeffs); - var_index add_term_undecided(const vector> & coeffs, - const mpq &m_v); + var_index add_term_undecided(const vector> & coeffs); - bool term_coeffs_are_ok(const vector> & coeffs, const mpq& v); + bool term_coeffs_are_ok(const vector> & coeffs); void push_and_register_term(lar_term* t); void add_row_for_term(const lar_term * term, unsigned term_ext_index); diff --git a/src/util/lp/lar_term.h b/src/util/lp/lar_term.h index 519847848..47861baf8 100644 --- a/src/util/lp/lar_term.h +++ b/src/util/lp/lar_term.h @@ -23,7 +23,6 @@ namespace lp { struct lar_term { // the term evaluates to sum of m_coeffs + m_v std::unordered_map m_coeffs; - mpq m_v; lar_term() {} void add_monomial(const mpq& c, unsigned j) { auto it = m_coeffs.find(j); @@ -37,7 +36,7 @@ struct lar_term { } bool is_empty() const { - return m_coeffs.size() == 0 && is_zero(m_v); + return m_coeffs.size() == 0; } unsigned size() const { return static_cast(m_coeffs.size()); } @@ -46,8 +45,7 @@ struct lar_term { return m_coeffs; } - lar_term(const vector>& coeffs, - const mpq & v) : m_v(v) { + lar_term(const vector>& coeffs) { for (const auto & p : coeffs) { add_monomial(p.first, p.second); } @@ -87,7 +85,7 @@ struct lar_term { template T apply(const vector& x) const { - T ret = T(m_v); + T ret = zero_of_type(); for (const auto & t : m_coeffs) { ret += t.second * x[t.first]; } @@ -96,7 +94,6 @@ struct lar_term { void clear() { m_coeffs.clear(); - m_v = zero_of_type(); } struct ival { From 57357b7ecebec0d6065658210f1bf4c5a573c6e2 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Thu, 13 Sep 2018 17:39:06 -0700 Subject: [PATCH 042/138] does not compile Signed-off-by: Lev Nachmanson --- src/smt/theory_lra.cpp | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index f8e2f9fe1..a5190459f 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -54,6 +54,17 @@ std::ostream& operator<<(std::ostream& out, bound_kind const& k) { return out; } +struct term_info { + rational m_offset; + lp::var_index m_var; + + rational const& offset() const { return m_offset; } + lp::var_index var() const { return m_var; } + + term_info(): m_var(-1) {} + term_info(lp:var_index vi, rational const& offset): m_offset(offset), m_var(vi) {} +}; + class bound { smt::bool_var m_bv; smt::theory_var m_var; @@ -777,8 +788,8 @@ class theory_lra::imp { lp::var_index vi = m_theory_var2var_index.get(v, UINT_MAX); TRACE("arith", tout << mk_pp(term, m) << " " << v << " " << vi << "\n";); if (vi == UINT_MAX) { - vi = m_solver->add_term(m_left_side, st.coeff()); - m_theory_var2var_index.setx(v, vi, UINT_MAX); + vi = m_solver->add_term(m_left_side); + m_theory_var2var_index.setx(v, term_info(vi, st.coeff()), term_info(0, rational::zero())); if (m_solver->is_term(vi)) { m_term_index2theory_var.setx(m_solver->adjust_term_index(vi), v, UINT_MAX); } @@ -1187,17 +1198,19 @@ public: lp::impq get_ivalue(theory_var v) const { SASSERT(can_get_ivalue(v)); lp::var_index vi = m_theory_var2var_index[v]; - if (!m_solver->is_term(vi)) - return m_solver->get_column_value(vi); - m_todo_terms.push_back(std::make_pair(vi, rational::one())); - lp::impq result(0); + term_info ti = m_theory_var2var_index[v]; + if (!m_solver->is_term(ti.var())) + return m_solver->get_column_value(ti.var()) + ti.offset(); + m_todo_terms.push_back(std::make_pair(ti, rational::one())); + lp::impq result(ti); while (!m_todo_terms.empty()) { - vi = m_todo_terms.back().first; + ti = m_todo_terms.back().first; + vi = ti.var(); rational coeff = m_todo_terms.back().second; m_todo_terms.pop_back(); + result += ti.offset() * coeff; if (m_solver->is_term(vi)) { const lp::lar_term& term = m_solver->get_term(vi); - result += term.m_v * coeff; for (const auto & i: term) { m_todo_terms.push_back(std::make_pair(i.var(), coeff * i.coeff())); } From 22213a9e73567983fa058c2c0a24b45ba74c932b Mon Sep 17 00:00:00 2001 From: Lev Date: Fri, 14 Sep 2018 11:53:54 -0700 Subject: [PATCH 043/138] rebase Signed-off-by: Lev --- src/smt/theory_lra.cpp | 29 ++++++++--------------------- src/util/lp/bound_propagator.cpp | 4 ++++ src/util/lp/lar_constraints.h | 2 +- src/util/lp/lar_solver.cpp | 25 +++++++++++++++---------- src/util/lp/lar_solver.h | 8 +++++--- src/util/lp/lar_term.h | 9 ++++++--- 6 files changed, 39 insertions(+), 38 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index a5190459f..f8e2f9fe1 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -54,17 +54,6 @@ std::ostream& operator<<(std::ostream& out, bound_kind const& k) { return out; } -struct term_info { - rational m_offset; - lp::var_index m_var; - - rational const& offset() const { return m_offset; } - lp::var_index var() const { return m_var; } - - term_info(): m_var(-1) {} - term_info(lp:var_index vi, rational const& offset): m_offset(offset), m_var(vi) {} -}; - class bound { smt::bool_var m_bv; smt::theory_var m_var; @@ -788,8 +777,8 @@ class theory_lra::imp { lp::var_index vi = m_theory_var2var_index.get(v, UINT_MAX); TRACE("arith", tout << mk_pp(term, m) << " " << v << " " << vi << "\n";); if (vi == UINT_MAX) { - vi = m_solver->add_term(m_left_side); - m_theory_var2var_index.setx(v, term_info(vi, st.coeff()), term_info(0, rational::zero())); + vi = m_solver->add_term(m_left_side, st.coeff()); + m_theory_var2var_index.setx(v, vi, UINT_MAX); if (m_solver->is_term(vi)) { m_term_index2theory_var.setx(m_solver->adjust_term_index(vi), v, UINT_MAX); } @@ -1198,19 +1187,17 @@ public: lp::impq get_ivalue(theory_var v) const { SASSERT(can_get_ivalue(v)); lp::var_index vi = m_theory_var2var_index[v]; - term_info ti = m_theory_var2var_index[v]; - if (!m_solver->is_term(ti.var())) - return m_solver->get_column_value(ti.var()) + ti.offset(); - m_todo_terms.push_back(std::make_pair(ti, rational::one())); - lp::impq result(ti); + if (!m_solver->is_term(vi)) + return m_solver->get_column_value(vi); + m_todo_terms.push_back(std::make_pair(vi, rational::one())); + lp::impq result(0); while (!m_todo_terms.empty()) { - ti = m_todo_terms.back().first; - vi = ti.var(); + vi = m_todo_terms.back().first; rational coeff = m_todo_terms.back().second; m_todo_terms.pop_back(); - result += ti.offset() * coeff; if (m_solver->is_term(vi)) { const lp::lar_term& term = m_solver->get_term(vi); + result += term.m_v * coeff; for (const auto & i: term) { m_todo_terms.push_back(std::make_pair(i.var(), coeff * i.coeff())); } diff --git a/src/util/lp/bound_propagator.cpp b/src/util/lp/bound_propagator.cpp index a5c7c976a..c4fa2aefa 100644 --- a/src/util/lp/bound_propagator.cpp +++ b/src/util/lp/bound_propagator.cpp @@ -17,6 +17,10 @@ const impq & bound_propagator::get_upper_bound(unsigned j) const { } void bound_propagator::try_add_bound(mpq v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) { j = m_lar_solver.adjust_column_index_to_term_index(j); + if (m_lar_solver.is_term(j)) { + // lp treats terms as not having a free coefficient, restoring it below for the outside consumption + v += m_lar_solver.get_term(j).m_v; + } lconstraint_kind kind = is_low? GE : LE; if (strict) diff --git a/src/util/lp/lar_constraints.h b/src/util/lp/lar_constraints.h index e06c6d1c7..ac15028bb 100644 --- a/src/util/lp/lar_constraints.h +++ b/src/util/lp/lar_constraints.h @@ -75,7 +75,7 @@ struct lar_term_constraint: public lar_base_constraint { } unsigned size() const override { return m_term->size();} lar_term_constraint(const lar_term *t, lconstraint_kind kind, const mpq& right_side) : lar_base_constraint(kind, right_side), m_term(t) { } - mpq get_free_coeff_of_left_side() const override { return zero_of_type();} + mpq get_free_coeff_of_left_side() const override { return m_term->m_v;} }; diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp index bc4c17d65..1340d1826 100644 --- a/src/util/lp/lar_solver.cpp +++ b/src/util/lp/lar_solver.cpp @@ -137,6 +137,7 @@ bool lar_solver::implied_bound_is_correctly_explained(implied_bound const & be, kind = static_cast(-kind); } rs_of_evidence /= ratio; + rs_of_evidence += t->m_v * ratio; } return kind == be.kind() && rs_of_evidence == be.m_bound; @@ -612,6 +613,7 @@ void lar_solver::substitute_terms_in_linear_expression(const vector(); + value = t.m_v; for (auto const& cv : t) { impq const& r = get_column_value(cv.var()); if (!numeric_traits::is_zero(r.y)) return false; @@ -1495,7 +1497,7 @@ bool lar_solver::term_is_int(const lar_term * t) const { for (auto const & p : t->m_coeffs) if (! (column_is_int(p.first) && p.second.is_int())) return false; - return true; + return t->m_v.is_int(); } bool lar_solver::var_is_int(var_index v) const { @@ -1596,8 +1598,9 @@ void lar_solver::add_new_var_to_core_fields_for_mpq(bool register_in_basis) { } -var_index lar_solver::add_term_undecided(const vector> & coeffs) { - push_and_register_term(new lar_term(coeffs)); +var_index lar_solver::add_term_undecided(const vector> & coeffs, + const mpq &m_v) { + push_and_register_term(new lar_term(coeffs, m_v)); return m_terms_start_index + m_terms.size() - 1; } @@ -1640,11 +1643,12 @@ void lar_solver::push_and_register_term(lar_term* t) { } // terms -var_index lar_solver::add_term(const vector> & coeffs) { +var_index lar_solver::add_term(const vector> & coeffs, + const mpq &m_v) { if (strategy_is_undecided()) - return add_term_undecided(coeffs); + return add_term_undecided(coeffs, m_v); - push_and_register_term(new lar_term(coeffs)); + push_and_register_term(new lar_term(coeffs, m_v)); unsigned adjusted_term_index = m_terms.size() - 1; var_index ret = m_terms_start_index + adjusted_term_index; if (use_tableau() && !coeffs.empty()) { @@ -1739,8 +1743,9 @@ void lar_solver::add_var_bound_on_constraint_for_term(var_index j, lconstraint_k // lp_assert(!term_is_int(m_terms[adjusted_term_index]) || right_side.is_int()); unsigned term_j; if (m_var_register.external_is_used(j, term_j)) { + mpq rs = right_side - m_terms[adjusted_term_index]->m_v; m_constraints.push_back(new lar_term_constraint(m_terms[adjusted_term_index], kind, right_side)); - update_column_type_and_bound(term_j, kind, right_side, ci); + update_column_type_and_bound(term_j, kind, rs, ci); } else { add_constraint_from_term_and_create_new_column_row(j, m_terms[adjusted_term_index], kind, right_side); @@ -1751,7 +1756,7 @@ constraint_index lar_solver::add_constraint(const vector> left_side; mpq rs = -right_side_parm; substitute_terms_in_linear_expression(left_side_with_terms, left_side, rs); - unsigned term_index = add_term(left_side); + unsigned term_index = add_term(left_side, zero_of_type()); constraint_index ci = m_constraints.size(); add_var_bound_on_constraint_for_term(term_index, kind_par, -rs, ci); return ci; @@ -1762,7 +1767,7 @@ void lar_solver::add_constraint_from_term_and_create_new_column_row(unsigned ter add_row_from_term_no_constraint(term, term_j); unsigned j = A_r().column_count() - 1; - update_column_type_and_bound(j, kind, right_side, m_constraints.size()); + update_column_type_and_bound(j, kind, right_side - term->m_v, m_constraints.size()); m_constraints.push_back(new lar_term_constraint(term, kind, right_side)); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); } diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index 2e85513ff..9afb70c72 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -164,11 +164,13 @@ public: // terms - var_index add_term(const vector> & coeffs); + var_index add_term(const vector> & coeffs, + const mpq &m_v); - var_index add_term_undecided(const vector> & coeffs); + var_index add_term_undecided(const vector> & coeffs, + const mpq &m_v); - bool term_coeffs_are_ok(const vector> & coeffs); + bool term_coeffs_are_ok(const vector> & coeffs, const mpq& v); void push_and_register_term(lar_term* t); void add_row_for_term(const lar_term * term, unsigned term_ext_index); diff --git a/src/util/lp/lar_term.h b/src/util/lp/lar_term.h index 47861baf8..519847848 100644 --- a/src/util/lp/lar_term.h +++ b/src/util/lp/lar_term.h @@ -23,6 +23,7 @@ namespace lp { struct lar_term { // the term evaluates to sum of m_coeffs + m_v std::unordered_map m_coeffs; + mpq m_v; lar_term() {} void add_monomial(const mpq& c, unsigned j) { auto it = m_coeffs.find(j); @@ -36,7 +37,7 @@ struct lar_term { } bool is_empty() const { - return m_coeffs.size() == 0; + return m_coeffs.size() == 0 && is_zero(m_v); } unsigned size() const { return static_cast(m_coeffs.size()); } @@ -45,7 +46,8 @@ struct lar_term { return m_coeffs; } - lar_term(const vector>& coeffs) { + lar_term(const vector>& coeffs, + const mpq & v) : m_v(v) { for (const auto & p : coeffs) { add_monomial(p.first, p.second); } @@ -85,7 +87,7 @@ struct lar_term { template T apply(const vector& x) const { - T ret = zero_of_type(); + T ret = T(m_v); for (const auto & t : m_coeffs) { ret += t.second * x[t.first]; } @@ -94,6 +96,7 @@ struct lar_term { void clear() { m_coeffs.clear(); + m_v = zero_of_type(); } struct ival { From 257ba6218fcf41b531b55f7f7d45ae092e189bf2 Mon Sep 17 00:00:00 2001 From: Lev Date: Fri, 14 Sep 2018 11:48:17 -0700 Subject: [PATCH 044/138] remove gomory.h Signed-off-by: Lev --- src/util/lp/gomory.h | 46 -------------------------------------------- 1 file changed, 46 deletions(-) delete mode 100644 src/util/lp/gomory.h diff --git a/src/util/lp/gomory.h b/src/util/lp/gomory.h deleted file mode 100644 index 87879f9f1..000000000 --- a/src/util/lp/gomory.h +++ /dev/null @@ -1,46 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - - -Abstract: - - - -Author: - Nikolaj Bjorner (nbjorner) - Lev Nachmanson (levnach) - -Revision History: - - ---*/ -#pragma once -#include "util/lp/lar_term.h" -#include "util/lp/lia_move.h" -#include "util/lp/explanation.h" - -namespace lp { -class gomory { - lar_term & m_t; // the term to return in the cut - mpq & m_k; // the right side of the cut - explanation& m_ex; // the conflict explanation - bool & m_upper; // we have a cut m_t*x <= k if m_upper is true nad m_t*x >= k otherwise - unsigned m_basic_inf_int_j; // a basis column which has to be an integer but has a not integral value - const row_strip& m_row -public : - gomory(lar_term & m_t, - mpq & m_k, - explanation& m_ex, - bool & m_upper, - unsigned m_basic_inf_int_j ) : - m_t(t), - m_k(k), - m_ex(ex), - m_upper(upper), - m_basic_inf_int_j(j) { - } -}; -} From 26764b076f2eab2656a5093946877376999a5530 Mon Sep 17 00:00:00 2001 From: Lev Date: Fri, 14 Sep 2018 12:39:46 -0700 Subject: [PATCH 045/138] adjust cuts and branch (m_t and m_k) for terms Signed-off-by: Lev --- src/util/lp/int_solver.cpp | 18 ++++++++++++------ src/util/lp/int_solver.h | 2 +- src/util/lp/lar_solver.cpp | 10 ++++++++++ src/util/lp/lar_solver.h | 1 + 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/util/lp/int_solver.cpp b/src/util/lp/int_solver.cpp index c416c63f3..f12e93103 100644 --- a/src/util/lp/int_solver.cpp +++ b/src/util/lp/int_solver.cpp @@ -383,19 +383,25 @@ typedef monomial mono; // this will allow to enable and disable tracking of the pivot rows -struct pivoted_rows_tracking_control { - lar_solver * m_lar_solver; - bool m_track_pivoted_rows; - pivoted_rows_tracking_control(lar_solver* ls) : +struct check_return_helper { + lar_solver * m_lar_solver; + const lia_move & m_r; + bool m_track_pivoted_rows; + check_return_helper(lar_solver* ls, const lia_move& r) : m_lar_solver(ls), + m_r(r), m_track_pivoted_rows(ls->get_track_pivoted_rows()) { TRACE("pivoted_rows", tout << "pivoted rows = " << ls->m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows->size() << std::endl;); m_lar_solver->set_track_pivoted_rows(false); } - ~pivoted_rows_tracking_control() { + ~check_return_helper() { TRACE("pivoted_rows", tout << "pivoted rows = " << m_lar_solver->m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows->size() << std::endl;); m_lar_solver->set_track_pivoted_rows(m_track_pivoted_rows); + if (m_r == lia_move::cut || m_r == lia_move::branch) { + int_solver * s = m_lar_solver->get_int_solver(); + m_lar_solver->adjust_cut_for_terms(*(s->m_t), *(s->m_k)); + } } }; @@ -615,7 +621,7 @@ lia_move int_solver::check(lar_term& t, mpq& k, explanation& ex, bool & upper) { lia_move r = run_gcd_test(); if (r != lia_move::undef) return r; - pivoted_rows_tracking_control pc(m_lar_solver); + check_return_helper pc(m_lar_solver, r); if(settings().m_int_pivot_fixed_vars_from_basis) m_lar_solver->pivot_fixed_vars_from_basis(); diff --git a/src/util/lp/int_solver.h b/src/util/lp/int_solver.h index 82fcb6eb4..414ca3006 100644 --- a/src/util/lp/int_solver.h +++ b/src/util/lp/int_solver.h @@ -160,5 +160,5 @@ public: bool hnf_has_var_with_non_integral_value() const; bool hnf_cutter_is_full() const; void patch_nbasic_column(unsigned j, bool patch_only_int_vals); -}; + }; } diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp index 1340d1826..89044c7d6 100644 --- a/src/util/lp/lar_solver.cpp +++ b/src/util/lp/lar_solver.cpp @@ -2265,6 +2265,16 @@ void lar_solver::set_cut_strategy(unsigned cut_frequency) { } } +void lar_solver::adjust_cut_for_terms(const lar_term& t, mpq & rs) { + for (const auto& p : t) { + if (!is_term(p.var())) continue; + const lar_term & p_term = get_term(p.var()); + if (p_term.m_v.is_zero()) continue; + rs -= p.coeff() * p_term.m_v; + } +} + + } // namespace lp diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index 9afb70c72..6ef0ea596 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -584,5 +584,6 @@ public: lar_term get_term_to_maximize(unsigned ext_j) const; void set_cut_strategy(unsigned cut_frequency); bool sum_first_coords(const lar_term& t, mpq & val) const; + void adjust_cut_for_terms(const lar_term& t, mpq & rs); }; } From 324396e4039d417057f466341fba45108c57dd24 Mon Sep 17 00:00:00 2001 From: Lev Date: Fri, 14 Sep 2018 17:12:49 -0700 Subject: [PATCH 046/138] separate the gomory cut functionality in a separate file Signed-off-by: Lev --- src/util/lp/CMakeLists.txt | 1 + src/util/lp/gomory.cpp | 227 +++++++++++++++++++++++++++++++++++++ src/util/lp/gomory.h | 36 ++++++ src/util/lp/int_solver.cpp | 221 +----------------------------------- src/util/lp/int_solver.h | 25 ++-- src/util/lp/lar_solver.cpp | 1 + src/util/lp/lar_solver.h | 5 +- 7 files changed, 284 insertions(+), 232 deletions(-) create mode 100644 src/util/lp/gomory.cpp create mode 100644 src/util/lp/gomory.h diff --git a/src/util/lp/CMakeLists.txt b/src/util/lp/CMakeLists.txt index bde6ed93a..539c68712 100644 --- a/src/util/lp/CMakeLists.txt +++ b/src/util/lp/CMakeLists.txt @@ -6,6 +6,7 @@ z3_add_component(lp core_solver_pretty_printer.cpp dense_matrix.cpp eta_matrix.cpp + gomory.cpp indexed_vector.cpp int_solver.cpp lar_solver.cpp diff --git a/src/util/lp/gomory.cpp b/src/util/lp/gomory.cpp new file mode 100644 index 000000000..dd3d7bbed --- /dev/null +++ b/src/util/lp/gomory.cpp @@ -0,0 +1,227 @@ +/*++ + Copyright (c) 2017 Microsoft Corporation + + Module Name: + + + + Abstract: + + + + Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + + Revision History: + + + --*/ +#include "util/lp/gomory.h" +#include "util/lp/int_solver.h" +#include "util/lp/lar_solver.h" +namespace lp { + +class gomory::imp { + lar_term & m_t; // the term to return in the cut + mpq & m_k; // the right side of the cut + explanation& m_ex; // the conflict explanation + unsigned m_inf_col; // a basis column which has to be an integer but has a not integral value + const row_strip& m_row; + const int_solver& m_int_solver; + + + const impq & get_value(unsigned j) const { return m_int_solver.get_value(j); } + bool is_real(unsigned j) const { return m_int_solver.is_real(j); } + bool at_lower(unsigned j) const { return m_int_solver.at_lower(j); } + bool at_upper(unsigned j) const { return m_int_solver.at_upper(j); } + const impq & lower_bound(unsigned j) const { return m_int_solver.lower_bound(j); } + const impq & upper_bound(unsigned j) const { return m_int_solver.upper_bound(j); } + constraint_index column_lower_bound_constraint(unsigned j) const { return m_int_solver.column_lower_bound_constraint(j); } + constraint_index column_upper_bound_constraint(unsigned j) const { return m_int_solver.column_upper_bound_constraint(j); } + + void int_case_in_gomory_cut(const mpq & a, unsigned x_j, + mpq & lcm_den, const mpq& f_0, const mpq& one_minus_f_0) { + lp_assert(is_int(x_j)); + lp_assert(!a.is_int()); + mpq f_j = int_solver::fractional_part(a); + TRACE("gomory_cut_detail", + tout << a << " x_j" << x_j << " k = " << m_k << "\n"; + tout << "f_j: " << f_j << "\n"; + tout << "f_0: " << f_0 << "\n"; + tout << "1 - f_0: " << 1 - f_0 << "\n"; + tout << "at_lower(" << x_j << ") = " << at_lower(x_j) << std::endl; + ); + lp_assert (!f_j.is_zero()); + mpq new_a; + if (at_lower(x_j)) { + if (f_j <= one_minus_f_0) { + new_a = f_j / one_minus_f_0; + } + else { + new_a = (1 - f_j) / f_0; + } + m_k.addmul(new_a, lower_bound(x_j).x); + m_ex.push_justification(column_lower_bound_constraint(x_j), new_a); + } + else { + lp_assert(at_upper(x_j)); + if (f_j <= f_0) { + new_a = f_j / f_0; + } + else { + new_a = (mpq(1) - f_j) / one_minus_f_0; + } + new_a.neg(); // the upper terms are inverted + m_k.addmul(new_a, upper_bound(x_j).x); + m_ex.push_justification(column_upper_bound_constraint(x_j), new_a); + } + TRACE("gomory_cut_detail", tout << "new_a: " << new_a << " k: " << m_k << "\n";); + m_t.add_monomial(new_a, x_j); + lcm_den = lcm(lcm_den, denominator(new_a)); + } + + void real_case_in_gomory_cut(const mpq & a, unsigned x_j, const mpq& f_0, const mpq& one_minus_f_0) { + TRACE("gomory_cut_detail_real", tout << "real\n";); + mpq new_a; + if (at_lower(x_j)) { + if (a.is_pos()) { + new_a = a / one_minus_f_0; + } + else { + new_a = a / f_0; + new_a.neg(); + } + m_k.addmul(new_a, lower_bound(x_j).x); // is it a faster operation than + // k += lower_bound(x_j).x * new_a; + m_ex.push_justification(column_lower_bound_constraint(x_j), new_a); + } + else { + lp_assert(at_upper(x_j)); + if (a.is_pos()) { + new_a = a / f_0; + new_a.neg(); // the upper terms are inverted. + } + else { + new_a = a / one_minus_f_0; + } + m_k.addmul(new_a, upper_bound(x_j).x); // k += upper_bound(x_j).x * new_a; + m_ex.push_justification(column_upper_bound_constraint(x_j), new_a); + } + TRACE("gomory_cut_detail_real", tout << a << "*v" << x_j << " k: " << m_k << "\n";); + m_t.add_monomial(new_a, x_j); + } + + lia_move report_conflict_from_gomory_cut() { + lp_assert(m_k.is_pos()); + // conflict 0 >= k where k is positive + m_k.neg(); // returning 0 <= -k + return lia_move::conflict; + } + + void adjust_term_and_k_for_some_ints_case_gomory(mpq &lcm_den) { + lp_assert(!m_t.is_empty()); + auto pol = m_t.coeffs_as_vector(); + m_t.clear(); + if (pol.size() == 1) { + TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;); + unsigned v = pol[0].second; + lp_assert(is_int(v)); + const mpq& a = pol[0].first; + m_k /= a; + if (a.is_pos()) { // we have av >= k + if (!m_k.is_int()) + m_k = ceil(m_k); + // switch size + m_t.add_monomial(- mpq(1), v); + m_k.neg(); + } else { + if (!m_k.is_int()) + m_k = floor(m_k); + m_t.add_monomial(mpq(1), v); + } + } else { + TRACE("gomory_cut_detail", tout << "pol.size() > 1" << std::endl;); + lcm_den = lcm(lcm_den, denominator(m_k)); + lp_assert(lcm_den.is_pos()); + if (!lcm_den.is_one()) { + // normalize coefficients of integer parameters to be integers. + for (auto & pi: pol) { + pi.first *= lcm_den; + SASSERT(!is_int(pi.second) || pi.first.is_int()); + } + m_k *= lcm_den; + } + // negate everything to return -pol <= -m_k + for (const auto & pi: pol) + m_t.add_monomial(-pi.first, pi.second); + m_k.neg(); + } + TRACE("gomory_cut_detail", tout << "k = " << m_k << std::endl;); + lp_assert(m_k.is_int()); + } +public: + lia_move create_cut() { + TRACE("gomory_cut", + tout << "applying cut at:\n"; m_int_solver.m_lar_solver->print_row(m_row, tout); tout << std::endl; + for (auto & p : m_row) { + m_int_solver.m_lar_solver->m_mpq_lar_core_solver.m_r_solver.print_column_info(p.var(), tout); + } + tout << "inf_col = " << m_inf_col << std::endl; + ); + + // gomory will be t <= k and the current solution has a property t > k + m_k = 1; + mpq lcm_den(1); + unsigned x_j; + mpq a; + bool some_int_columns = false; + mpq f_0 = int_solver::fractional_part(get_value(m_inf_col)); + mpq one_min_f_0 = 1 - f_0; + for (const auto & p : m_row) { + x_j = p.var(); + if (x_j == m_inf_col) + continue; + // make the format compatible with the format used in: Integrating Simplex with DPLL(T) + a = p.coeff(); + a.neg(); + if (is_real(x_j)) + real_case_in_gomory_cut(a, x_j, f_0, one_min_f_0); + else if (!a.is_int()) { // f_j will be zero and no monomial will be added + some_int_columns = true; + int_case_in_gomory_cut(a, x_j, lcm_den, f_0, one_min_f_0); + } + } + + if (m_t.is_empty()) + return report_conflict_from_gomory_cut(); + if (some_int_columns) + adjust_term_and_k_for_some_ints_case_gomory(lcm_den); + lp_assert(m_int_solver.current_solution_is_inf_on_cut()); + m_int_solver.m_lar_solver->subs_term_columns(m_t, m_k); + TRACE("gomory_cut", tout<<"gomory cut:"; m_int_solver.m_lar_solver->print_term(m_t, tout); tout << " <= " << m_k << std::endl;); + return lia_move::cut; + } + imp(lar_term & t, mpq & k, explanation& ex, unsigned basic_inf_int_j, const row_strip& row, const int_solver& int_slv ) : + m_t(t), + m_k(k), + m_ex(ex), + m_inf_col(basic_inf_int_j), + m_row(row), + m_int_solver(int_slv) + { + } + +}; + +lia_move gomory::create_cut() { + return m_imp->create_cut(); +} + +gomory::gomory(lar_term & t, mpq & k, explanation& ex, unsigned basic_inf_int_j, const row_strip& row, const int_solver& s) { + m_imp = alloc(imp, t, k, ex, basic_inf_int_j, row, s); +} + +gomory::~gomory() { dealloc(m_imp); } + +} diff --git a/src/util/lp/gomory.h b/src/util/lp/gomory.h new file mode 100644 index 000000000..b7946d6b0 --- /dev/null +++ b/src/util/lp/gomory.h @@ -0,0 +1,36 @@ +/*++ +Copyright (c) 2017 Microsoft Corporation + +Module Name: + + + +Abstract: + + + +Author: + Nikolaj Bjorner (nbjorner) + Lev Nachmanson (levnach) + +Revision History: + + +--*/ +#pragma once +#include "util/lp/lar_term.h" +#include "util/lp/lia_move.h" +#include "util/lp/explanation.h" +#include "util/lp/static_matrix.h" + +namespace lp { +class int_solver; +class gomory { + class imp; + imp *m_imp; +public : + gomory(lar_term & t, mpq & k, explanation& ex, unsigned basic_inf_int_j, const row_strip& row, const int_solver& s); + lia_move create_cut(); + ~gomory(); +}; +} diff --git a/src/util/lp/int_solver.cpp b/src/util/lp/int_solver.cpp index f12e93103..cc01a0038 100644 --- a/src/util/lp/int_solver.cpp +++ b/src/util/lp/int_solver.cpp @@ -8,6 +8,7 @@ #include "util/lp/lp_utils.h" #include #include "util/lp/monomial.h" +#include "util/lp/gomory.h" namespace lp { @@ -101,12 +102,7 @@ bool int_solver::is_gomory_cut_target(const row_strip& row) { unsigned j; for (const auto & p : row) { j = p.var(); - if (is_base(j)) continue; - if (is_free(j)) - return false; - if (!at_bound(j)) - return false; - if (!is_zero(get_value(j).y)) { + if (!is_base(j) && (!at_bound(j) || !is_zero(get_value(j).y))) { TRACE("gomory_cut", tout << "row is not gomory cut target:\n"; display_column(tout, j); tout << "infinitesimal: " << !is_zero(get_value(j).y) << "\n";); @@ -117,36 +113,6 @@ bool int_solver::is_gomory_cut_target(const row_strip& row) { } -void int_solver::real_case_in_gomory_cut(const mpq & a, unsigned x_j, const mpq& f_0, const mpq& one_minus_f_0) { - TRACE("gomory_cut_detail_real", tout << "real\n";); - mpq new_a; - if (at_low(x_j)) { - if (a.is_pos()) { - new_a = a / one_minus_f_0; - } - else { - new_a = a / f_0; - new_a.neg(); - } - m_k->addmul(new_a, lower_bound(x_j).x); // is it a faster operation than - // k += lower_bound(x_j).x * new_a; - m_ex->push_justification(column_lower_bound_constraint(x_j), new_a); - } - else { - lp_assert(at_upper(x_j)); - if (a.is_pos()) { - new_a = a / f_0; - new_a.neg(); // the upper terms are inverted. - } - else { - new_a = a / one_minus_f_0; - } - m_k->addmul(new_a, upper_bound(x_j).x); // k += upper_bound(x_j).x * new_a; - m_ex->push_justification(column_upper_bound_constraint(x_j), new_a); - } - TRACE("gomory_cut_detail_real", tout << a << "*v" << x_j << " k: " << *m_k << "\n";); - m_t->add_monomial(new_a, x_j); -} constraint_index int_solver::column_upper_bound_constraint(unsigned j) const { return m_lar_solver->get_column_upper_bound_witness(j); @@ -157,99 +123,6 @@ constraint_index int_solver::column_lower_bound_constraint(unsigned j) const { } -void int_solver::int_case_in_gomory_cut(const mpq & a, unsigned x_j, - mpq & lcm_den, const mpq& f_0, const mpq& one_minus_f_0) { - lp_assert(is_int(x_j)); - lp_assert(!a.is_int()); - mpq f_j = fractional_part(a); - TRACE("gomory_cut_detail", - tout << a << " x_j" << x_j << " k = " << *m_k << "\n"; - tout << "f_j: " << f_j << "\n"; - tout << "f_0: " << f_0 << "\n"; - tout << "1 - f_0: " << 1 - f_0 << "\n"; - tout << "at_low(" << x_j << ") = " << at_low(x_j) << std::endl; - ); - lp_assert (!f_j.is_zero()); - mpq new_a; - if (at_low(x_j)) { - if (f_j <= one_minus_f_0) { - new_a = f_j / one_minus_f_0; - } - else { - new_a = (1 - f_j) / f_0; - } - m_k->addmul(new_a, lower_bound(x_j).x); - m_ex->push_justification(column_lower_bound_constraint(x_j), new_a); - } - else { - lp_assert(at_upper(x_j)); - if (f_j <= f_0) { - new_a = f_j / f_0; - } - else { - new_a = (mpq(1) - f_j) / one_minus_f_0; - } - new_a.neg(); // the upper terms are inverted - m_k->addmul(new_a, upper_bound(x_j).x); - m_ex->push_justification(column_upper_bound_constraint(x_j), new_a); - } - TRACE("gomory_cut_detail", tout << "new_a: " << new_a << " k: " << *m_k << "\n";); - m_t->add_monomial(new_a, x_j); - lcm_den = lcm(lcm_den, denominator(new_a)); -} - -lia_move int_solver::report_conflict_from_gomory_cut() { - TRACE("empty_pol",); - lp_assert(m_k->is_pos()); - // conflict 0 >= k where k is positive - m_k->neg(); // returning 0 <= -k - return lia_move::conflict; -} - -void int_solver::gomory_cut_adjust_t_and_k(vector> & pol, - lar_term & t, - mpq &k, - bool some_ints, - mpq & lcm_den) { - if (!some_ints) - return; - - t.clear(); - if (pol.size() == 1) { - unsigned v = pol[0].second; - lp_assert(is_int(v)); - bool k_is_int = k.is_int(); - const mpq& a = pol[0].first; - k /= a; - if (a.is_pos()) { // we have av >= k - if (!k_is_int) - k = ceil(k); - // switch size - t.add_monomial(- mpq(1), v); - k.neg(); - } else { - if (!k_is_int) - k = floor(k); - t.add_monomial(mpq(1), v); - } - } else if (some_ints) { - lcm_den = lcm(lcm_den, denominator(k)); - lp_assert(lcm_den.is_pos()); - if (!lcm_den.is_one()) { - // normalize coefficients of integer parameters to be integers. - for (auto & pi: pol) { - pi.first *= lcm_den; - SASSERT(!is_int(pi.second) || pi.first.is_int()); - } - k *= lcm_den; - } - // negate everything to return -pol <= -k - for (const auto & pi: pol) - t.add_monomial(-pi.first, pi.second); - k.neg(); - } -} - bool int_solver::current_solution_is_inf_on_cut() const { const auto & x = m_lar_solver->m_mpq_lar_core_solver.m_r_x; impq v = m_t->apply(x); @@ -261,95 +134,11 @@ bool int_solver::current_solution_is_inf_on_cut() const { return v * sign > (*m_k) * sign; } -void int_solver::adjust_term_and_k_for_some_ints_case_gomory(mpq &lcm_den) { - lp_assert(!m_t->is_empty()); - auto pol = m_t->coeffs_as_vector(); - m_t->clear(); - if (pol.size() == 1) { - TRACE("gomory_cut_detail", tout << "pol.size() is 1" << std::endl;); - unsigned v = pol[0].second; - lp_assert(is_int(v)); - const mpq& a = pol[0].first; - (*m_k) /= a; - if (a.is_pos()) { // we have av >= k - if (!(*m_k).is_int()) - (*m_k) = ceil((*m_k)); - // switch size - m_t->add_monomial(- mpq(1), v); - (*m_k).neg(); - } else { - if (!(*m_k).is_int()) - (*m_k) = floor((*m_k)); - m_t->add_monomial(mpq(1), v); - } - } else { - TRACE("gomory_cut_detail", tout << "pol.size() > 1" << std::endl;); - lcm_den = lcm(lcm_den, denominator((*m_k))); - lp_assert(lcm_den.is_pos()); - if (!lcm_den.is_one()) { - // normalize coefficients of integer parameters to be integers. - for (auto & pi: pol) { - pi.first *= lcm_den; - SASSERT(!is_int(pi.second) || pi.first.is_int()); - } - (*m_k) *= lcm_den; - } - // negate everything to return -pol <= -(*m_k) - for (const auto & pi: pol) - m_t->add_monomial(-pi.first, pi.second); - (*m_k).neg(); - } - TRACE("gomory_cut_detail", tout << "k = " << (*m_k) << std::endl;); - lp_assert((*m_k).is_int()); -} - - - - lia_move int_solver::mk_gomory_cut( unsigned inf_col, const row_strip & row) { - lp_assert(column_is_int_inf(inf_col)); - TRACE("gomory_cut", - tout << "applying cut at:\n"; m_lar_solver->print_row(row, tout); tout << std::endl; - for (auto & p : row) { - m_lar_solver->m_mpq_lar_core_solver.m_r_solver.print_column_info(p.var(), tout); - } - tout << "inf_col = " << inf_col << std::endl; - ); - - // gomory will be t <= k and the current solution has a property t > k - *m_k = 1; - mpq lcm_den(1); - unsigned x_j; - mpq a; - bool some_int_columns = false; - mpq f_0 = int_solver::fractional_part(get_value(inf_col)); - mpq one_min_f_0 = 1 - f_0; - for (const auto & p : row) { - x_j = p.var(); - if (x_j == inf_col) - continue; - // make the format compatible with the format used in: Integrating Simplex with DPLL(T) - a = p.coeff(); - a.neg(); - if (is_real(x_j)) - real_case_in_gomory_cut(a, x_j, f_0, one_min_f_0); - else if (!a.is_int()) { // f_j will be zero and no monomial will be added - some_int_columns = true; - int_case_in_gomory_cut(a, x_j, lcm_den, f_0, one_min_f_0); - } - } - - if (m_t->is_empty()) - return report_conflict_from_gomory_cut(); - if (some_int_columns) - adjust_term_and_k_for_some_ints_case_gomory(lcm_den); - - lp_assert(current_solution_is_inf_on_cut()); - m_lar_solver->subs_term_columns(*m_t); - TRACE("gomory_cut", tout<<"precut:"; m_lar_solver->print_term(*m_t, tout); tout << " <= " << *m_k << std::endl;); - return lia_move::cut; + gomory gc(*m_t, *m_k, *m_ex, inf_col, row, *this); + return gc.create_cut(); } lia_move int_solver::proceed_with_gomory_cut(unsigned j) { @@ -1121,7 +910,7 @@ bool int_solver::at_bound(unsigned j) const { } } -bool int_solver::at_low(unsigned j) const { +bool int_solver::at_lower(unsigned j) const { auto & mpq_solver = m_lar_solver->m_mpq_lar_core_solver.m_r_solver; switch (mpq_solver.m_column_types[j] ) { case column_type::fixed: diff --git a/src/util/lp/int_solver.h b/src/util/lp/int_solver.h index 414ca3006..dfe51711c 100644 --- a/src/util/lp/int_solver.h +++ b/src/util/lp/int_solver.h @@ -53,6 +53,13 @@ public: bool move_non_basic_column_to_bounds(unsigned j); lia_move check_wrapper(lar_term& t, mpq& k, explanation& ex); bool is_base(unsigned j) const; + bool is_real(unsigned j) const; + const impq & lower_bound(unsigned j) const; + const impq & upper_bound(unsigned j) const; + bool is_int(unsigned j) const; + const impq & get_value(unsigned j) const; + bool at_lower(unsigned j) const; + bool at_upper(unsigned j) const; private: @@ -79,10 +86,7 @@ private: void add_to_explanation_from_fixed_or_boxed_column(unsigned j); lia_move patch_nbasic_columns(); bool get_freedom_interval_for_column(unsigned j, bool & inf_l, impq & l, bool & inf_u, impq & u, mpq & m); - const impq & lower_bound(unsigned j) const; - const impq & upper_bound(unsigned j) const; - bool is_int(unsigned j) const; - bool is_real(unsigned j) const; +private: bool is_boxed(unsigned j) const; bool is_fixed(unsigned j) const; bool is_free(unsigned j) const; @@ -91,7 +95,6 @@ private: void set_value_for_nbasic_column_ignore_old_values(unsigned j, const impq & new_val); bool non_basic_columns_are_at_bounds() const; bool is_feasible() const; - const impq & get_value(unsigned j) const; bool column_is_int_inf(unsigned j) const; void trace_inf_rows() const; lia_move branch_or_sat(); @@ -104,13 +107,9 @@ private: bool move_non_basic_columns_to_bounds(); void branch_infeasible_int_var(unsigned); lia_move mk_gomory_cut(unsigned inf_col, const row_strip& row); - lia_move report_conflict_from_gomory_cut(); - void adjust_term_and_k_for_some_ints_case_gomory(mpq& lcm_den); lia_move proceed_with_gomory_cut(unsigned j); bool is_gomory_cut_target(const row_strip&); bool at_bound(unsigned j) const; - bool at_low(unsigned j) const; - bool at_upper(unsigned j) const; bool has_low(unsigned j) const; bool has_upper(unsigned j) const; unsigned row_of_basic_column(unsigned j) const; @@ -125,17 +124,13 @@ public: lp_assert(is_rational(n)); return n.x - floor(n.x); } -private: - void real_case_in_gomory_cut(const mpq & a, unsigned x_j, const mpq& f_0, const mpq& one_minus_f_0); - void int_case_in_gomory_cut(const mpq & a, unsigned x_j, mpq & lcm_den, const mpq& f_0, const mpq& one_minus_f_0); constraint_index column_upper_bound_constraint(unsigned j) const; constraint_index column_lower_bound_constraint(unsigned j) const; - void display_row_info(std::ostream & out, unsigned row_index) const; - void gomory_cut_adjust_t_and_k(vector> & pol, lar_term & t, mpq &k, bool num_ints, mpq &lcm_den); bool current_solution_is_inf_on_cut() const; -public: + bool shift_var(unsigned j, unsigned range); private: + void display_row_info(std::ostream & out, unsigned row_index) const; unsigned random(); bool has_inf_int() const; lia_move create_branch_on_column(int j); diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp index 89044c7d6..3a53b6068 100644 --- a/src/util/lp/lar_solver.cpp +++ b/src/util/lp/lar_solver.cpp @@ -1645,6 +1645,7 @@ void lar_solver::push_and_register_term(lar_term* t) { // terms var_index lar_solver::add_term(const vector> & coeffs, const mpq &m_v) { + TRACE("add_term_lar_solver", print_linear_combination_of_column_indices(coeffs, tout);); if (strategy_is_undecided()) return add_term_undecided(coeffs, m_v); diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index 6ef0ea596..3c0ed4fbf 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -535,7 +535,7 @@ public: return m_columns_to_ul_pairs()[j].lower_bound_witness(); } - void subs_term_columns(lar_term& t) { + void subs_term_columns(lar_term& t, mpq & rs) { vector> columns_to_subs; for (const auto & m : t.m_coeffs) { unsigned tj = adjust_column_index_to_term_index(m.first); @@ -545,9 +545,12 @@ public: for (const auto & p : columns_to_subs) { auto it = t.m_coeffs.find(p.first); lp_assert(it != t.m_coeffs.end()); + const lar_term& lt = get_term(p.second); mpq v = it->second; t.m_coeffs.erase(it); t.m_coeffs[p.second] = v; + if (lt.m_v.is_zero()) continue; + rs -= v * lt.m_v; } } From 0232383191dab226f8ca0e993c31cbce039a2c2c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 15 Sep 2018 16:59:06 -0700 Subject: [PATCH 047/138] mini IC3 sample Signed-off-by: Nikolaj Bjorner --- examples/python/data/horn1.smt2 | 50 +++++ examples/python/data/horn2.smt2 | 44 ++++ examples/python/mini_ic3.py | 363 ++++++++++++++++++++++++++++++++ 3 files changed, 457 insertions(+) create mode 100644 examples/python/data/horn1.smt2 create mode 100644 examples/python/data/horn2.smt2 create mode 100644 examples/python/mini_ic3.py diff --git a/examples/python/data/horn1.smt2 b/examples/python/data/horn1.smt2 new file mode 100644 index 000000000..20d043534 --- /dev/null +++ b/examples/python/data/horn1.smt2 @@ -0,0 +1,50 @@ +(declare-rel Goal (Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool)) +(declare-rel Invariant (Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool)) +(declare-var A Bool) +(declare-var B Bool) +(declare-var C Bool) +(declare-var D Bool) +(declare-var E Bool) +(declare-var F Bool) +(declare-var G Bool) +(declare-var H Bool) +(declare-var I Bool) +(declare-var J Bool) +(declare-var K Bool) +(declare-var L Bool) +(declare-var M Bool) +(declare-var N Bool) +(declare-var O Bool) +(declare-var P Bool) +(declare-var Q Bool) +(declare-var R Bool) +(declare-var S Bool) +(declare-var T Bool) +(declare-var U Bool) +(declare-var V Bool) +(declare-var W Bool) +(declare-var X Bool) +(rule (=> (not (or L K J I H G F E D C B A)) (Invariant L K J I H G F E D C B A))) +(rule (let ((a!1 (and (Invariant X W V U T S R Q P O N M) + (=> (not (and true)) (not F)) + (=> (not (and true)) (not E)) + (=> (not (and W)) (not D)) + (=> (not (and W)) (not C)) + (=> (not (and U)) (not B)) + (=> (not (and U)) (not A)) + (= L (xor F X)) + (= K (xor E W)) + (= J (xor D V)) + (= I (xor C U)) + (= H (xor B T)) + (= G (xor A S)) + (=> D (not E)) + (=> C (not E)) + (=> B (not C)) + (=> A (not C)) + ((_ at-most 5) L K J I H G)))) + (=> a!1 (Invariant L K J I H G F E D C B A)))) +(rule (=> (and (Invariant L K J I H G F E D C B A) L (not K) J (not I) H G) + (Goal L K J I H G F E D C B A))) + +(query Goal) \ No newline at end of file diff --git a/examples/python/data/horn2.smt2 b/examples/python/data/horn2.smt2 new file mode 100644 index 000000000..478c39d5f --- /dev/null +++ b/examples/python/data/horn2.smt2 @@ -0,0 +1,44 @@ +(declare-rel Invariant (Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool)) +(declare-rel Goal (Bool Bool Bool Bool Bool Bool Bool Bool Bool Bool)) +(declare-var A Bool) +(declare-var B Bool) +(declare-var C Bool) +(declare-var D Bool) +(declare-var E Bool) +(declare-var F Bool) +(declare-var G Bool) +(declare-var H Bool) +(declare-var I Bool) +(declare-var J Bool) +(declare-var K Bool) +(declare-var L Bool) +(declare-var M Bool) +(declare-var N Bool) +(declare-var O Bool) +(declare-var P Bool) +(declare-var Q Bool) +(declare-var R Bool) +(declare-var S Bool) +(declare-var T Bool) +(rule (=> (not (or J I H G F E D C B A)) (Invariant J I H G F E D C B A))) +(rule (let ((a!1 (and (Invariant T S R Q P O N M L K) + (=> (not (and true)) (not E)) + (=> (not (and T)) (not D)) + (=> (not (and S)) (not C)) + (=> (not (and R)) (not B)) + (=> (not (and Q)) (not A)) + (= J (xor E T)) + (= I (xor D S)) + (= H (xor C R)) + (= G (xor B Q)) + (= F (xor A P)) + (=> D (not E)) + (=> C (not D)) + (=> B (not C)) + (=> A (not B)) + ((_ at-most 3) J I H G F)))) + (=> a!1 (Invariant J I H G F E D C B A)))) +(rule (=> (and (Invariant J I H G F E D C B A) (not J) (not I) (not H) (not G) F) + (Goal J I H G F E D C B A))) + +(query Goal) diff --git a/examples/python/mini_ic3.py b/examples/python/mini_ic3.py new file mode 100644 index 000000000..3ca126a2f --- /dev/null +++ b/examples/python/mini_ic3.py @@ -0,0 +1,363 @@ +from z3 import * +import heapq + + +# Simplistic (and fragile) converter from +# a class of Horn clauses corresponding to +# a transition system into a transition system +# representation as +# It assumes it is given three Horn clauses +# of the form: +# init(x) => Invariant(x) +# Invariant(x) and trans(x,x') => Invariant(x') +# Invariant(x) and goal(x) => Goal(x) +# where Invariant and Goal are uninterpreted predicates + +class Horn2Transitions: + def __init__(self): + self.trans = True + self.init = True + self.goal = True + self.index = 0 + + def parse(self, file): + fp = Fixedpoint() + goals = fp.parse_file(file) + for r in fp.get_rules(): + if not is_quantifier(r): + continue + b = r.body() + if not is_implies(b): + continue + f = b.arg(0) + g = b.arg(1) + if self.is_goal(f, g): + continue + if self.is_transition(f, g): + continue + if self.is_init(f, g): + continue + + def is_pred(self, p, name): + return is_app(p) and p.decl().name() == name + + def is_goal(self, body, head): + if not self.is_pred(head, "Goal"): + return False + pred, inv = self.is_body(body) + if pred is None: + return False + self.goal = self.subst_vars("x", inv, pred) + return True + + def is_body(self, body): + if not is_and(body): + return None, None + fmls = [f for f in body.children() if self.is_inv(f) is None] + inv = None + for f in body.children(): + if self.is_inv(f) is not None: + inv = f; + break + return And(fmls), inv + + def is_inv(self, f): + if self.is_pred(f, "Invariant"): + return f + return None + + def is_transition(self, body, head): + pred, inv0 = self.is_body(body) + if pred is None: + return False + inv1 = self.is_inv(head) + if inv1 is None: + return False + pred = self.subst_vars("x", inv0, pred) + self.xs = self.vars + pred = self.subst_vars("xn", inv1, pred) + self.xns = self.vars + self.trans = pred + return True + + def is_init(self, body, head): + for f in body.children(): + if self.is_inv(f) is not None: + return False + inv = self.is_inv(head) + if inv is None: + return False + self.init = self.subst_vars("x", inv, body) + return True + + def subst_vars(self, prefix, inv, fml): + subst = self.mk_subst(prefix, inv) + self.vars = [ v for (k,v) in subst ] + return substitute(fml, subst) + + def mk_subst(self, prefix, inv): + self.index = 0 + return [(f, self.mk_bool(prefix)) for f in inv.children()] + + def mk_bool(self, prefix): + self.index += 1 + return Bool("%s%d" % (prefix, self.index)) + +# Produce a finite domain solver. +# The theory QF_FD covers bit-vector formulas +# and pseudo-Boolean constraints. +# By default cardinality and pseudo-Boolean +# constraints are converted to clauses. To override +# this default for cardinality constraints +# we set sat.cardinality.solver to True + +def fd_solver(): + s = SolverFor("QF_FD") + s.set("sat.cardinality.solver", True) + return s + + +# negate, avoid double negation +def negate(f): + if is_not(f): + return f.arg(0) + else: + return Not(f) + +def cube2clause(cube): + return Or([negate(f) for f in cube]) + +class State: + def __init__(self, s): + self.R = set([]) + self.solver = s + + def add(self, clause): + self.R |= { clause } + self.solver.add(clause) + +class Goal: + def __init__(self, cube, parent, level): + self.level = level + self.cube = cube + self.parent = parent + +# push a goal on a heap +def push_heap(heap, goal): + heapq.heappush(heap, (goal.level, goal)) + +class MiniIC3: + def __init__(self, init, trans, goal, x0, xn): + self.x0 = x0 + self.xn = xn + self.init = init + self.bad = goal + self.trans = trans + self.min_cube_solver = fd_solver() + self.min_cube_solver.add(Not(trans)) + self.goals = [] + s = State(fd_solver()) + s.add(init) + s.solver.add(trans) + self.states = [s] + self.s_bad = fd_solver() + self.s_good = fd_solver() + self.s_bad.add(self.bad) + self.s_good.add(Not(self.bad)) + + def next(self, f): + if isinstance(f, list) or isinstance(f, tuple) or isinstance(f, AstVector): + return [self.next(f1) for f1 in f] + return substitute(f, zip(self.x0, self.xn)) + + def prev(self, f): + if isinstance(f, list) or isinstance(f, tuple) or isinstance(f, AstVector): + return [self.prev(f1) for f1 in f] + return substitute(f, zip(self.xn, self.x0)) + + def add_solver(self): + s = fd_solver() + s.add(self.trans) + self.states += [State(s)] + + # Check if the initial state is bad + def check_init(self): + s = fd_solver() + s.add(self.bad) + s.add(self.init) + return unsat == s.check() + + # Remove clauses that are subsumed + def prune(self, i): + removed = set([]) + s = fd_solver() + for f1 in self.states[i].R: + s.push() + for f2 in self.states[i].R: + if f2 not in removed: + s.add(Not(f2) if f1.eq(f2) else f2) + if s.check() == unsat: + removed |= { f1 } + s.pop() + self.states[i].R = self.states[i].R - removed + + def R(self, i): + return And(self.states[i].R) + + # Check if there are two states next to each other that have the same clauses. + def is_valid(self): + i = 1 + while i + 1 < len(self.states): + if not (self.states[i].R - self.states[i+1].R): + self.prune(i) + return self.R(i) + i += 1 + return None + + def value2literal(self, m, x): + value = m.eval(x) + if is_true(value): + return x + if is_false(value): + return Not(x) + return None + + def values2literals(self, m, xs): + p = [self.value2literal(m, x) for x in xs] + return [x for x in p if x is not None] + + def project0(self, m): + return self.values2literals(m, self.x0) + + def projectN(self, m): + return self.values2literals(m, self.xn) + + # Determine if there is a cube for the current state + # that is potentially reachable. + def unfold(self): + core = [] + self.s_bad.push() + R = self.R(len(self.states)-1) + self.s_bad.add(R) + is_sat = self.s_bad.check() + if is_sat == sat: + m = self.s_bad.model() + props = self.project0(m) + self.s_good.push() + self.s_good.add(R) + is_sat2 = self.s_good.check(props) + assert is_sat2 == unsat + core = self.s_good.unsat_core() + self.s_good.pop() + self.s_bad.pop() + return is_sat, core + + # Block a cube by asserting the clause corresponding to its negation + def block_cube(self, i, cube): + self.assert_clause(i, cube2clause(cube)) + + # Add a clause to levels 0 until i + def assert_clause(self, i, clause): + for j in range(i + 1): + if clause not in self.states[j].R: + self.states[j].add(clause) + + # minimize cube that is core of Dual solver. + # this assumes that props & cube => Trans + def minimize_cube(self, cube, lits): + is_sat = self.min_cube_solver.check(lits + [c for c in cube]) + assert is_sat == unsat + core = self.min_cube_solver.unsat_core() + assert core + return [c for c in core if c in set(cube)] + + # A state s0 and level f0 such that + # not(s0) is f0-1 inductive + def ic3_blocked(self, s0, f0): + push_heap(self.goals, Goal(self.next(s0), None, f0)) + while self.goals: + f, g = heapq.heappop(self.goals) + sys.stdout.write("%d." % f) + sys.stdout.flush() + # Not(g.cube) is f-1 invariant + if f == 0: + print("") + return g + cube, f, is_sat = self.is_inductive(f, g.cube) + if is_sat == unsat: + self.block_cube(f, self.prev(cube)) + if f < f0: + push_heap(self.goals, Goal(g.cube, g.parent, f + 1)) + elif is_sat == sat: + push_heap(self.goals, Goal(cube, g, f - 1)) + push_heap(self.goals, g) + else: + return is_sat + print("") + return None + + # Rudimentary generalization: + # If the cube is already unsat with respect to transition relation + # extract a core (not necessarily minimal) + # otherwise, just return the cube. + def generalize(self, cube, f): + s = self.states[f - 1].solver + if unsat == s.check(cube): + return s.unsat_core(), f + return cube, f + + # Check if the negation of cube is inductive at level f + def is_inductive(self, f, cube): + s = self.states[f - 1].solver + s.push() + s.add(self.prev(Not(And(cube)))) + is_sat = s.check(cube) + if is_sat == sat: + m = s.model() + s.pop() + if is_sat == sat: + cube = self.next(self.minimize_cube(self.project0(m), self.projectN(m))) + elif is_sat == unsat: + cube, f = self.generalize(cube, f) + return cube, f, is_sat + + def run(self): + if not self.check_init(): + return "goal is reached in initial state" + level = 0 + while True: + inv = self.is_valid() + if inv is not None: + return inv + is_sat, cube = self.unfold() + if is_sat == unsat: + level += 1 + print("Unfold %d" % level) + sys.stdout.flush() + self.add_solver() + elif is_sat == sat: + cex = self.ic3_blocked(cube, level) + if cex is not None: + return cex + else: + return is_sat + +def test(file): + h2t = Horn2Transitions() + h2t.parse(file) + mp = MiniIC3(h2t.init, h2t.trans, h2t.goal, h2t.xs, h2t.xns) + result = mp.run() + if isinstance(result, Goal): + g = result + print("Trace") + while g: + print(g.level, g.cube) + g = g.parent + return + if isinstance(result, ExprRef): + print("Invariant:\n%s " % result) + return + print(result) + +test("data/horn1.smt2") +test("data/horn2.smt2") From 03d55426bb6c64c248d5024f1b423b2aa26964e5 Mon Sep 17 00:00:00 2001 From: Lev Date: Sat, 15 Sep 2018 17:15:46 -0700 Subject: [PATCH 048/138] fixes in gomory cut Signed-off-by: Lev --- src/smt/theory_lra.cpp | 3 +- src/util/lp/gomory.cpp | 93 ++++++++++++++++++++------------------ src/util/lp/lar_solver.cpp | 2 +- 3 files changed, 53 insertions(+), 45 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index f8e2f9fe1..d976df56a 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1704,10 +1704,11 @@ public: TRACE("arith", tout << "canceled\n";); return l_undef; } + /* if (!check_idiv_bounds()) { TRACE("arith", tout << "idiv bounds check\n";); return l_false; - } + }*/ lp::lar_term term; lp::mpq k; lp::explanation ex; // TBD, this should be streamlined accross different explanations diff --git a/src/util/lp/gomory.cpp b/src/util/lp/gomory.cpp index dd3d7bbed..ec6877536 100644 --- a/src/util/lp/gomory.cpp +++ b/src/util/lp/gomory.cpp @@ -39,57 +39,59 @@ class gomory::imp { const impq & upper_bound(unsigned j) const { return m_int_solver.upper_bound(j); } constraint_index column_lower_bound_constraint(unsigned j) const { return m_int_solver.column_lower_bound_constraint(j); } constraint_index column_upper_bound_constraint(unsigned j) const { return m_int_solver.column_upper_bound_constraint(j); } - - void int_case_in_gomory_cut(const mpq & a, unsigned x_j, - mpq & lcm_den, const mpq& f_0, const mpq& one_minus_f_0) { - lp_assert(is_int(x_j)); - lp_assert(!a.is_int()); - mpq f_j = int_solver::fractional_part(a); + bool column_is_fixed(unsigned j) const { return m_int_solver.m_lar_solver->column_is_fixed(j); } + void int_case_in_gomory_cut(const mpq & a, unsigned j, + mpq & lcm_den, const mpq& f0, const mpq& one_minus_f0) { + lp_assert(is_int(j) && !a.is_int()); + mpq fj = int_solver::fractional_part(a); + lp_assert(fj.is_pos()); TRACE("gomory_cut_detail", - tout << a << " x_j" << x_j << " k = " << m_k << "\n"; - tout << "f_j: " << f_j << "\n"; - tout << "f_0: " << f_0 << "\n"; - tout << "1 - f_0: " << 1 - f_0 << "\n"; - tout << "at_lower(" << x_j << ") = " << at_lower(x_j) << std::endl; + tout << a << " j=" << j << " k = " << m_k; + tout << ", fj: " << fj << ", "; + tout << "f0: " << f0 << ", "; + tout << "1 - f0: " << 1 - f0 << ", "; + tout << (at_lower(j)?"at_lower":"at_upper")<< std::endl; ); - lp_assert (!f_j.is_zero()); mpq new_a; - if (at_lower(x_j)) { - if (f_j <= one_minus_f_0) { - new_a = f_j / one_minus_f_0; + mpq one_minus_fj = 1 - fj; + if (at_lower(j)) { + bool go_for_pos_a = fj / one_minus_f0 < one_minus_fj / f0; + if (go_for_pos_a) { + new_a = fj / one_minus_f0; } else { - new_a = (1 - f_j) / f_0; + new_a = one_minus_fj / f0; } - m_k.addmul(new_a, lower_bound(x_j).x); - m_ex.push_justification(column_lower_bound_constraint(x_j), new_a); + m_k.addmul(new_a, lower_bound(j).x); + m_ex.push_justification(column_lower_bound_constraint(j), new_a); } else { - lp_assert(at_upper(x_j)); - if (f_j <= f_0) { - new_a = f_j / f_0; + bool go_for_pos_a = fj / f0 < one_minus_fj / one_minus_f0; + lp_assert(at_upper(j)); + // the upper terms are inverted + if (go_for_pos_a) { + new_a = - fj / f0; } else { - new_a = (mpq(1) - f_j) / one_minus_f_0; + new_a = - one_minus_fj / one_minus_f0; } - new_a.neg(); // the upper terms are inverted - m_k.addmul(new_a, upper_bound(x_j).x); - m_ex.push_justification(column_upper_bound_constraint(x_j), new_a); + m_k.addmul(new_a, upper_bound(j).x); + m_ex.push_justification(column_upper_bound_constraint(j), new_a); } TRACE("gomory_cut_detail", tout << "new_a: " << new_a << " k: " << m_k << "\n";); - m_t.add_monomial(new_a, x_j); + m_t.add_monomial(new_a, j); lcm_den = lcm(lcm_den, denominator(new_a)); } - void real_case_in_gomory_cut(const mpq & a, unsigned x_j, const mpq& f_0, const mpq& one_minus_f_0) { + void real_case_in_gomory_cut(const mpq & a, unsigned x_j, const mpq& f0, const mpq& one_minus_f0) { TRACE("gomory_cut_detail_real", tout << "real\n";); mpq new_a; if (at_lower(x_j)) { if (a.is_pos()) { - new_a = a / one_minus_f_0; + new_a = a / one_minus_f0; } else { - new_a = a / f_0; + new_a = a / f0; new_a.neg(); } m_k.addmul(new_a, lower_bound(x_j).x); // is it a faster operation than @@ -99,11 +101,11 @@ class gomory::imp { else { lp_assert(at_upper(x_j)); if (a.is_pos()) { - new_a = a / f_0; + new_a = a / f0; new_a.neg(); // the upper terms are inverted. } else { - new_a = a / one_minus_f_0; + new_a = a / one_minus_f0; } m_k.addmul(new_a, upper_bound(x_j).x); // k += upper_bound(x_j).x * new_a; m_ex.push_justification(column_upper_bound_constraint(x_j), new_a); @@ -173,23 +175,28 @@ public: // gomory will be t <= k and the current solution has a property t > k m_k = 1; mpq lcm_den(1); - unsigned x_j; - mpq a; bool some_int_columns = false; - mpq f_0 = int_solver::fractional_part(get_value(m_inf_col)); - mpq one_min_f_0 = 1 - f_0; + mpq f0 = int_solver::fractional_part(get_value(m_inf_col)); + mpq one_min_f0 = 1 - f0; for (const auto & p : m_row) { - x_j = p.var(); - if (x_j == m_inf_col) + unsigned j = p.var(); + if (column_is_fixed(j)) { + m_ex.push_justification(column_lower_bound_constraint(j)); + m_ex.push_justification(column_upper_bound_constraint(j)); continue; + } + if (j == m_inf_col) { + lp_assert(p.coeff() == one_of_type()); + TRACE("gomory_cut_detail", tout << "seeing basic var";); + continue; + } // make the format compatible with the format used in: Integrating Simplex with DPLL(T) - a = p.coeff(); - a.neg(); - if (is_real(x_j)) - real_case_in_gomory_cut(a, x_j, f_0, one_min_f_0); - else if (!a.is_int()) { // f_j will be zero and no monomial will be added + mpq a = - p.coeff(); + if (is_real(j)) + real_case_in_gomory_cut(a, j, f0, one_min_f0); + else if (!a.is_int()) { // fj will be zero and no monomial will be added some_int_columns = true; - int_case_in_gomory_cut(a, x_j, lcm_den, f_0, one_min_f_0); + int_case_in_gomory_cut(a, j, lcm_den, f0, one_min_f0); } } diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp index 3a53b6068..09c3bd5b9 100644 --- a/src/util/lp/lar_solver.cpp +++ b/src/util/lp/lar_solver.cpp @@ -1645,7 +1645,6 @@ void lar_solver::push_and_register_term(lar_term* t) { // terms var_index lar_solver::add_term(const vector> & coeffs, const mpq &m_v) { - TRACE("add_term_lar_solver", print_linear_combination_of_column_indices(coeffs, tout);); if (strategy_is_undecided()) return add_term_undecided(coeffs, m_v); @@ -1657,6 +1656,7 @@ var_index lar_solver::add_term(const vector> & coeffs, if (m_settings.bound_propagation()) m_rows_with_changed_bounds.insert(A_r().row_count() - 1); } + CTRACE("add_term_lar_solver", !m_v.is_zero(), print_term(*m_terms.back(), tout);); lp_assert(m_var_register.size() == A_r().column_count()); return ret; } From 8c122ba9bd6840d08453b33a3ec5f9c388c3883f Mon Sep 17 00:00:00 2001 From: Lev Date: Sat, 15 Sep 2018 17:33:35 -0700 Subject: [PATCH 049/138] fixes in gomory cut Signed-off-by: Lev --- src/smt/theory_lra.cpp | 3 +-- src/util/lp/gomory.cpp | 22 +++++++--------------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index d976df56a..f8e2f9fe1 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1704,11 +1704,10 @@ public: TRACE("arith", tout << "canceled\n";); return l_undef; } - /* if (!check_idiv_bounds()) { TRACE("arith", tout << "idiv bounds check\n";); return l_false; - }*/ + } lp::lar_term term; lp::mpq k; lp::explanation ex; // TBD, this should be streamlined accross different explanations diff --git a/src/util/lp/gomory.cpp b/src/util/lp/gomory.cpp index ec6877536..244244da0 100644 --- a/src/util/lp/gomory.cpp +++ b/src/util/lp/gomory.cpp @@ -55,26 +55,18 @@ class gomory::imp { mpq new_a; mpq one_minus_fj = 1 - fj; if (at_lower(j)) { - bool go_for_pos_a = fj / one_minus_f0 < one_minus_fj / f0; - if (go_for_pos_a) { - new_a = fj / one_minus_f0; - } - else { - new_a = one_minus_fj / f0; - } + mpq fj_over_one_min_f0 = fj / one_minus_f0; + mpq one_minus_fj_over_f0 = one_minus_fj / f0; + new_a = fj_over_one_min_f0 < one_minus_fj_over_f0? fj_over_one_min_f0 : one_minus_fj_over_f0; m_k.addmul(new_a, lower_bound(j).x); m_ex.push_justification(column_lower_bound_constraint(j), new_a); } else { - bool go_for_pos_a = fj / f0 < one_minus_fj / one_minus_f0; + mpq fj_over_f0 = fj / f0; + mpq one_minus_fj_over_one_minus_f0 = one_minus_fj / one_minus_f0; lp_assert(at_upper(j)); - // the upper terms are inverted - if (go_for_pos_a) { - new_a = - fj / f0; - } - else { - new_a = - one_minus_fj / one_minus_f0; - } + // the upper terms are inverted - therefore we have - + new_a = - (fj_over_f0 < one_minus_fj_over_one_minus_f0? fj_over_f0 : one_minus_fj_over_one_minus_f0); m_k.addmul(new_a, upper_bound(j).x); m_ex.push_justification(column_upper_bound_constraint(j), new_a); } From 34bdea750c77639c1d64e74731da8a3292319c29 Mon Sep 17 00:00:00 2001 From: Lev Date: Sat, 15 Sep 2018 17:46:16 -0700 Subject: [PATCH 050/138] fixes in gomory cut Signed-off-by: Lev --- src/util/lp/gomory.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/util/lp/gomory.cpp b/src/util/lp/gomory.cpp index 244244da0..29dc98bd6 100644 --- a/src/util/lp/gomory.cpp +++ b/src/util/lp/gomory.cpp @@ -55,18 +55,14 @@ class gomory::imp { mpq new_a; mpq one_minus_fj = 1 - fj; if (at_lower(j)) { - mpq fj_over_one_min_f0 = fj / one_minus_f0; - mpq one_minus_fj_over_f0 = one_minus_fj / f0; - new_a = fj_over_one_min_f0 < one_minus_fj_over_f0? fj_over_one_min_f0 : one_minus_fj_over_f0; + new_a = fj < one_minus_f0? fj / one_minus_f0 : one_minus_fj / f0; m_k.addmul(new_a, lower_bound(j).x); m_ex.push_justification(column_lower_bound_constraint(j), new_a); } else { - mpq fj_over_f0 = fj / f0; - mpq one_minus_fj_over_one_minus_f0 = one_minus_fj / one_minus_f0; lp_assert(at_upper(j)); // the upper terms are inverted - therefore we have - - new_a = - (fj_over_f0 < one_minus_fj_over_one_minus_f0? fj_over_f0 : one_minus_fj_over_one_minus_f0); + new_a = - (fj < f0? fj / f0 : one_minus_fj / one_minus_f0); m_k.addmul(new_a, upper_bound(j).x); m_ex.push_justification(column_upper_bound_constraint(j), new_a); } From 106b677201fd385a60aa510b6abe827bd0f2ee91 Mon Sep 17 00:00:00 2001 From: Lev Date: Sat, 15 Sep 2018 17:47:54 -0700 Subject: [PATCH 051/138] fixes in gomory cut Signed-off-by: Lev --- src/util/lp/gomory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/lp/gomory.cpp b/src/util/lp/gomory.cpp index 29dc98bd6..55098dccf 100644 --- a/src/util/lp/gomory.cpp +++ b/src/util/lp/gomory.cpp @@ -61,7 +61,7 @@ class gomory::imp { } else { lp_assert(at_upper(j)); - // the upper terms are inverted - therefore we have - + // the upper terms are inverted: therefore we have the minus new_a = - (fj < f0? fj / f0 : one_minus_fj / one_minus_f0); m_k.addmul(new_a, upper_bound(j).x); m_ex.push_justification(column_upper_bound_constraint(j), new_a); From 98dfd827652aa8b10294e0dbf82618ed9a358b7d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 15 Sep 2018 21:55:49 -0700 Subject: [PATCH 052/138] adding quipie Signed-off-by: Nikolaj Bjorner --- examples/python/mini_ic3.py | 152 +++++++++++++++++++++++++++--------- 1 file changed, 114 insertions(+), 38 deletions(-) diff --git a/examples/python/mini_ic3.py b/examples/python/mini_ic3.py index 3ca126a2f..39391bd12 100644 --- a/examples/python/mini_ic3.py +++ b/examples/python/mini_ic3.py @@ -133,8 +133,9 @@ class State: self.solver = s def add(self, clause): - self.R |= { clause } - self.solver.add(clause) + if clause not in self.R: + self.R |= { clause } + self.solver.add(clause) class Goal: def __init__(self, cube, parent, level): @@ -142,9 +143,30 @@ class Goal: self.cube = cube self.parent = parent -# push a goal on a heap -def push_heap(heap, goal): - heapq.heappush(heap, (goal.level, goal)) +def is_seq(f): + return isinstance(f, list) or isinstance(f, tuple) or isinstance(f, AstVector) + +# Check if the initial state is bad +def check_disjoint(a, b): + s = fd_solver() + s.add(a) + s.add(b) + return unsat == s.check() + + +# Remove clauses that are subsumed +def prune(R): + removed = set([]) + s = fd_solver() + for f1 in R: + s.push() + for f2 in R: + if f2 not in removed: + s.add(Not(f2) if f1.eq(f2) else f2) + if s.check() == unsat: + removed |= { f1 } + s.pop() + return R - removed class MiniIC3: def __init__(self, init, trans, goal, x0, xn): @@ -166,40 +188,19 @@ class MiniIC3: self.s_good.add(Not(self.bad)) def next(self, f): - if isinstance(f, list) or isinstance(f, tuple) or isinstance(f, AstVector): + if is_seq(f): return [self.next(f1) for f1 in f] return substitute(f, zip(self.x0, self.xn)) def prev(self, f): - if isinstance(f, list) or isinstance(f, tuple) or isinstance(f, AstVector): + if is_seq(f): return [self.prev(f1) for f1 in f] return substitute(f, zip(self.xn, self.x0)) def add_solver(self): s = fd_solver() s.add(self.trans) - self.states += [State(s)] - - # Check if the initial state is bad - def check_init(self): - s = fd_solver() - s.add(self.bad) - s.add(self.init) - return unsat == s.check() - - # Remove clauses that are subsumed - def prune(self, i): - removed = set([]) - s = fd_solver() - for f1 in self.states[i].R: - s.push() - for f2 in self.states[i].R: - if f2 not in removed: - s.add(Not(f2) if f1.eq(f2) else f2) - if s.check() == unsat: - removed |= { f1 } - s.pop() - self.states[i].R = self.states[i].R - removed + self.states += [State(s)] def R(self, i): return And(self.states[i].R) @@ -209,8 +210,7 @@ class MiniIC3: i = 1 while i + 1 < len(self.states): if not (self.states[i].R - self.states[i+1].R): - self.prune(i) - return self.R(i) + return And(prune(self.states[i].R)) i += 1 return None @@ -259,8 +259,7 @@ class MiniIC3: # Add a clause to levels 0 until i def assert_clause(self, i, clause): for j in range(i + 1): - if clause not in self.states[j].R: - self.states[j].add(clause) + self.states[j].add(clause) # minimize cube that is core of Dual solver. # this assumes that props & cube => Trans @@ -271,10 +270,14 @@ class MiniIC3: assert core return [c for c in core if c in set(cube)] + # push a goal on a heap + def push_heap(self, goal): + heapq.heappush(self.goals, (goal.level, goal)) + # A state s0 and level f0 such that # not(s0) is f0-1 inductive def ic3_blocked(self, s0, f0): - push_heap(self.goals, Goal(self.next(s0), None, f0)) + self.push_heap(Goal(self.next(s0), None, f0)) while self.goals: f, g = heapq.heappop(self.goals) sys.stdout.write("%d." % f) @@ -287,10 +290,10 @@ class MiniIC3: if is_sat == unsat: self.block_cube(f, self.prev(cube)) if f < f0: - push_heap(self.goals, Goal(g.cube, g.parent, f + 1)) + self.push_heap(Goal(g.cube, g.parent, f + 1)) elif is_sat == sat: - push_heap(self.goals, Goal(cube, g, f - 1)) - push_heap(self.goals, g) + self.push_heap(Goal(cube, g, f - 1)) + self.push_heap(g) else: return is_sat print("") @@ -322,7 +325,7 @@ class MiniIC3: return cube, f, is_sat def run(self): - if not self.check_init(): + if not check_disjoint(self.init, self.bad): return "goal is reached in initial state" level = 0 while True: @@ -361,3 +364,76 @@ def test(file): test("data/horn1.smt2") test("data/horn2.smt2") + + + +""" +# TBD: Quip variant of IC3 + +must = True +may = False + +class QGoal: + def __init__(self, cube, parent, level, must): + self.level = level + self.cube = cube + self.parent = parent + self.must = must + +class Quipie(MiniIC3): + + # prev & tras -> r', such that r' intersects with cube + def add_reachable(self, prev, cube): + s = fd_solver() + s.add(self.trans) + s.add(prev) + s.add(Or(cube)) + is_sat = s.check() + assert is_sat == sat + m = s.model(); + result = [self.prev(lit) for lit in cube if is_true(m.eval(lit))] + # ? result = self.values2literals(m, cube) + assert result + self.reachable.add(result) + + # A state s0 and level f0 such that + # not(s0) is f0-1 inductive + def quipie_blocked(self, s0, f0): + self.push_heap(QGoal(self.next(s0), None, f0, must)) + while self.goals: + f, g = heapq.heappop(self.goals) + sys.stdout.write("%d." % f) + sys.stdout.flush() + if f == 0: + if g.must: + print("") + return g + self.add_reachable(self.init, p.parent.cube) + continue + + # TBD + return None + + + def run(self): + if not check_disjoint(self.init, self.bad): + return "goal is reached in initial state" + level = 0 + while True: + inv = self.is_valid() + if inv is not None: + return inv + is_sat, cube = self.unfold() + if is_sat == unsat: + level += 1 + print("Unfold %d" % level) + sys.stdout.flush() + self.add_solver() + elif is_sat == sat: + cex = self.quipie_blocked(cube, level) + if cex is not None: + return cex + else: + return is_sat + +""" \ No newline at end of file From 2b35f1a924ac00aa3a2e9403996f876ab1398d50 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 16 Sep 2018 13:14:41 -0700 Subject: [PATCH 053/138] quip Signed-off-by: Nikolaj Bjorner --- examples/python/mini_ic3.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/examples/python/mini_ic3.py b/examples/python/mini_ic3.py index 39391bd12..5a8ea566b 100644 --- a/examples/python/mini_ic3.py +++ b/examples/python/mini_ic3.py @@ -380,7 +380,7 @@ class QGoal: self.parent = parent self.must = must -class Quipie(MiniIC3): +class Quip(MiniIC3): # prev & tras -> r', such that r' intersects with cube def add_reachable(self, prev, cube): @@ -391,14 +391,13 @@ class Quipie(MiniIC3): is_sat = s.check() assert is_sat == sat m = s.model(); - result = [self.prev(lit) for lit in cube if is_true(m.eval(lit))] - # ? result = self.values2literals(m, cube) + result = self.values2literals(m, cube) assert result self.reachable.add(result) # A state s0 and level f0 such that # not(s0) is f0-1 inductive - def quipie_blocked(self, s0, f0): + def quip_blocked(self, s0, f0): self.push_heap(QGoal(self.next(s0), None, f0, must)) while self.goals: f, g = heapq.heappop(self.goals) From 286126dde972e1739635516174bf473aa206e3dc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 16 Sep 2018 13:31:37 -0700 Subject: [PATCH 054/138] fix #1828, add self-contained utility to extract arithmetical values for use in theory_seq and theory_str and other theories that access current values assigned to numeric variables Signed-off-by: Nikolaj Bjorner --- src/smt/CMakeLists.txt | 1 + src/smt/smt_arith_value.cpp | 99 +++++++++++++++++++++++++++++++++++++ src/smt/smt_arith_value.h | 37 ++++++++++++++ src/smt/theory_seq.cpp | 4 +- 4 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 src/smt/smt_arith_value.cpp create mode 100644 src/smt/smt_arith_value.h diff --git a/src/smt/CMakeLists.txt b/src/smt/CMakeLists.txt index fb1997fb4..26b099240 100644 --- a/src/smt/CMakeLists.txt +++ b/src/smt/CMakeLists.txt @@ -13,6 +13,7 @@ z3_add_component(smt old_interval.cpp qi_queue.cpp smt_almost_cg_table.cpp + smt_arith_value.cpp smt_case_split_queue.cpp smt_cg_table.cpp smt_checker.cpp diff --git a/src/smt/smt_arith_value.cpp b/src/smt/smt_arith_value.cpp new file mode 100644 index 000000000..ce4c0d9a9 --- /dev/null +++ b/src/smt/smt_arith_value.cpp @@ -0,0 +1,99 @@ + +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + smt_arith_value.cpp + +Abstract: + + Utility to extract arithmetic values from context. + +Author: + + Nikolaj Bjorner (nbjorner) 2018-12-08. + +Revision History: + +--*/ +#pragma once; + +#include "smt/smt_arith_value.h" +#include "smt/theory_lra.h" +#include "smt/theory_arith.h" + +namespace smt { + + arith_value::arith_value(context& ctx): + m_ctx(ctx), m(ctx.get_manager()), a(m) {} + + bool arith_value::get_lo(expr* e, rational& lo, bool& is_strict) { + if (!m_ctx.e_internalized(e)) return false; + family_id afid = a.get_family_id(); + is_strict = false; + enode* next = m_ctx.get_enode(e), *n = next; + bool found = false; + bool is_strict1; + rational lo1; + theory* th = m_ctx.get_theory(afid); + theory_mi_arith* tha = dynamic_cast(th); + theory_i_arith* thi = dynamic_cast(th); + theory_lra* thr = dynamic_cast(th); + do { + if ((tha && tha->get_lower(next, lo1, is_strict1)) || + (thi && thi->get_lower(next, lo1, is_strict1)) || + (thr && thr->get_lower(next, lo1, is_strict1))) { + if (!found || lo1 > lo || (lo == lo1 && is_strict1)) lo = lo1, is_strict = is_strict1; + found = true; + } + next = next->get_next(); + } + while (n != next); + return found; + } + + bool arith_value::get_up(expr* e, rational& up, bool& is_strict) { + if (!m_ctx.e_internalized(e)) return false; + family_id afid = a.get_family_id(); + is_strict = false; + enode* next = m_ctx.get_enode(e), *n = next; + bool found = false, is_strict1; + rational up1; + theory* th = m_ctx.get_theory(afid); + theory_mi_arith* tha = dynamic_cast(th); + theory_i_arith* thi = dynamic_cast(th); + theory_lra* thr = dynamic_cast(th); + do { + if ((tha && tha->get_upper(next, up1, is_strict1)) || + (thi && thi->get_upper(next, up1, is_strict1)) || + (thr && thr->get_upper(next, up1, is_strict1))) { + if (!found || up1 < up || (up1 == up && is_strict1)) up = up1, is_strict = is_strict1; + found = true; + } + next = next->get_next(); + } + while (n != next); + return found; + } + + bool arith_value::get_value(expr* e, rational& val) { + if (!m_ctx.e_internalized(e)) return false; + expr_ref _val(m); + enode* next = m_ctx.get_enode(e), *n = next; + family_id afid = a.get_family_id(); + theory* th = m_ctx.get_theory(afid); + theory_mi_arith* tha = dynamic_cast(th); + theory_i_arith* thi = dynamic_cast(th); + theory_lra* thr = dynamic_cast(th); + do { + e = next->get_owner(); + if (tha && tha->get_value(next, _val) && a.is_numeral(_val, val)) return true; + if (thi && thi->get_value(next, _val) && a.is_numeral(_val, val)) return true; + if (thr && thr->get_value(next, val)) return true; + next = next->get_next(); + } + while (next != n); + return false; + } +}; diff --git a/src/smt/smt_arith_value.h b/src/smt/smt_arith_value.h new file mode 100644 index 000000000..9b0f833ac --- /dev/null +++ b/src/smt/smt_arith_value.h @@ -0,0 +1,37 @@ + +/*++ +Copyright (c) 2018 Microsoft Corporation + +Module Name: + + smt_arith_value.h + +Abstract: + + Utility to extract arithmetic values from context. + +Author: + + Nikolaj Bjorner (nbjorner) 2018-12-08. + +Revision History: + +--*/ +#pragma once; + +#include "ast/arith_decl_plugin.h" +#include "smt/smt_context.h" + + +namespace smt { + class arith_value { + context& m_ctx; + ast_manager& m; + arith_util a; + public: + arith_value(context& ctx); + bool get_lo(expr* e, rational& lo, bool& strict); + bool get_up(expr* e, rational& up, bool& strict); + bool get_value(expr* e, rational& value); + }; +}; diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index b00c1565c..857691b8b 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -4588,10 +4588,10 @@ bool theory_seq::lower_bound2(expr* _e, rational& lo) { theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); if (!tha) { theory_i_arith* thi = get_th_arith(ctx, m_autil.get_family_id(), e); - if (!thi || !thi->get_lower(ctx.get_enode(e), _lo)) return false; + if (!thi || !thi->get_lower(ctx.get_enode(e), _lo) || !m_autil.is_numeral(_lo, lo)) return false; } enode *ee = ctx.get_enode(e); - if (!tha->get_lower(ee, _lo) || m_autil.is_numeral(_lo, lo)) { + if (tha && (!tha->get_lower(ee, _lo) || m_autil.is_numeral(_lo, lo))) { enode *next = ee->get_next(); bool flag = false; while (next != ee) { From 1a3fe1edd3a9d13d5b3bc672c994f1374c23a226 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 16 Sep 2018 13:43:38 -0700 Subject: [PATCH 055/138] merge Signed-off-by: Nikolaj Bjorner --- src/smt/theory_arith.h | 2 ++ src/smt/theory_arith_core.h | 15 +++++++++++++ src/smt/theory_lra.cpp | 45 ++++++++++++++++++++++++++++--------- src/smt/theory_lra.h | 3 +++ 4 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/smt/theory_arith.h b/src/smt/theory_arith.h index 4b3ee52f6..a2c6c1191 100644 --- a/src/smt/theory_arith.h +++ b/src/smt/theory_arith.h @@ -1071,6 +1071,8 @@ namespace smt { bool get_lower(enode* n, expr_ref& r); bool get_upper(enode* n, expr_ref& r); + bool get_lower(enode* n, rational& r, bool &is_strict); + bool get_upper(enode* n, rational& r, bool &is_strict); bool to_expr(inf_numeral const& val, bool is_int, expr_ref& r); diff --git a/src/smt/theory_arith_core.h b/src/smt/theory_arith_core.h index 67271ad4f..bce029753 100644 --- a/src/smt/theory_arith_core.h +++ b/src/smt/theory_arith_core.h @@ -3302,6 +3302,21 @@ namespace smt { return b && to_expr(b->get_value(), is_int(v), r); } + + template + bool theory_arith::get_lower(enode * n, rational& r, bool& is_strict) { + theory_var v = n->get_th_var(get_id()); + bound* b = (v == null_theory_var) ? nullptr : lower(v); + return b && (r = b->get_value().get_rational().to_rational(), is_strict = b->get_value().get_infinitesimal().is_pos(), true); + } + + template + bool theory_arith::get_upper(enode * n, rational& r, bool& is_strict) { + theory_var v = n->get_th_var(get_id()); + bound* b = (v == null_theory_var) ? nullptr : upper(v); + return b && (r = b->get_value().get_rational().to_rational(), is_strict = b->get_value().get_infinitesimal().is_neg(), true); + } + // ----------------------------------- // // Backtracking diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index f8e2f9fe1..fd2dc0da2 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -2963,13 +2963,22 @@ public: } } - bool get_value(enode* n, expr_ref& r) { + bool get_value(enode* n, rational& val) { theory_var v = n->get_th_var(get_id()); if (!can_get_bound(v)) return false; lp::var_index vi = m_theory_var2var_index[v]; - rational val; if (m_solver->has_value(vi, val)) { if (is_int(n) && !val.is_int()) return false; + return true; + } + else { + return false; + } + } + + bool get_value(enode* n, expr_ref& r) { + rational val; + if (get_value(n, val)) { r = a.mk_numeral(val, is_int(n)); return true; } @@ -2978,7 +2987,7 @@ public: } } - bool get_lower(enode* n, expr_ref& r) { + bool get_lower(enode* n, rational& val, bool& is_strict) { theory_var v = n->get_th_var(get_id()); if (!can_get_bound(v)) { TRACE("arith", tout << "cannot get lower for " << v << "\n";); @@ -2986,29 +2995,36 @@ public: } lp::var_index vi = m_theory_var2var_index[v]; lp::constraint_index ci; - rational val; + return m_solver->has_lower_bound(vi, ci, val, is_strict); + } + + bool get_lower(enode* n, expr_ref& r) { bool is_strict; - if (m_solver->has_lower_bound(vi, ci, val, is_strict)) { + rational val; + if (get_lower(n, val, is_strict) && !is_strict) { r = a.mk_numeral(val, is_int(n)); return true; } - TRACE("arith", m_solver->print_constraints(tout << "does not have lower bound " << vi << "\n");); return false; } - bool get_upper(enode* n, expr_ref& r) { + bool get_upper(enode* n, rational& val, bool& is_strict) { theory_var v = n->get_th_var(get_id()); if (!can_get_bound(v)) return false; lp::var_index vi = m_theory_var2var_index[v]; lp::constraint_index ci; - rational val; + return m_solver->has_upper_bound(vi, ci, val, is_strict); + + } + + bool get_upper(enode* n, expr_ref& r) { bool is_strict; - if (m_solver->has_upper_bound(vi, ci, val, is_strict)) { + rational val; + if (get_upper(n, val, is_strict) && !is_strict) { r = a.mk_numeral(val, is_int(n)); return true; } - TRACE("arith", m_solver->print_constraints(tout << "does not have upper bound " << vi << "\n");); return false; } @@ -3439,6 +3455,9 @@ void theory_lra::init_model(model_generator & m) { model_value_proc * theory_lra::mk_value(enode * n, model_generator & mg) { return m_imp->mk_value(n, mg); } +bool theory_lra::get_value(enode* n, rational& r) { + return m_imp->get_value(n, r); +} bool theory_lra::get_value(enode* n, expr_ref& r) { return m_imp->get_value(n, r); } @@ -3448,6 +3467,12 @@ bool theory_lra::get_lower(enode* n, expr_ref& r) { bool theory_lra::get_upper(enode* n, expr_ref& r) { return m_imp->get_upper(n, r); } +bool theory_lra::get_lower(enode* n, rational& r, bool& is_strict) { + return m_imp->get_lower(n, r, is_strict); +} +bool theory_lra::get_upper(enode* n, rational& r, bool& is_strict) { + return m_imp->get_upper(n, r, is_strict); +} bool theory_lra::validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const { return m_imp->validate_eq_in_model(v1, v2, is_true); diff --git a/src/smt/theory_lra.h b/src/smt/theory_lra.h index 074b11ba7..23adaa557 100644 --- a/src/smt/theory_lra.h +++ b/src/smt/theory_lra.h @@ -78,8 +78,11 @@ namespace smt { model_value_proc * mk_value(enode * n, model_generator & mg) override; bool get_value(enode* n, expr_ref& r) override; + bool get_value(enode* n, rational& r); bool get_lower(enode* n, expr_ref& r); bool get_upper(enode* n, expr_ref& r); + bool get_lower(enode* n, rational& r, bool& is_strict); + bool get_upper(enode* n, rational& r, bool& is_strict); bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const override; From 7e419137b1bda69a816164c40ae61b7e01137dd3 Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Mon, 17 Sep 2018 16:13:34 -0400 Subject: [PATCH 056/138] Z3str3: refactor regex automata to subroutine, use arith_value --- src/smt/smt_arith_value.cpp | 6 + src/smt/smt_arith_value.h | 1 + src/smt/smt_theory.h | 1 + src/smt/theory_str.cpp | 1511 +++++++++++++++++------------------ src/smt/theory_str.h | 2 + 5 files changed, 745 insertions(+), 776 deletions(-) diff --git a/src/smt/smt_arith_value.cpp b/src/smt/smt_arith_value.cpp index ce4c0d9a9..fcd6a3c4f 100644 --- a/src/smt/smt_arith_value.cpp +++ b/src/smt/smt_arith_value.cpp @@ -96,4 +96,10 @@ namespace smt { while (next != n); return false; } + + final_check_status arith_value::final_check() { + family_id afid = a.get_family_id(); + theory * th = m_ctx.get_theory(afid); + return th->final_check_eh(); + } }; diff --git a/src/smt/smt_arith_value.h b/src/smt/smt_arith_value.h index 9b0f833ac..b3748923e 100644 --- a/src/smt/smt_arith_value.h +++ b/src/smt/smt_arith_value.h @@ -33,5 +33,6 @@ namespace smt { bool get_lo(expr* e, rational& lo, bool& strict); bool get_up(expr* e, rational& up, bool& strict); bool get_value(expr* e, rational& value); + final_check_status final_check(); }; }; diff --git a/src/smt/smt_theory.h b/src/smt/smt_theory.h index 9d5a4f49f..b791d890e 100644 --- a/src/smt/smt_theory.h +++ b/src/smt/smt_theory.h @@ -36,6 +36,7 @@ namespace smt { unsigned_vector m_var2enode_lim; friend class context; + friend class arith_value; protected: virtual void init(context * ctx); diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index aadcb63a7..436892a20 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -4835,40 +4835,9 @@ namespace smt { return n; } - // from Z3: theory_seq.cpp - - static theory_mi_arith* get_th_arith(context& ctx, theory_id afid, expr* e) { - theory* th = ctx.get_theory(afid); - if (th && ctx.e_internalized(e)) { - return dynamic_cast(th); - } - else { - return nullptr; - } - } - bool theory_str::get_arith_value(expr* e, rational& val) const { - context& ctx = get_context(); - ast_manager & m = get_manager(); - // safety - if (!ctx.e_internalized(e)) { - return false; - } - // if an integer constant exists in the eqc, it should be the root - enode * en_e = ctx.get_enode(e); - enode * root_e = en_e->get_root(); - if (m_autil.is_numeral(root_e->get_owner(), val) && val.is_int()) { - TRACE("str", tout << mk_pp(e, m) << " ~= " << mk_pp(root_e->get_owner(), m) << std::endl;); - return true; - } else { - TRACE("str", tout << "root of eqc of " << mk_pp(e, m) << " is not a numeral" << std::endl;); - return false; - theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), e); - if (!tha) return false; - expr_ref val_e(m); - if (tha->get_value(root_e, val_e) && m_autil.is_numeral(val_e, val) && val.is_int()) return true; - return false; - } + arith_value v(get_context()); + return v.get_value(e, val); } bool theory_str::lower_bound(expr* _e, rational& lo) { @@ -4877,12 +4846,9 @@ namespace smt { return false; } - context& ctx = get_context(); - ast_manager & m = get_manager(); - theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), _e); - expr_ref _lo(m); - if (!tha || !tha->get_lower(ctx.get_enode(_e), _lo)) return false; - return m_autil.is_numeral(_lo, lo) && lo.is_int(); + arith_value v(get_context()); + bool strict; + return v.get_lo(_e, lo, strict); } bool theory_str::upper_bound(expr* _e, rational& hi) { @@ -4891,12 +4857,9 @@ namespace smt { return false; } - context& ctx = get_context(); - ast_manager & m = get_manager(); - theory_mi_arith* tha = get_th_arith(ctx, m_autil.get_family_id(), _e); - expr_ref _hi(m); - if (!tha || !tha->get_upper(ctx.get_enode(_e), _hi)) return false; - return m_autil.is_numeral(_hi, hi) && hi.is_int(); + arith_value v(get_context()); + bool strict; + return v.get_up(_e, hi, strict); } bool theory_str::get_len_value(expr* e, rational& val) { @@ -4908,17 +4871,6 @@ namespace smt { context& ctx = get_context(); ast_manager & m = get_manager(); - theory* th = ctx.get_theory(m_autil.get_family_id()); - if (!th) { - TRACE("str", tout << "oops, can't get m_autil's theory" << std::endl;); - return false; - } - theory_mi_arith* tha = dynamic_cast(th); - if (!tha) { - TRACE("str", tout << "oops, can't cast to theory_mi_arith" << std::endl;); - return false; - } - TRACE("str", tout << "checking len value of " << mk_ismt2_pp(e, m) << std::endl;); rational val1; @@ -9830,6 +9782,732 @@ namespace smt { } } + void theory_str::solve_regex_automata() { + context & ctx = get_context(); + ast_manager & m = get_manager(); + + // TODO since heuristics might fail, the "no progress" flag might need to be handled specially here + bool regex_axiom_add = false; + for (obj_hashtable::iterator it = regex_terms.begin(); it != regex_terms.end(); ++it) { + expr * str_in_re = *it; + expr * str = nullptr; + expr * re = nullptr; + u.str.is_in_re(str_in_re, str, re); + lbool current_assignment = ctx.get_assignment(str_in_re); + TRACE("str", tout << "regex term: " << mk_pp(str, m) << " in " << mk_pp(re, m) << " : " << current_assignment << std::endl;); + if (current_assignment == l_undef) { + continue; + } + + if (!regex_terms_with_length_constraints.contains(str_in_re)) { + if (current_assignment == l_true && check_regex_length_linearity(re)) { + TRACE("str", tout << "regex length constraints expected to be linear -- generating and asserting them" << std::endl;); + + if (regex_term_to_length_constraint.contains(str_in_re)) { + // use existing length constraint + expr * top_level_length_constraint = nullptr; + regex_term_to_length_constraint.find(str_in_re, top_level_length_constraint); + + ptr_vector extra_length_vars; + regex_term_to_extra_length_vars.find(str_in_re, extra_length_vars); + + assert_axiom(top_level_length_constraint); + for(ptr_vector::iterator it = extra_length_vars.begin(); it != extra_length_vars.end(); ++it) { + expr * v = *it; + refresh_theory_var(v); + expr_ref len_constraint(m_autil.mk_ge(v, m_autil.mk_numeral(rational::zero(), true)), m); + assert_axiom(len_constraint); + } + } else { + // generate new length constraint + expr_ref_vector extra_length_vars(m); + expr_ref _top_level_length_constraint = infer_all_regex_lengths(mk_strlen(str), re, extra_length_vars); + expr_ref top_level_length_constraint(_top_level_length_constraint, m); + th_rewriter rw(m); + rw(top_level_length_constraint); + TRACE("str", tout << "top-level length constraint: " << mk_pp(top_level_length_constraint, m) << std::endl;); + // assert and track length constraint + assert_axiom(top_level_length_constraint); + for(expr_ref_vector::iterator it = extra_length_vars.begin(); it != extra_length_vars.end(); ++it) { + expr * v = *it; + expr_ref len_constraint(m_autil.mk_ge(v, m_autil.mk_numeral(rational::zero(), true)), m); + assert_axiom(len_constraint); + } + + regex_term_to_length_constraint.insert(str_in_re, top_level_length_constraint); + ptr_vector vtmp; + for(expr_ref_vector::iterator it = extra_length_vars.begin(); it != extra_length_vars.end(); ++it) { + vtmp.push_back(*it); + } + regex_term_to_extra_length_vars.insert(str_in_re, vtmp); + } + + regex_terms_with_length_constraints.insert(str_in_re); + m_trail_stack.push(insert_obj_trail(regex_terms_with_length_constraints, str_in_re)); + regex_axiom_add = true; + } + } // re not in regex_terms_with_length_constraints + + rational exact_length_value; + if (get_len_value(str, exact_length_value)) { + TRACE("str", tout << "exact length of " << mk_pp(str, m) << " is " << exact_length_value << std::endl;); + + if (regex_terms_with_path_constraints.contains(str_in_re)) { + TRACE("str", tout << "term " << mk_pp(str_in_re, m) << " already has path constraints set up" << std::endl;); + continue; + } + + // find a consistent automaton for this term + bool found = false; + regex_automaton_under_assumptions assumption; + if (regex_automaton_assumptions.contains(re) && + !regex_automaton_assumptions[re].empty()){ + for (svector::iterator it = regex_automaton_assumptions[re].begin(); + it != regex_automaton_assumptions[re].end(); ++it) { + regex_automaton_under_assumptions autA = *it; + rational assumed_upper_bound, assumed_lower_bound; + bool assumes_upper_bound = autA.get_upper_bound(assumed_upper_bound); + bool assumes_lower_bound = autA.get_lower_bound(assumed_lower_bound); + if (!assumes_upper_bound && !assumes_lower_bound) { + // automaton with no assumptions is always usable + assumption = autA; + found = true; + break; + } + // TODO check consistency of bounds assumptions + } // foreach(a in regex_automaton_assumptions) + } + if (found) { + if (exact_length_value.is_zero()) { + // check consistency of 0-length solution with automaton + eautomaton * aut = assumption.get_automaton(); + bool zero_solution = false; + unsigned initial_state = aut->init(); + if (aut->is_final_state(initial_state)) { + zero_solution = true; + } else { + unsigned_vector eps_states; + aut->get_epsilon_closure(initial_state, eps_states); + for (unsigned_vector::iterator it = eps_states.begin(); it != eps_states.end(); ++it) { + unsigned state = *it; + if (aut->is_final_state(state)) { + zero_solution = true; + break; + } + } + } + + // now check polarity of automaton wrt. original term + if ( (current_assignment == l_true && !assumption.get_polarity()) + || (current_assignment == l_false && assumption.get_polarity())) { + // invert sense + zero_solution = !zero_solution; + } + + if (zero_solution) { + TRACE("str", tout << "zero-length solution OK -- asserting empty path constraint" << std::endl;); + expr_ref_vector lhs_terms(m); + if (current_assignment == l_true) { + lhs_terms.push_back(str_in_re); + } else { + lhs_terms.push_back(m.mk_not(str_in_re)); + } + lhs_terms.push_back(ctx.mk_eq_atom(mk_strlen(str), m_autil.mk_numeral(exact_length_value, true))); + expr_ref lhs(mk_and(lhs_terms), m); + expr_ref rhs(ctx.mk_eq_atom(str, mk_string("")), m); + assert_implication(lhs, rhs); + regex_terms_with_path_constraints.insert(str_in_re); + m_trail_stack.push(insert_obj_trail(regex_terms_with_path_constraints, str_in_re)); + } else { + TRACE("str", tout << "zero-length solution not admitted by this automaton -- asserting conflict clause" << std::endl;); + expr_ref_vector lhs_terms(m); + if (current_assignment == l_true) { + lhs_terms.push_back(str_in_re); + } else { + lhs_terms.push_back(m.mk_not(str_in_re)); + } + lhs_terms.push_back(ctx.mk_eq_atom(mk_strlen(str), m_autil.mk_numeral(exact_length_value, true))); + expr_ref lhs(mk_and(lhs_terms), m); + expr_ref conflict(m.mk_not(lhs), m); + assert_axiom(conflict); + } + regex_axiom_add = true; + regex_inc_counter(regex_length_attempt_count, re); + continue; + } else { + expr_ref pathConstraint(m); + expr_ref characterConstraints(m); + pathConstraint = generate_regex_path_constraints(str, assumption.get_automaton(), exact_length_value, characterConstraints); + TRACE("str", tout << "generated regex path constraint " << mk_pp(pathConstraint, m) << std::endl;); + TRACE("str", tout << "character constraints are " << mk_pp(characterConstraints, m) << std::endl;); + + expr_ref_vector lhs_terms(m); + if (current_assignment == l_true) { + lhs_terms.push_back(str_in_re); + } else { + lhs_terms.push_back(m.mk_not(str_in_re)); + } + lhs_terms.push_back(ctx.mk_eq_atom(mk_strlen(str), m_autil.mk_numeral(exact_length_value, true))); + expr_ref lhs(mk_and(lhs_terms), m); + + // If the path constraint comes out as "false", this means there are no paths of that length + // in the automaton. If the polarity is the same, we can assert a conflict clause. + // If the polarity is opposite, we ignore the path constraint. + + if (m.is_false(pathConstraint)) { + if ( (current_assignment == l_true && assumption.get_polarity()) + || (current_assignment == l_false && !assumption.get_polarity())) { + // automaton and constraint have same polarity -- assert conflict clause + TRACE("str", tout << "path constraint is false with matching polarity; asserting conflict clause" << std::endl;); + expr_ref conflict(m.mk_not(mk_and(lhs_terms)), m); + assert_axiom(conflict); + // don't set up "regex_terms_with_path_constraints" as a conflict clause is not a path constraint + } else { + // automaton and constraint have opposite polarity -- ignore path constraint + TRACE("str", tout << "path constraint is false with opposite polarity; ignoring path constraint" << std::endl;); + assert_implication(lhs, characterConstraints); + regex_terms_with_path_constraints.insert(str_in_re); + m_trail_stack.push(insert_obj_trail(regex_terms_with_path_constraints, str_in_re)); + } + regex_axiom_add = true; + } else { + // If the automaton was built with the same polarity as the constraint, + // assert directly. Otherwise, negate the path constraint + if ( (current_assignment == l_true && assumption.get_polarity()) + || (current_assignment == l_false && !assumption.get_polarity())) { + TRACE("str", tout << "automaton and regex term have same polarity" << std::endl;); + expr_ref rhs(m.mk_and(pathConstraint, characterConstraints), m); + assert_implication(lhs, rhs); + } else { + TRACE("str", tout << "automaton and regex term have opposite polarity" << std::endl;); + expr_ref rhs(m.mk_and(m.mk_not(pathConstraint), characterConstraints), m); + assert_implication(lhs, rhs); + } + regex_terms_with_path_constraints.insert(str_in_re); + m_trail_stack.push(insert_obj_trail(regex_terms_with_path_constraints, str_in_re)); + regex_axiom_add = true; + } + + // increment LengthAttemptCount + regex_inc_counter(regex_length_attempt_count, re); + + TRACE("str", + { + unsigned v = regex_get_counter(regex_length_attempt_count, re); + tout << "length attempt count for " << mk_pp(re, m) << " is " << v << std::endl; + }); + + continue; + } + } else { + // no automata available, or else all bounds assumptions are invalid + unsigned expected_complexity = estimate_regex_complexity(re); + if (expected_complexity <= m_params.m_RegexAutomata_DifficultyThreshold || regex_get_counter(regex_fail_count, str_in_re) >= m_params.m_RegexAutomata_FailedAutomatonThreshold) { + CTRACE("str", regex_get_counter(regex_fail_count, str_in_re) >= m_params.m_RegexAutomata_FailedAutomatonThreshold, + tout << "failed automaton threshold reached for " << mk_pp(str_in_re, m) << " -- automatically constructing full automaton" << std::endl;); + eautomaton * aut = m_mk_aut(re); + aut->compress(); + regex_automata.push_back(aut); + regex_automaton_under_assumptions new_aut(re, aut, true); + if (!regex_automaton_assumptions.contains(re)) { + regex_automaton_assumptions.insert(re, svector()); + } + regex_automaton_assumptions[re].push_back(new_aut); + TRACE("str", tout << "add new automaton for " << mk_pp(re, m) << ": no assumptions" << std::endl;); + regex_axiom_add = true; + find_automaton_initial_bounds(str_in_re, aut); + } else { + regex_inc_counter(regex_fail_count, str_in_re); + } + continue; + } + } // get_len_value() + expr_ref str_len(mk_strlen(str), m); + rational lower_bound_value; + rational upper_bound_value; + bool lower_bound_exists = lower_bound(str_len, lower_bound_value); + bool upper_bound_exists = upper_bound(str_len, upper_bound_value); + CTRACE("str", lower_bound_exists, tout << "lower bound of " << mk_pp(str, m) << " is " << lower_bound_value << std::endl;); + CTRACE("str", upper_bound_exists, tout << "upper bound of " << mk_pp(str, m) << " is " << upper_bound_value << std::endl;); + + bool new_lower_bound_info = true; + bool new_upper_bound_info = true; + // check last seen lower/upper bound to avoid performing duplicate work + if (regex_last_lower_bound.contains(str)) { + rational last_lb_value; + regex_last_lower_bound.find(str, last_lb_value); + if (last_lb_value == lower_bound_value) { + new_lower_bound_info = false; + } + } + if (regex_last_upper_bound.contains(str)) { + rational last_ub_value; + regex_last_upper_bound.find(str, last_ub_value); + if (last_ub_value == upper_bound_value) { + new_upper_bound_info = false; + } + } + + if (new_lower_bound_info) { + regex_last_lower_bound.insert(str, lower_bound_value); + } + if (new_upper_bound_info) { + regex_last_upper_bound.insert(str, upper_bound_value); + } + + if (upper_bound_exists && new_upper_bound_info) { + // check current assumptions + if (regex_automaton_assumptions.contains(re) && + !regex_automaton_assumptions[re].empty()){ + // one or more existing assumptions. + // see if the (current best) upper bound can be refined + // (note that if we have an automaton with no assumption, + // this automatically counts as best) + bool need_assumption = true; + regex_automaton_under_assumptions last_assumption; + rational last_ub = rational::minus_one(); + for (svector::iterator it = regex_automaton_assumptions[re].begin(); + it != regex_automaton_assumptions[re].end(); ++it) { + regex_automaton_under_assumptions autA = *it; + if ((current_assignment == l_true && autA.get_polarity() == false) + || (current_assignment == l_false && autA.get_polarity() == true)) { + // automaton uses incorrect polarity + continue; + } + rational this_ub; + if (autA.get_upper_bound(this_ub)) { + if (last_ub == rational::minus_one() || this_ub < last_ub) { + last_ub = this_ub; + last_assumption = autA; + } + } else { + need_assumption = false; + last_assumption = autA; + break; + } + } + if (!last_ub.is_minus_one() || !need_assumption) { + CTRACE("str", !need_assumption, tout << "using automaton with full length information" << std::endl;); + CTRACE("str", need_assumption, tout << "using automaton with assumed upper bound of " << last_ub << std::endl;); + + rational refined_upper_bound; + bool solution_at_upper_bound = refine_automaton_upper_bound(last_assumption.get_automaton(), + upper_bound_value, refined_upper_bound); + TRACE("str", tout << "refined upper bound is " << refined_upper_bound << + (solution_at_upper_bound?", solution at upper bound":", no solution at upper bound") << std::endl;); + + expr_ref_vector lhs(m); + if (current_assignment == l_false) { + lhs.push_back(m.mk_not(str_in_re)); + } else { + lhs.push_back(str_in_re); + } + if (need_assumption) { + lhs.push_back(m_autil.mk_le(str_len, m_autil.mk_numeral(last_ub, true))); + } + lhs.push_back(m_autil.mk_le(str_len, m_autil.mk_numeral(upper_bound_value, true))); + + expr_ref_vector rhs(m); + + if (solution_at_upper_bound) { + if (refined_upper_bound.is_minus_one()) { + // If there are solutions at the upper bound but not below it, make the bound exact. + rhs.push_back(ctx.mk_eq_atom(str_len, m_autil.mk_numeral(upper_bound_value, true))); + } else { + // If there are solutions at and below the upper bound, add an additional bound. + rhs.push_back(m.mk_or( + ctx.mk_eq_atom(str_len, m_autil.mk_numeral(upper_bound_value, true)), + m_autil.mk_le(str_len, m_autil.mk_numeral(refined_upper_bound, true)) + )); + } + } else { + if (refined_upper_bound.is_minus_one()) { + // If there are no solutions at or below the upper bound, assert a conflict clause. + rhs.push_back(m.mk_not(m_autil.mk_le(str_len, m_autil.mk_numeral(upper_bound_value, true)))); + } else { + // If there are solutions below the upper bound but not at it, refine the bound. + rhs.push_back(m_autil.mk_le(str_len, m_autil.mk_numeral(refined_upper_bound, true))); + } + } + + if (!rhs.empty()) { + expr_ref lhs_terms(mk_and(lhs), m); + expr_ref rhs_terms(mk_and(rhs), m); + assert_implication(lhs_terms, rhs_terms); + regex_axiom_add = true; + } + } + } else { + // no existing automata/assumptions. + // if it's easy to construct a full automaton for R, do so + unsigned expected_complexity = estimate_regex_complexity(re); + bool failureThresholdExceeded = (regex_get_counter(regex_fail_count, str_in_re) >= m_params.m_RegexAutomata_FailedAutomatonThreshold); + if (expected_complexity <= m_params.m_RegexAutomata_DifficultyThreshold || failureThresholdExceeded) { + eautomaton * aut = m_mk_aut(re); + aut->compress(); + regex_automata.push_back(aut); + regex_automaton_under_assumptions new_aut(re, aut, true); + if (!regex_automaton_assumptions.contains(re)) { + regex_automaton_assumptions.insert(re, svector()); + } + regex_automaton_assumptions[re].push_back(new_aut); + TRACE("str", tout << "add new automaton for " << mk_pp(re, m) << ": no assumptions" << std::endl;); + regex_axiom_add = true; + find_automaton_initial_bounds(str_in_re, aut); + } else { + // TODO check negation? + // TODO construct a partial automaton for R to the given upper bound? + if (false) { + + } else { + regex_inc_counter(regex_fail_count, str_in_re); + } + } + continue; + } + // if we have *any* automaton for R, and the upper bound is not too large, + // finitize the automaton (if we have not already done so) and assert all solutions + if (upper_bound_value < 50) { // TODO better metric for threshold + // NOT_IMPLEMENTED_YET(); // TODO(mtrberzi) + } + } else { // !upper_bound_exists + // no upper bound information + if (lower_bound_exists && !lower_bound_value.is_zero() && new_lower_bound_info) { + // nonzero lower bound, no upper bound + + // check current assumptions + if (regex_automaton_assumptions.contains(re) && + !regex_automaton_assumptions[re].empty()){ + // one or more existing assumptions. + // see if the (current best) lower bound can be refined + // (note that if we have an automaton with no assumption, + // this automatically counts as best) + bool need_assumption = true; + regex_automaton_under_assumptions last_assumption; + rational last_lb = rational::zero(); // the default + for (svector::iterator it = regex_automaton_assumptions[re].begin(); + it != regex_automaton_assumptions[re].end(); ++it) { + regex_automaton_under_assumptions autA = *it; + if ((current_assignment == l_true && autA.get_polarity() == false) + || (current_assignment == l_false && autA.get_polarity() == true)) { + // automaton uses incorrect polarity + continue; + } + rational this_lb; + if (autA.get_lower_bound(this_lb)) { + if (this_lb > last_lb) { + last_lb = this_lb; + last_assumption = autA; + } + } else { + need_assumption = false; + last_assumption = autA; + break; + } + } + if (!last_lb.is_zero() || !need_assumption) { + CTRACE("str", !need_assumption, tout << "using automaton with full length information" << std::endl;); + CTRACE("str", need_assumption, tout << "using automaton with assumed lower bound of " << last_lb << std::endl;); + rational refined_lower_bound; + bool solution_at_lower_bound = refine_automaton_lower_bound(last_assumption.get_automaton(), + lower_bound_value, refined_lower_bound); + TRACE("str", tout << "refined lower bound is " << refined_lower_bound << + (solution_at_lower_bound?", solution at lower bound":", no solution at lower bound") << std::endl;); + + expr_ref_vector lhs(m); + if (current_assignment == l_false) { + lhs.push_back(m.mk_not(str_in_re)); + } else { + lhs.push_back(str_in_re); + } + if (need_assumption) { + lhs.push_back(m_autil.mk_ge(str_len, m_autil.mk_numeral(last_lb, true))); + } + lhs.push_back(m_autil.mk_ge(str_len, m_autil.mk_numeral(lower_bound_value, true))); + + expr_ref_vector rhs(m); + + if (solution_at_lower_bound) { + if (refined_lower_bound.is_minus_one()) { + // If there are solutions at the lower bound but not above it, make the bound exact. + rhs.push_back(ctx.mk_eq_atom(str_len, m_autil.mk_numeral(lower_bound_value, true))); + } else { + // If there are solutions at and above the lower bound, add an additional bound. + // DISABLED as this is causing non-termination in the integer solver. --mtrberzi + /* + rhs.push_back(m.mk_or( + ctx.mk_eq_atom(str_len, m_autil.mk_numeral(lower_bound_value, true)), + m_autil.mk_ge(str_len, m_autil.mk_numeral(refined_lower_bound, true)) + )); + */ + } + } else { + if (refined_lower_bound.is_minus_one()) { + // If there are no solutions at or above the lower bound, assert a conflict clause. + rhs.push_back(m.mk_not(m_autil.mk_ge(str_len, m_autil.mk_numeral(lower_bound_value, true)))); + } else { + // If there are solutions above the lower bound but not at it, refine the bound. + rhs.push_back(m_autil.mk_ge(str_len, m_autil.mk_numeral(refined_lower_bound, true))); + } + } + + if (!rhs.empty()) { + expr_ref lhs_terms(mk_and(lhs), m); + expr_ref rhs_terms(mk_and(rhs), m); + assert_implication(lhs_terms, rhs_terms); + regex_axiom_add = true; + } + } + } else { + // no existing automata/assumptions. + // if it's easy to construct a full automaton for R, do so + unsigned expected_complexity = estimate_regex_complexity(re); + bool failureThresholdExceeded = (regex_get_counter(regex_fail_count, str_in_re) >= m_params.m_RegexAutomata_FailedAutomatonThreshold); + if (expected_complexity <= m_params.m_RegexAutomata_DifficultyThreshold || failureThresholdExceeded) { + eautomaton * aut = m_mk_aut(re); + aut->compress(); + regex_automata.push_back(aut); + regex_automaton_under_assumptions new_aut(re, aut, true); + if (!regex_automaton_assumptions.contains(re)) { + regex_automaton_assumptions.insert(re, svector()); + } + regex_automaton_assumptions[re].push_back(new_aut); + TRACE("str", tout << "add new automaton for " << mk_pp(re, m) << ": no assumptions" << std::endl;); + regex_axiom_add = true; + find_automaton_initial_bounds(str_in_re, aut); + } else { + // TODO check negation? + // TODO construct a partial automaton for R to the given lower bound? + if (false) { + + } else { + regex_inc_counter(regex_fail_count, str_in_re); + } + } + continue; + } + } else { // !lower_bound_exists + // no bounds information + // check for existing automata; + // try to construct an automaton if we don't have one yet + // and doing so without bounds is not difficult + bool existingAutomata = (regex_automaton_assumptions.contains(re) && !regex_automaton_assumptions[re].empty()); + bool failureThresholdExceeded = (regex_get_counter(regex_fail_count, str_in_re) >= m_params.m_RegexAutomata_FailedAutomatonThreshold); + if (!existingAutomata) { + unsigned expected_complexity = estimate_regex_complexity(re); + if (expected_complexity <= m_params.m_RegexAutomata_DifficultyThreshold + || failureThresholdExceeded) { + eautomaton * aut = m_mk_aut(re); + aut->compress(); + regex_automata.push_back(aut); + regex_automaton_under_assumptions new_aut(re, aut, true); + if (!regex_automaton_assumptions.contains(re)) { + regex_automaton_assumptions.insert(re, svector()); + } + regex_automaton_assumptions[re].push_back(new_aut); + TRACE("str", tout << "add new automaton for " << mk_pp(re, m) << ": no assumptions" << std::endl;); + regex_axiom_add = true; + find_automaton_initial_bounds(str_in_re, aut); + } else { + regex_inc_counter(regex_fail_count, str_in_re); + } + } else { + regex_inc_counter(regex_fail_count, str_in_re); + } + } + } + } // foreach (entry in regex_terms) + + for (obj_map >::iterator it = regex_terms_by_string.begin(); + it != regex_terms_by_string.end(); ++it) { + // TODO do we need to check equivalence classes of strings here? + + expr * str = it->m_key; + ptr_vector str_in_re_terms = it->m_value; + + svector intersect_constraints; + // we may find empty intersection before checking every constraint; + // this vector keeps track of which ones actually take part in intersection + svector used_intersect_constraints; + + // choose an automaton/assumption for each assigned (str.in.re) + // that's consistent with the current length information + for (ptr_vector::iterator term_it = str_in_re_terms.begin(); + term_it != str_in_re_terms.end(); ++term_it) { + expr * _unused = nullptr; + expr * re = nullptr; + SASSERT(u.str.is_in_re(*term_it)); + u.str.is_in_re(*term_it, _unused, re); + + rational exact_len; + bool has_exact_len = get_len_value(str, exact_len); + + rational lb, ub; + bool has_lower_bound = lower_bound(mk_strlen(str), lb); + bool has_upper_bound = upper_bound(mk_strlen(str), ub); + + if (regex_automaton_assumptions.contains(re) && + !regex_automaton_assumptions[re].empty()){ + for (svector::iterator aut_it = regex_automaton_assumptions[re].begin(); + aut_it != regex_automaton_assumptions[re].end(); ++aut_it) { + regex_automaton_under_assumptions aut = *aut_it; + rational aut_ub; + bool assume_ub = aut.get_upper_bound(aut_ub); + rational aut_lb; + bool assume_lb = aut.get_lower_bound(aut_lb); + bool consistent = true; + + if (assume_ub) { + // check consistency of assumed upper bound + if (has_exact_len) { + if (exact_len > aut_ub) { + consistent = false; + } + } else { + if (has_upper_bound && ub > aut_ub) { + consistent = false; + } + } + } + + if (assume_lb) { + // check consistency of assumed lower bound + if (has_exact_len) { + if (exact_len < aut_lb) { + consistent = false; + } + } else { + if (has_lower_bound && lb < aut_lb) { + consistent = false; + } + } + } + + if (consistent) { + intersect_constraints.push_back(aut); + break; + } + } + } + } // foreach(term in str_in_re_terms) + + eautomaton * aut_inter = NULL; + CTRACE("str", !intersect_constraints.empty(), tout << "check intersection of automata constraints for " << mk_pp(str, m) << std::endl;); + for (svector::iterator aut_it = intersect_constraints.begin(); + aut_it != intersect_constraints.end(); ++aut_it) { + regex_automaton_under_assumptions aut = *aut_it; + if (aut_inter == NULL) { + // start somewhere + aut_inter = aut.get_automaton(); + used_intersect_constraints.push_back(aut); + continue; + } + + TRACE("str", + { + unsigned v = regex_get_counter(regex_length_attempt_count, aut.get_regex_term()); + tout << "length attempt count of " << mk_pp(aut.get_regex_term(), m) << " is " << v + << ", threshold is " << m_params.m_RegexAutomata_LengthAttemptThreshold << std::endl; + }); + + if (regex_get_counter(regex_length_attempt_count, aut.get_regex_term()) >= m_params.m_RegexAutomata_LengthAttemptThreshold) { + unsigned intersectionDifficulty = estimate_automata_intersection_difficulty(aut_inter, aut.get_automaton()); + TRACE("str", tout << "intersection difficulty is " << intersectionDifficulty << std::endl;); + if (intersectionDifficulty <= m_params.m_RegexAutomata_IntersectionDifficultyThreshold + || regex_get_counter(regex_intersection_fail_count, aut.get_regex_term()) >= m_params.m_RegexAutomata_FailedIntersectionThreshold) { + + expr * str_in_re_term(u.re.mk_in_re(str, aut.get_regex_term())); + lbool current_assignment = ctx.get_assignment(str_in_re_term); + // if the assignment is consistent with our assumption, use the automaton directly; + // otherwise, complement it (and save that automaton for next time) + // TODO we should cache these intermediate results + // TODO do we need to push the intermediates into a vector for deletion anyway? + if ( (current_assignment == l_true && aut.get_polarity()) + || (current_assignment == l_false && !aut.get_polarity())) { + aut_inter = m_mk_aut.mk_product(aut_inter, aut.get_automaton()); + m_automata.push_back(aut_inter); + } else { + // need to complement first + expr_ref rc(u.re.mk_complement(aut.get_regex_term()), m); + eautomaton * aut_c = m_mk_aut(rc); + regex_automata.push_back(aut_c); + // TODO is there any way to build a complement automaton from an existing one? + // this discards length information + aut_inter = m_mk_aut.mk_product(aut_inter, aut_c); + m_automata.push_back(aut_inter); + } + used_intersect_constraints.push_back(aut); + if (aut_inter->is_empty()) { + break; + } + } else { + // failed intersection + regex_inc_counter(regex_intersection_fail_count, aut.get_regex_term()); + } + } + } // foreach(entry in intersect_constraints) + if (aut_inter != NULL) { + aut_inter->compress(); + } + TRACE("str", tout << "intersected " << used_intersect_constraints.size() << " constraints" << std::endl;); + + expr_ref_vector conflict_terms(m); + expr_ref conflict_lhs(m); + for (svector::iterator aut_it = used_intersect_constraints.begin(); + aut_it != used_intersect_constraints.end(); ++aut_it) { + regex_automaton_under_assumptions aut = *aut_it; + expr * str_in_re_term(u.re.mk_in_re(str, aut.get_regex_term())); + lbool current_assignment = ctx.get_assignment(str_in_re_term); + if (current_assignment == l_true) { + conflict_terms.push_back(str_in_re_term); + } else if (current_assignment == l_false) { + conflict_terms.push_back(m.mk_not(str_in_re_term)); + } + // add length assumptions, if any + rational ub; + if (aut.get_upper_bound(ub)) { + expr_ref ub_term(m_autil.mk_le(mk_strlen(str), m_autil.mk_numeral(ub, true)), m); + conflict_terms.push_back(ub_term); + } + rational lb; + if (aut.get_lower_bound(lb)) { + expr_ref lb_term(m_autil.mk_ge(mk_strlen(str), m_autil.mk_numeral(lb, true)), m); + conflict_terms.push_back(lb_term); + } + } + conflict_lhs = mk_and(conflict_terms); + + if (used_intersect_constraints.size() > 1 && aut_inter != NULL) { + // check whether the intersection is only the empty string + unsigned initial_state = aut_inter->init(); + if (aut_inter->final_states().size() == 1 && aut_inter->is_final_state(initial_state)) { + // initial state is final and it is the only final state + // if there are no moves from the initial state, + // the only solution is the empty string + if (aut_inter->get_moves_from(initial_state).empty()) { + TRACE("str", tout << "product automaton only accepts empty string" << std::endl;); + expr_ref rhs1(ctx.mk_eq_atom(str, mk_string("")), m); + expr_ref rhs2(ctx.mk_eq_atom(mk_strlen(str), m_autil.mk_numeral(rational::zero(), true)), m); + expr_ref rhs(m.mk_and(rhs1, rhs2), m); + assert_implication(conflict_lhs, rhs); + regex_axiom_add = true; + } + } + } + + if (aut_inter != NULL && aut_inter->is_empty()) { + TRACE("str", tout << "product automaton is empty; asserting conflict clause" << std::endl;); + expr_ref conflict_clause(m.mk_not(mk_and(conflict_terms)), m); + assert_axiom(conflict_clause); + add_persisted_axiom(conflict_clause); + regex_axiom_add = true; + } + } // foreach (entry in regex_terms_by_string) + if (regex_axiom_add) { + //return FC_CONTINUE; + } + } + final_check_status theory_str::final_check_eh() { context & ctx = get_context(); ast_manager & m = get_manager(); @@ -9977,726 +10655,7 @@ namespace smt { // regex automata if (m_params.m_RegexAutomata) { - // TODO since heuristics might fail, the "no progress" flag might need to be handled specially here - bool regex_axiom_add = false; - for (obj_hashtable::iterator it = regex_terms.begin(); it != regex_terms.end(); ++it) { - expr * str_in_re = *it; - expr * str = nullptr; - expr * re = nullptr; - u.str.is_in_re(str_in_re, str, re); - lbool current_assignment = ctx.get_assignment(str_in_re); - TRACE("str", tout << "regex term: " << mk_pp(str, m) << " in " << mk_pp(re, m) << " : " << current_assignment << std::endl;); - if (current_assignment == l_undef) { - continue; - } - - if (!regex_terms_with_length_constraints.contains(str_in_re)) { - if (current_assignment == l_true && check_regex_length_linearity(re)) { - TRACE("str", tout << "regex length constraints expected to be linear -- generating and asserting them" << std::endl;); - - if (regex_term_to_length_constraint.contains(str_in_re)) { - // use existing length constraint - expr * top_level_length_constraint = nullptr; - regex_term_to_length_constraint.find(str_in_re, top_level_length_constraint); - - ptr_vector extra_length_vars; - regex_term_to_extra_length_vars.find(str_in_re, extra_length_vars); - - assert_axiom(top_level_length_constraint); - for(ptr_vector::iterator it = extra_length_vars.begin(); it != extra_length_vars.end(); ++it) { - expr * v = *it; - refresh_theory_var(v); - expr_ref len_constraint(m_autil.mk_ge(v, m_autil.mk_numeral(rational::zero(), true)), m); - assert_axiom(len_constraint); - } - } else { - // generate new length constraint - expr_ref_vector extra_length_vars(m); - expr_ref _top_level_length_constraint = infer_all_regex_lengths(mk_strlen(str), re, extra_length_vars); - expr_ref top_level_length_constraint(_top_level_length_constraint, m); - th_rewriter rw(m); - rw(top_level_length_constraint); - TRACE("str", tout << "top-level length constraint: " << mk_pp(top_level_length_constraint, m) << std::endl;); - // assert and track length constraint - assert_axiom(top_level_length_constraint); - for(expr_ref_vector::iterator it = extra_length_vars.begin(); it != extra_length_vars.end(); ++it) { - expr * v = *it; - expr_ref len_constraint(m_autil.mk_ge(v, m_autil.mk_numeral(rational::zero(), true)), m); - assert_axiom(len_constraint); - } - - regex_term_to_length_constraint.insert(str_in_re, top_level_length_constraint); - ptr_vector vtmp; - for(expr_ref_vector::iterator it = extra_length_vars.begin(); it != extra_length_vars.end(); ++it) { - vtmp.push_back(*it); - } - regex_term_to_extra_length_vars.insert(str_in_re, vtmp); - } - - regex_terms_with_length_constraints.insert(str_in_re); - m_trail_stack.push(insert_obj_trail(regex_terms_with_length_constraints, str_in_re)); - regex_axiom_add = true; - } - } // re not in regex_terms_with_length_constraints - - rational exact_length_value; - if (get_len_value(str, exact_length_value)) { - TRACE("str", tout << "exact length of " << mk_pp(str, m) << " is " << exact_length_value << std::endl;); - - if (regex_terms_with_path_constraints.contains(str_in_re)) { - TRACE("str", tout << "term " << mk_pp(str_in_re, m) << " already has path constraints set up" << std::endl;); - continue; - } - - // find a consistent automaton for this term - bool found = false; - regex_automaton_under_assumptions assumption; - if (regex_automaton_assumptions.contains(re) && - !regex_automaton_assumptions[re].empty()){ - for (svector::iterator it = regex_automaton_assumptions[re].begin(); - it != regex_automaton_assumptions[re].end(); ++it) { - regex_automaton_under_assumptions autA = *it; - rational assumed_upper_bound, assumed_lower_bound; - bool assumes_upper_bound = autA.get_upper_bound(assumed_upper_bound); - bool assumes_lower_bound = autA.get_lower_bound(assumed_lower_bound); - if (!assumes_upper_bound && !assumes_lower_bound) { - // automaton with no assumptions is always usable - assumption = autA; - found = true; - break; - } - // TODO check consistency of bounds assumptions - } // foreach(a in regex_automaton_assumptions) - } - if (found) { - if (exact_length_value.is_zero()) { - // check consistency of 0-length solution with automaton - eautomaton * aut = assumption.get_automaton(); - bool zero_solution = false; - unsigned initial_state = aut->init(); - if (aut->is_final_state(initial_state)) { - zero_solution = true; - } else { - unsigned_vector eps_states; - aut->get_epsilon_closure(initial_state, eps_states); - for (unsigned_vector::iterator it = eps_states.begin(); it != eps_states.end(); ++it) { - unsigned state = *it; - if (aut->is_final_state(state)) { - zero_solution = true; - break; - } - } - } - - // now check polarity of automaton wrt. original term - if ( (current_assignment == l_true && !assumption.get_polarity()) - || (current_assignment == l_false && assumption.get_polarity())) { - // invert sense - zero_solution = !zero_solution; - } - - if (zero_solution) { - TRACE("str", tout << "zero-length solution OK -- asserting empty path constraint" << std::endl;); - expr_ref_vector lhs_terms(m); - if (current_assignment == l_true) { - lhs_terms.push_back(str_in_re); - } else { - lhs_terms.push_back(m.mk_not(str_in_re)); - } - lhs_terms.push_back(ctx.mk_eq_atom(mk_strlen(str), m_autil.mk_numeral(exact_length_value, true))); - expr_ref lhs(mk_and(lhs_terms), m); - expr_ref rhs(ctx.mk_eq_atom(str, mk_string("")), m); - assert_implication(lhs, rhs); - regex_terms_with_path_constraints.insert(str_in_re); - m_trail_stack.push(insert_obj_trail(regex_terms_with_path_constraints, str_in_re)); - } else { - TRACE("str", tout << "zero-length solution not admitted by this automaton -- asserting conflict clause" << std::endl;); - expr_ref_vector lhs_terms(m); - if (current_assignment == l_true) { - lhs_terms.push_back(str_in_re); - } else { - lhs_terms.push_back(m.mk_not(str_in_re)); - } - lhs_terms.push_back(ctx.mk_eq_atom(mk_strlen(str), m_autil.mk_numeral(exact_length_value, true))); - expr_ref lhs(mk_and(lhs_terms), m); - expr_ref conflict(m.mk_not(lhs), m); - assert_axiom(conflict); - } - regex_axiom_add = true; - regex_inc_counter(regex_length_attempt_count, re); - continue; - } else { - expr_ref pathConstraint(m); - expr_ref characterConstraints(m); - pathConstraint = generate_regex_path_constraints(str, assumption.get_automaton(), exact_length_value, characterConstraints); - TRACE("str", tout << "generated regex path constraint " << mk_pp(pathConstraint, m) << std::endl;); - TRACE("str", tout << "character constraints are " << mk_pp(characterConstraints, m) << std::endl;); - - expr_ref_vector lhs_terms(m); - if (current_assignment == l_true) { - lhs_terms.push_back(str_in_re); - } else { - lhs_terms.push_back(m.mk_not(str_in_re)); - } - lhs_terms.push_back(ctx.mk_eq_atom(mk_strlen(str), m_autil.mk_numeral(exact_length_value, true))); - expr_ref lhs(mk_and(lhs_terms), m); - - // If the path constraint comes out as "false", this means there are no paths of that length - // in the automaton. If the polarity is the same, we can assert a conflict clause. - // If the polarity is opposite, we ignore the path constraint. - - if (m.is_false(pathConstraint)) { - if ( (current_assignment == l_true && assumption.get_polarity()) - || (current_assignment == l_false && !assumption.get_polarity())) { - // automaton and constraint have same polarity -- assert conflict clause - TRACE("str", tout << "path constraint is false with matching polarity; asserting conflict clause" << std::endl;); - expr_ref conflict(m.mk_not(mk_and(lhs_terms)), m); - assert_axiom(conflict); - // don't set up "regex_terms_with_path_constraints" as a conflict clause is not a path constraint - } else { - // automaton and constraint have opposite polarity -- ignore path constraint - TRACE("str", tout << "path constraint is false with opposite polarity; ignoring path constraint" << std::endl;); - assert_implication(lhs, characterConstraints); - regex_terms_with_path_constraints.insert(str_in_re); - m_trail_stack.push(insert_obj_trail(regex_terms_with_path_constraints, str_in_re)); - } - regex_axiom_add = true; - } else { - // If the automaton was built with the same polarity as the constraint, - // assert directly. Otherwise, negate the path constraint - if ( (current_assignment == l_true && assumption.get_polarity()) - || (current_assignment == l_false && !assumption.get_polarity())) { - TRACE("str", tout << "automaton and regex term have same polarity" << std::endl;); - expr_ref rhs(m.mk_and(pathConstraint, characterConstraints), m); - assert_implication(lhs, rhs); - } else { - TRACE("str", tout << "automaton and regex term have opposite polarity" << std::endl;); - expr_ref rhs(m.mk_and(m.mk_not(pathConstraint), characterConstraints), m); - assert_implication(lhs, rhs); - } - regex_terms_with_path_constraints.insert(str_in_re); - m_trail_stack.push(insert_obj_trail(regex_terms_with_path_constraints, str_in_re)); - regex_axiom_add = true; - } - - // increment LengthAttemptCount - regex_inc_counter(regex_length_attempt_count, re); - - TRACE("str", - { - unsigned v = regex_get_counter(regex_length_attempt_count, re); - tout << "length attempt count for " << mk_pp(re, m) << " is " << v << std::endl; - }); - - continue; - } - } else { - // no automata available, or else all bounds assumptions are invalid - unsigned expected_complexity = estimate_regex_complexity(re); - if (expected_complexity <= m_params.m_RegexAutomata_DifficultyThreshold || regex_get_counter(regex_fail_count, str_in_re) >= m_params.m_RegexAutomata_FailedAutomatonThreshold) { - CTRACE("str", regex_get_counter(regex_fail_count, str_in_re) >= m_params.m_RegexAutomata_FailedAutomatonThreshold, - tout << "failed automaton threshold reached for " << mk_pp(str_in_re, m) << " -- automatically constructing full automaton" << std::endl;); - eautomaton * aut = m_mk_aut(re); - aut->compress(); - regex_automata.push_back(aut); - regex_automaton_under_assumptions new_aut(re, aut, true); - if (!regex_automaton_assumptions.contains(re)) { - regex_automaton_assumptions.insert(re, svector()); - } - regex_automaton_assumptions[re].push_back(new_aut); - TRACE("str", tout << "add new automaton for " << mk_pp(re, m) << ": no assumptions" << std::endl;); - regex_axiom_add = true; - find_automaton_initial_bounds(str_in_re, aut); - } else { - regex_inc_counter(regex_fail_count, str_in_re); - } - continue; - } - } // get_len_value() - expr_ref str_len(mk_strlen(str), m); - rational lower_bound_value; - rational upper_bound_value; - bool lower_bound_exists = lower_bound(str_len, lower_bound_value); - bool upper_bound_exists = upper_bound(str_len, upper_bound_value); - CTRACE("str", lower_bound_exists, tout << "lower bound of " << mk_pp(str, m) << " is " << lower_bound_value << std::endl;); - CTRACE("str", upper_bound_exists, tout << "upper bound of " << mk_pp(str, m) << " is " << upper_bound_value << std::endl;); - - bool new_lower_bound_info = true; - bool new_upper_bound_info = true; - // check last seen lower/upper bound to avoid performing duplicate work - if (regex_last_lower_bound.contains(str)) { - rational last_lb_value; - regex_last_lower_bound.find(str, last_lb_value); - if (last_lb_value == lower_bound_value) { - new_lower_bound_info = false; - } - } - if (regex_last_upper_bound.contains(str)) { - rational last_ub_value; - regex_last_upper_bound.find(str, last_ub_value); - if (last_ub_value == upper_bound_value) { - new_upper_bound_info = false; - } - } - - if (new_lower_bound_info) { - regex_last_lower_bound.insert(str, lower_bound_value); - } - if (new_upper_bound_info) { - regex_last_upper_bound.insert(str, upper_bound_value); - } - - if (upper_bound_exists && new_upper_bound_info) { - // check current assumptions - if (regex_automaton_assumptions.contains(re) && - !regex_automaton_assumptions[re].empty()){ - // one or more existing assumptions. - // see if the (current best) upper bound can be refined - // (note that if we have an automaton with no assumption, - // this automatically counts as best) - bool need_assumption = true; - regex_automaton_under_assumptions last_assumption; - rational last_ub = rational::minus_one(); - for (svector::iterator it = regex_automaton_assumptions[re].begin(); - it != regex_automaton_assumptions[re].end(); ++it) { - regex_automaton_under_assumptions autA = *it; - if ((current_assignment == l_true && autA.get_polarity() == false) - || (current_assignment == l_false && autA.get_polarity() == true)) { - // automaton uses incorrect polarity - continue; - } - rational this_ub; - if (autA.get_upper_bound(this_ub)) { - if (last_ub == rational::minus_one() || this_ub < last_ub) { - last_ub = this_ub; - last_assumption = autA; - } - } else { - need_assumption = false; - last_assumption = autA; - break; - } - } - if (!last_ub.is_minus_one() || !need_assumption) { - CTRACE("str", !need_assumption, tout << "using automaton with full length information" << std::endl;); - CTRACE("str", need_assumption, tout << "using automaton with assumed upper bound of " << last_ub << std::endl;); - - rational refined_upper_bound; - bool solution_at_upper_bound = refine_automaton_upper_bound(last_assumption.get_automaton(), - upper_bound_value, refined_upper_bound); - TRACE("str", tout << "refined upper bound is " << refined_upper_bound << - (solution_at_upper_bound?", solution at upper bound":", no solution at upper bound") << std::endl;); - - expr_ref_vector lhs(m); - if (current_assignment == l_false) { - lhs.push_back(m.mk_not(str_in_re)); - } else { - lhs.push_back(str_in_re); - } - if (need_assumption) { - lhs.push_back(m_autil.mk_le(str_len, m_autil.mk_numeral(last_ub, true))); - } - lhs.push_back(m_autil.mk_le(str_len, m_autil.mk_numeral(upper_bound_value, true))); - - expr_ref_vector rhs(m); - - if (solution_at_upper_bound) { - if (refined_upper_bound.is_minus_one()) { - // If there are solutions at the upper bound but not below it, make the bound exact. - rhs.push_back(ctx.mk_eq_atom(str_len, m_autil.mk_numeral(upper_bound_value, true))); - } else { - // If there are solutions at and below the upper bound, add an additional bound. - rhs.push_back(m.mk_or( - ctx.mk_eq_atom(str_len, m_autil.mk_numeral(upper_bound_value, true)), - m_autil.mk_le(str_len, m_autil.mk_numeral(refined_upper_bound, true)) - )); - } - } else { - if (refined_upper_bound.is_minus_one()) { - // If there are no solutions at or below the upper bound, assert a conflict clause. - rhs.push_back(m.mk_not(m_autil.mk_le(str_len, m_autil.mk_numeral(upper_bound_value, true)))); - } else { - // If there are solutions below the upper bound but not at it, refine the bound. - rhs.push_back(m_autil.mk_le(str_len, m_autil.mk_numeral(refined_upper_bound, true))); - } - } - - if (!rhs.empty()) { - expr_ref lhs_terms(mk_and(lhs), m); - expr_ref rhs_terms(mk_and(rhs), m); - assert_implication(lhs_terms, rhs_terms); - regex_axiom_add = true; - } - } - } else { - // no existing automata/assumptions. - // if it's easy to construct a full automaton for R, do so - unsigned expected_complexity = estimate_regex_complexity(re); - bool failureThresholdExceeded = (regex_get_counter(regex_fail_count, str_in_re) >= m_params.m_RegexAutomata_FailedAutomatonThreshold); - if (expected_complexity <= m_params.m_RegexAutomata_DifficultyThreshold || failureThresholdExceeded) { - eautomaton * aut = m_mk_aut(re); - aut->compress(); - regex_automata.push_back(aut); - regex_automaton_under_assumptions new_aut(re, aut, true); - if (!regex_automaton_assumptions.contains(re)) { - regex_automaton_assumptions.insert(re, svector()); - } - regex_automaton_assumptions[re].push_back(new_aut); - TRACE("str", tout << "add new automaton for " << mk_pp(re, m) << ": no assumptions" << std::endl;); - regex_axiom_add = true; - find_automaton_initial_bounds(str_in_re, aut); - } else { - // TODO check negation? - // TODO construct a partial automaton for R to the given upper bound? - if (false) { - - } else { - regex_inc_counter(regex_fail_count, str_in_re); - } - } - continue; - } - // if we have *any* automaton for R, and the upper bound is not too large, - // finitize the automaton (if we have not already done so) and assert all solutions - if (upper_bound_value < 50) { // TODO better metric for threshold - // NOT_IMPLEMENTED_YET(); // TODO(mtrberzi) - } - } else { // !upper_bound_exists - // no upper bound information - if (lower_bound_exists && !lower_bound_value.is_zero() && new_lower_bound_info) { - // nonzero lower bound, no upper bound - - // check current assumptions - if (regex_automaton_assumptions.contains(re) && - !regex_automaton_assumptions[re].empty()){ - // one or more existing assumptions. - // see if the (current best) lower bound can be refined - // (note that if we have an automaton with no assumption, - // this automatically counts as best) - bool need_assumption = true; - regex_automaton_under_assumptions last_assumption; - rational last_lb = rational::zero(); // the default - for (svector::iterator it = regex_automaton_assumptions[re].begin(); - it != regex_automaton_assumptions[re].end(); ++it) { - regex_automaton_under_assumptions autA = *it; - if ((current_assignment == l_true && autA.get_polarity() == false) - || (current_assignment == l_false && autA.get_polarity() == true)) { - // automaton uses incorrect polarity - continue; - } - rational this_lb; - if (autA.get_lower_bound(this_lb)) { - if (this_lb > last_lb) { - last_lb = this_lb; - last_assumption = autA; - } - } else { - need_assumption = false; - last_assumption = autA; - break; - } - } - if (!last_lb.is_zero() || !need_assumption) { - CTRACE("str", !need_assumption, tout << "using automaton with full length information" << std::endl;); - CTRACE("str", need_assumption, tout << "using automaton with assumed lower bound of " << last_lb << std::endl;); - rational refined_lower_bound; - bool solution_at_lower_bound = refine_automaton_lower_bound(last_assumption.get_automaton(), - lower_bound_value, refined_lower_bound); - TRACE("str", tout << "refined lower bound is " << refined_lower_bound << - (solution_at_lower_bound?", solution at lower bound":", no solution at lower bound") << std::endl;); - - expr_ref_vector lhs(m); - if (current_assignment == l_false) { - lhs.push_back(m.mk_not(str_in_re)); - } else { - lhs.push_back(str_in_re); - } - if (need_assumption) { - lhs.push_back(m_autil.mk_ge(str_len, m_autil.mk_numeral(last_lb, true))); - } - lhs.push_back(m_autil.mk_ge(str_len, m_autil.mk_numeral(lower_bound_value, true))); - - expr_ref_vector rhs(m); - - if (solution_at_lower_bound) { - if (refined_lower_bound.is_minus_one()) { - // If there are solutions at the lower bound but not above it, make the bound exact. - rhs.push_back(ctx.mk_eq_atom(str_len, m_autil.mk_numeral(lower_bound_value, true))); - } else { - // If there are solutions at and above the lower bound, add an additional bound. - // DISABLED as this is causing non-termination in the integer solver. --mtrberzi - /* - rhs.push_back(m.mk_or( - ctx.mk_eq_atom(str_len, m_autil.mk_numeral(lower_bound_value, true)), - m_autil.mk_ge(str_len, m_autil.mk_numeral(refined_lower_bound, true)) - )); - */ - } - } else { - if (refined_lower_bound.is_minus_one()) { - // If there are no solutions at or above the lower bound, assert a conflict clause. - rhs.push_back(m.mk_not(m_autil.mk_ge(str_len, m_autil.mk_numeral(lower_bound_value, true)))); - } else { - // If there are solutions above the lower bound but not at it, refine the bound. - rhs.push_back(m_autil.mk_ge(str_len, m_autil.mk_numeral(refined_lower_bound, true))); - } - } - - if (!rhs.empty()) { - expr_ref lhs_terms(mk_and(lhs), m); - expr_ref rhs_terms(mk_and(rhs), m); - assert_implication(lhs_terms, rhs_terms); - regex_axiom_add = true; - } - } - } else { - // no existing automata/assumptions. - // if it's easy to construct a full automaton for R, do so - unsigned expected_complexity = estimate_regex_complexity(re); - bool failureThresholdExceeded = (regex_get_counter(regex_fail_count, str_in_re) >= m_params.m_RegexAutomata_FailedAutomatonThreshold); - if (expected_complexity <= m_params.m_RegexAutomata_DifficultyThreshold || failureThresholdExceeded) { - eautomaton * aut = m_mk_aut(re); - aut->compress(); - regex_automata.push_back(aut); - regex_automaton_under_assumptions new_aut(re, aut, true); - if (!regex_automaton_assumptions.contains(re)) { - regex_automaton_assumptions.insert(re, svector()); - } - regex_automaton_assumptions[re].push_back(new_aut); - TRACE("str", tout << "add new automaton for " << mk_pp(re, m) << ": no assumptions" << std::endl;); - regex_axiom_add = true; - find_automaton_initial_bounds(str_in_re, aut); - } else { - // TODO check negation? - // TODO construct a partial automaton for R to the given lower bound? - if (false) { - - } else { - regex_inc_counter(regex_fail_count, str_in_re); - } - } - continue; - } - } else { // !lower_bound_exists - // no bounds information - // check for existing automata; - // try to construct an automaton if we don't have one yet - // and doing so without bounds is not difficult - bool existingAutomata = (regex_automaton_assumptions.contains(re) && !regex_automaton_assumptions[re].empty()); - bool failureThresholdExceeded = (regex_get_counter(regex_fail_count, str_in_re) >= m_params.m_RegexAutomata_FailedAutomatonThreshold); - if (!existingAutomata) { - unsigned expected_complexity = estimate_regex_complexity(re); - if (expected_complexity <= m_params.m_RegexAutomata_DifficultyThreshold - || failureThresholdExceeded) { - eautomaton * aut = m_mk_aut(re); - aut->compress(); - regex_automata.push_back(aut); - regex_automaton_under_assumptions new_aut(re, aut, true); - if (!regex_automaton_assumptions.contains(re)) { - regex_automaton_assumptions.insert(re, svector()); - } - regex_automaton_assumptions[re].push_back(new_aut); - TRACE("str", tout << "add new automaton for " << mk_pp(re, m) << ": no assumptions" << std::endl;); - regex_axiom_add = true; - find_automaton_initial_bounds(str_in_re, aut); - } else { - regex_inc_counter(regex_fail_count, str_in_re); - } - } else { - regex_inc_counter(regex_fail_count, str_in_re); - } - } - } - } // foreach (entry in regex_terms) - - for (obj_map >::iterator it = regex_terms_by_string.begin(); - it != regex_terms_by_string.end(); ++it) { - // TODO do we need to check equivalence classes of strings here? - - expr * str = it->m_key; - ptr_vector str_in_re_terms = it->m_value; - - svector intersect_constraints; - // we may find empty intersection before checking every constraint; - // this vector keeps track of which ones actually take part in intersection - svector used_intersect_constraints; - - // choose an automaton/assumption for each assigned (str.in.re) - // that's consistent with the current length information - for (ptr_vector::iterator term_it = str_in_re_terms.begin(); - term_it != str_in_re_terms.end(); ++term_it) { - expr * _unused = nullptr; - expr * re = nullptr; - SASSERT(u.str.is_in_re(*term_it)); - u.str.is_in_re(*term_it, _unused, re); - - rational exact_len; - bool has_exact_len = get_len_value(str, exact_len); - - rational lb, ub; - bool has_lower_bound = lower_bound(mk_strlen(str), lb); - bool has_upper_bound = upper_bound(mk_strlen(str), ub); - - if (regex_automaton_assumptions.contains(re) && - !regex_automaton_assumptions[re].empty()){ - for (svector::iterator aut_it = regex_automaton_assumptions[re].begin(); - aut_it != regex_automaton_assumptions[re].end(); ++aut_it) { - regex_automaton_under_assumptions aut = *aut_it; - rational aut_ub; - bool assume_ub = aut.get_upper_bound(aut_ub); - rational aut_lb; - bool assume_lb = aut.get_lower_bound(aut_lb); - bool consistent = true; - - if (assume_ub) { - // check consistency of assumed upper bound - if (has_exact_len) { - if (exact_len > aut_ub) { - consistent = false; - } - } else { - if (has_upper_bound && ub > aut_ub) { - consistent = false; - } - } - } - - if (assume_lb) { - // check consistency of assumed lower bound - if (has_exact_len) { - if (exact_len < aut_lb) { - consistent = false; - } - } else { - if (has_lower_bound && lb < aut_lb) { - consistent = false; - } - } - } - - if (consistent) { - intersect_constraints.push_back(aut); - break; - } - } - } - } // foreach(term in str_in_re_terms) - - eautomaton * aut_inter = NULL; - CTRACE("str", !intersect_constraints.empty(), tout << "check intersection of automata constraints for " << mk_pp(str, m) << std::endl;); - for (svector::iterator aut_it = intersect_constraints.begin(); - aut_it != intersect_constraints.end(); ++aut_it) { - regex_automaton_under_assumptions aut = *aut_it; - if (aut_inter == NULL) { - // start somewhere - aut_inter = aut.get_automaton(); - used_intersect_constraints.push_back(aut); - continue; - } - - TRACE("str", - { - unsigned v = regex_get_counter(regex_length_attempt_count, aut.get_regex_term()); - tout << "length attempt count of " << mk_pp(aut.get_regex_term(), m) << " is " << v - << ", threshold is " << m_params.m_RegexAutomata_LengthAttemptThreshold << std::endl; - }); - - if (regex_get_counter(regex_length_attempt_count, aut.get_regex_term()) >= m_params.m_RegexAutomata_LengthAttemptThreshold) { - unsigned intersectionDifficulty = estimate_automata_intersection_difficulty(aut_inter, aut.get_automaton()); - TRACE("str", tout << "intersection difficulty is " << intersectionDifficulty << std::endl;); - if (intersectionDifficulty <= m_params.m_RegexAutomata_IntersectionDifficultyThreshold - || regex_get_counter(regex_intersection_fail_count, aut.get_regex_term()) >= m_params.m_RegexAutomata_FailedIntersectionThreshold) { - - expr * str_in_re_term(u.re.mk_in_re(str, aut.get_regex_term())); - lbool current_assignment = ctx.get_assignment(str_in_re_term); - // if the assignment is consistent with our assumption, use the automaton directly; - // otherwise, complement it (and save that automaton for next time) - // TODO we should cache these intermediate results - // TODO do we need to push the intermediates into a vector for deletion anyway? - if ( (current_assignment == l_true && aut.get_polarity()) - || (current_assignment == l_false && !aut.get_polarity())) { - aut_inter = m_mk_aut.mk_product(aut_inter, aut.get_automaton()); - m_automata.push_back(aut_inter); - } else { - // need to complement first - expr_ref rc(u.re.mk_complement(aut.get_regex_term()), m); - eautomaton * aut_c = m_mk_aut(rc); - regex_automata.push_back(aut_c); - // TODO is there any way to build a complement automaton from an existing one? - // this discards length information - aut_inter = m_mk_aut.mk_product(aut_inter, aut_c); - m_automata.push_back(aut_inter); - } - used_intersect_constraints.push_back(aut); - if (aut_inter->is_empty()) { - break; - } - } else { - // failed intersection - regex_inc_counter(regex_intersection_fail_count, aut.get_regex_term()); - } - } - } // foreach(entry in intersect_constraints) - if (aut_inter != NULL) { - aut_inter->compress(); - } - TRACE("str", tout << "intersected " << used_intersect_constraints.size() << " constraints" << std::endl;); - - expr_ref_vector conflict_terms(m); - expr_ref conflict_lhs(m); - for (svector::iterator aut_it = used_intersect_constraints.begin(); - aut_it != used_intersect_constraints.end(); ++aut_it) { - regex_automaton_under_assumptions aut = *aut_it; - expr * str_in_re_term(u.re.mk_in_re(str, aut.get_regex_term())); - lbool current_assignment = ctx.get_assignment(str_in_re_term); - if (current_assignment == l_true) { - conflict_terms.push_back(str_in_re_term); - } else if (current_assignment == l_false) { - conflict_terms.push_back(m.mk_not(str_in_re_term)); - } - // add length assumptions, if any - rational ub; - if (aut.get_upper_bound(ub)) { - expr_ref ub_term(m_autil.mk_le(mk_strlen(str), m_autil.mk_numeral(ub, true)), m); - conflict_terms.push_back(ub_term); - } - rational lb; - if (aut.get_lower_bound(lb)) { - expr_ref lb_term(m_autil.mk_ge(mk_strlen(str), m_autil.mk_numeral(lb, true)), m); - conflict_terms.push_back(lb_term); - } - } - conflict_lhs = mk_and(conflict_terms); - - if (used_intersect_constraints.size() > 1 && aut_inter != NULL) { - // check whether the intersection is only the empty string - unsigned initial_state = aut_inter->init(); - if (aut_inter->final_states().size() == 1 && aut_inter->is_final_state(initial_state)) { - // initial state is final and it is the only final state - // if there are no moves from the initial state, - // the only solution is the empty string - if (aut_inter->get_moves_from(initial_state).empty()) { - TRACE("str", tout << "product automaton only accepts empty string" << std::endl;); - expr_ref rhs1(ctx.mk_eq_atom(str, mk_string("")), m); - expr_ref rhs2(ctx.mk_eq_atom(mk_strlen(str), m_autil.mk_numeral(rational::zero(), true)), m); - expr_ref rhs(m.mk_and(rhs1, rhs2), m); - assert_implication(conflict_lhs, rhs); - regex_axiom_add = true; - } - } - } - - if (aut_inter != NULL && aut_inter->is_empty()) { - TRACE("str", tout << "product automaton is empty; asserting conflict clause" << std::endl;); - expr_ref conflict_clause(m.mk_not(mk_and(conflict_terms)), m); - assert_axiom(conflict_clause); - add_persisted_axiom(conflict_clause); - regex_axiom_add = true; - } - } // foreach (entry in regex_terms_by_string) - if (regex_axiom_add) { - //return FC_CONTINUE; - } + solve_regex_automata(); } // RegexAutomata bool needToAssignFreeVars = false; diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 419084091..6ee6866b3 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -30,6 +30,7 @@ #include "smt/params/theory_str_params.h" #include "smt/proto_model/value_factory.h" #include "smt/smt_model_generator.h" +#include "smt/smt_arith_value.h" #include #include #include @@ -546,6 +547,7 @@ protected: void process_concat_eq_unroll(expr * concat, expr * unroll); // regex automata and length-aware regex + void solve_regex_automata(); unsigned estimate_regex_complexity(expr * re); unsigned estimate_regex_complexity_under_complement(expr * re); unsigned estimate_automata_intersection_difficulty(eautomaton * aut1, eautomaton * aut2); From 144b72244e5d928182b248c8fc76bd21e0dbfc0e Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 18 Sep 2018 16:11:40 -0400 Subject: [PATCH 057/138] clean up pragmas, Z3str3 refactoring --- src/smt/smt_arith_value.cpp | 1 - src/smt/smt_arith_value.h | 2 +- src/smt/theory_str.cpp | 153 +++++++++++++++++++----------------- src/smt/theory_str.h | 2 + 4 files changed, 85 insertions(+), 73 deletions(-) diff --git a/src/smt/smt_arith_value.cpp b/src/smt/smt_arith_value.cpp index fcd6a3c4f..c85f6564b 100644 --- a/src/smt/smt_arith_value.cpp +++ b/src/smt/smt_arith_value.cpp @@ -17,7 +17,6 @@ Author: Revision History: --*/ -#pragma once; #include "smt/smt_arith_value.h" #include "smt/theory_lra.h" diff --git a/src/smt/smt_arith_value.h b/src/smt/smt_arith_value.h index b3748923e..b819b2b9a 100644 --- a/src/smt/smt_arith_value.h +++ b/src/smt/smt_arith_value.h @@ -17,7 +17,7 @@ Author: Revision History: --*/ -#pragma once; +#pragma once #include "ast/arith_decl_plugin.h" #include "smt/smt_context.h" diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 436892a20..662d76ae3 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -8097,31 +8097,7 @@ namespace smt { // BEGIN new_eq_handler() in strTheory - { - rational nn1Len, nn2Len; - bool nn1Len_exists = get_len_value(lhs, nn1Len); - bool nn2Len_exists = get_len_value(rhs, nn2Len); - expr_ref emptyStr(mk_string(""), m); - - if (nn1Len_exists && nn1Len.is_zero()) { - if (!in_same_eqc(lhs, emptyStr) && rhs != emptyStr) { - expr_ref eql(ctx.mk_eq_atom(mk_strlen(lhs), mk_int(0)), m); - expr_ref eqr(ctx.mk_eq_atom(lhs, emptyStr), m); - expr_ref toAssert(ctx.mk_eq_atom(eql, eqr), m); - assert_axiom(toAssert); - } - } - - if (nn2Len_exists && nn2Len.is_zero()) { - if (!in_same_eqc(rhs, emptyStr) && lhs != emptyStr) { - expr_ref eql(ctx.mk_eq_atom(mk_strlen(rhs), mk_int(0)), m); - expr_ref eqr(ctx.mk_eq_atom(rhs, emptyStr), m); - expr_ref toAssert(ctx.mk_eq_atom(eql, eqr), m); - assert_axiom(toAssert); - } - } - } - + check_eqc_empty_string(lhs, rhs); instantiate_str_eq_length_axiom(ctx.get_enode(lhs), ctx.get_enode(rhs)); // group terms by equivalence class (groupNodeInEqc()) @@ -8173,52 +8149,7 @@ namespace smt { ); // step 1: Concat == Concat - int hasCommon = 0; - if (eqc_concat_lhs.size() != 0 && eqc_concat_rhs.size() != 0) { - std::set::iterator itor1 = eqc_concat_lhs.begin(); - std::set::iterator itor2 = eqc_concat_rhs.begin(); - for (; itor1 != eqc_concat_lhs.end(); itor1++) { - if (eqc_concat_rhs.find(*itor1) != eqc_concat_rhs.end()) { - hasCommon = 1; - break; - } - } - for (; itor2 != eqc_concat_rhs.end(); itor2++) { - if (eqc_concat_lhs.find(*itor2) != eqc_concat_lhs.end()) { - hasCommon = 1; - break; - } - } - if (hasCommon == 0) { - if (opt_ConcatOverlapAvoid) { - bool found = false; - // check each pair and take the first ones that won't immediately overlap - for (itor1 = eqc_concat_lhs.begin(); itor1 != eqc_concat_lhs.end() && !found; ++itor1) { - expr * concat_lhs = *itor1; - for (itor2 = eqc_concat_rhs.begin(); itor2 != eqc_concat_rhs.end() && !found; ++itor2) { - expr * concat_rhs = *itor2; - if (will_result_in_overlap(concat_lhs, concat_rhs)) { - TRACE("str", tout << "Concats " << mk_pp(concat_lhs, m) << " and " - << mk_pp(concat_rhs, m) << " will result in overlap; skipping." << std::endl;); - } else { - TRACE("str", tout << "Concats " << mk_pp(concat_lhs, m) << " and " - << mk_pp(concat_rhs, m) << " won't overlap. Simplifying here." << std::endl;); - simplify_concat_equality(concat_lhs, concat_rhs); - found = true; - break; - } - } - } - if (!found) { - TRACE("str", tout << "All pairs of concats expected to overlap, falling back." << std::endl;); - simplify_concat_equality(*(eqc_concat_lhs.begin()), *(eqc_concat_rhs.begin())); - } - } else { - // default behaviour - simplify_concat_equality(*(eqc_concat_lhs.begin()), *(eqc_concat_rhs.begin())); - } - } - } + check_eqc_concat_concat(eqc_concat_lhs, eqc_concat_rhs); // step 2: Concat == Constant @@ -8271,6 +8202,86 @@ namespace smt { } + // Check that a string's length can be 0 iff it is the empty string. + void theory_str::check_eqc_empty_string(expr * lhs, expr * rhs) { + context & ctx = get_context(); + ast_manager & m = get_manager(); + + rational nn1Len, nn2Len; + bool nn1Len_exists = get_len_value(lhs, nn1Len); + bool nn2Len_exists = get_len_value(rhs, nn2Len); + expr_ref emptyStr(mk_string(""), m); + + if (nn1Len_exists && nn1Len.is_zero()) { + if (!in_same_eqc(lhs, emptyStr) && rhs != emptyStr) { + expr_ref eql(ctx.mk_eq_atom(mk_strlen(lhs), mk_int(0)), m); + expr_ref eqr(ctx.mk_eq_atom(lhs, emptyStr), m); + expr_ref toAssert(ctx.mk_eq_atom(eql, eqr), m); + assert_axiom(toAssert); + } + } + + if (nn2Len_exists && nn2Len.is_zero()) { + if (!in_same_eqc(rhs, emptyStr) && lhs != emptyStr) { + expr_ref eql(ctx.mk_eq_atom(mk_strlen(rhs), mk_int(0)), m); + expr_ref eqr(ctx.mk_eq_atom(rhs, emptyStr), m); + expr_ref toAssert(ctx.mk_eq_atom(eql, eqr), m); + assert_axiom(toAssert); + } + } + } + + void theory_str::check_eqc_concat_concat(std::set & eqc_concat_lhs, std::set & eqc_concat_rhs) { + ast_manager & m = get_manager(); + + int hasCommon = 0; + if (eqc_concat_lhs.size() != 0 && eqc_concat_rhs.size() != 0) { + std::set::iterator itor1 = eqc_concat_lhs.begin(); + std::set::iterator itor2 = eqc_concat_rhs.begin(); + for (; itor1 != eqc_concat_lhs.end(); itor1++) { + if (eqc_concat_rhs.find(*itor1) != eqc_concat_rhs.end()) { + hasCommon = 1; + break; + } + } + for (; itor2 != eqc_concat_rhs.end(); itor2++) { + if (eqc_concat_lhs.find(*itor2) != eqc_concat_lhs.end()) { + hasCommon = 1; + break; + } + } + if (hasCommon == 0) { + if (opt_ConcatOverlapAvoid) { + bool found = false; + // check each pair and take the first ones that won't immediately overlap + for (itor1 = eqc_concat_lhs.begin(); itor1 != eqc_concat_lhs.end() && !found; ++itor1) { + expr * concat_lhs = *itor1; + for (itor2 = eqc_concat_rhs.begin(); itor2 != eqc_concat_rhs.end() && !found; ++itor2) { + expr * concat_rhs = *itor2; + if (will_result_in_overlap(concat_lhs, concat_rhs)) { + TRACE("str", tout << "Concats " << mk_pp(concat_lhs, m) << " and " + << mk_pp(concat_rhs, m) << " will result in overlap; skipping." << std::endl;); + } else { + TRACE("str", tout << "Concats " << mk_pp(concat_lhs, m) << " and " + << mk_pp(concat_rhs, m) << " won't overlap. Simplifying here." << std::endl;); + simplify_concat_equality(concat_lhs, concat_rhs); + found = true; + break; + } + } + } + if (!found) { + TRACE("str", tout << "All pairs of concats expected to overlap, falling back." << std::endl;); + simplify_concat_equality(*(eqc_concat_lhs.begin()), *(eqc_concat_rhs.begin())); + } + } else { + // default behaviour + simplify_concat_equality(*(eqc_concat_lhs.begin()), *(eqc_concat_rhs.begin())); + } + } + } + } + void theory_str::set_up_axioms(expr * ex) { ast_manager & m = get_manager(); context & ctx = get_context(); diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 6ee6866b3..522492603 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -582,6 +582,8 @@ protected: bool can_concat_eq_str(expr * concat, zstring& str); bool can_concat_eq_concat(expr * concat1, expr * concat2); bool check_concat_len_in_eqc(expr * concat); + void check_eqc_empty_string(expr * lhs, expr * rhs); + void check_eqc_concat_concat(std::set & eqc_concat_lhs, std::set & eqc_concat_rhs); bool check_length_consistency(expr * n1, expr * n2); bool check_length_const_string(expr * n1, expr * constStr); bool check_length_eq_var_concat(expr * n1, expr * n2); From ca3ce964ce27d4e24abdfd6eaa144e2a1d358970 Mon Sep 17 00:00:00 2001 From: Lev Date: Tue, 18 Sep 2018 13:34:05 -0700 Subject: [PATCH 058/138] work on Gomory cut Signed-off-by: Lev --- src/smt/smt_arith_value.h | 2 +- src/util/lp/column_namer.h | 23 ------ src/util/lp/gomory.cpp | 141 ++++++++++++++++++++++++++++++++++--- src/util/lp/int_solver.h | 8 --- src/util/lp/lar_solver.cpp | 2 +- src/util/lp/lp_utils.h | 31 ++++++-- 6 files changed, 160 insertions(+), 47 deletions(-) diff --git a/src/smt/smt_arith_value.h b/src/smt/smt_arith_value.h index 9b0f833ac..4e22b44f8 100644 --- a/src/smt/smt_arith_value.h +++ b/src/smt/smt_arith_value.h @@ -17,7 +17,7 @@ Author: Revision History: --*/ -#pragma once; +#pragma once #include "ast/arith_decl_plugin.h" #include "smt/smt_context.h" diff --git a/src/util/lp/column_namer.h b/src/util/lp/column_namer.h index e6e8e53a2..51baf445f 100644 --- a/src/util/lp/column_namer.h +++ b/src/util/lp/column_namer.h @@ -33,29 +33,6 @@ public: print_linear_combination_of_column_indices(coeff, out); } - template - void print_linear_combination_of_column_indices_only(const vector> & coeffs, std::ostream & out) const { - bool first = true; - for (const auto & it : coeffs) { - auto val = it.first; - if (first) { - first = false; - } else { - if (numeric_traits::is_pos(val)) { - out << " + "; - } else { - out << " - "; - val = -val; - } - } - if (val == -numeric_traits::one()) - out << " - "; - else if (val != numeric_traits::one()) - out << T_to_string(val); - - out << "v" << it.second; - } - } template diff --git a/src/util/lp/gomory.cpp b/src/util/lp/gomory.cpp index 55098dccf..f936397d4 100644 --- a/src/util/lp/gomory.cpp +++ b/src/util/lp/gomory.cpp @@ -23,6 +23,16 @@ namespace lp { class gomory::imp { + inline static bool is_rational(const impq & n) { return is_zero(n.y); } + + inline static mpq fractional_part(const impq & n) { + lp_assert(is_rational(n)); + return n.x - floor(n.x); + } + inline static mpq fractional_part(const mpq & n) { + return n - floor(n); + } + lar_term & m_t; // the term to return in the cut mpq & m_k; // the right side of the cut explanation& m_ex; // the conflict explanation @@ -43,32 +53,31 @@ class gomory::imp { void int_case_in_gomory_cut(const mpq & a, unsigned j, mpq & lcm_den, const mpq& f0, const mpq& one_minus_f0) { lp_assert(is_int(j) && !a.is_int()); - mpq fj = int_solver::fractional_part(a); - lp_assert(fj.is_pos()); + mpq fj = fractional_part(a); TRACE("gomory_cut_detail", tout << a << " j=" << j << " k = " << m_k; tout << ", fj: " << fj << ", "; - tout << "f0: " << f0 << ", "; - tout << "1 - f0: " << 1 - f0 << ", "; + tout << "a - fj = " << a - fj << ", "; tout << (at_lower(j)?"at_lower":"at_upper")<< std::endl; ); + lp_assert(fj.is_pos() && (a - fj).is_int()); mpq new_a; mpq one_minus_fj = 1 - fj; if (at_lower(j)) { - new_a = fj < one_minus_f0? fj / one_minus_f0 : one_minus_fj / f0; + new_a = fj < one_minus_f0? fj / one_minus_f0 : (- one_minus_fj / f0); m_k.addmul(new_a, lower_bound(j).x); m_ex.push_justification(column_lower_bound_constraint(j), new_a); } else { lp_assert(at_upper(j)); // the upper terms are inverted: therefore we have the minus - new_a = - (fj < f0? fj / f0 : one_minus_fj / one_minus_f0); + new_a = - (fj < f0? fj / f0 : (- one_minus_fj / one_minus_f0)); m_k.addmul(new_a, upper_bound(j).x); m_ex.push_justification(column_upper_bound_constraint(j), new_a); } - TRACE("gomory_cut_detail", tout << "new_a: " << new_a << " k: " << m_k << "\n";); m_t.add_monomial(new_a, j); lcm_den = lcm(lcm_den, denominator(new_a)); + TRACE("gomory_cut_detail", tout << "new_a = " << new_a << ", k = " << m_k << ", lcm_den = " << lcm_den << "\n";); } void real_case_in_gomory_cut(const mpq & a, unsigned x_j, const mpq& f0, const mpq& one_minus_f0) { @@ -150,10 +159,117 @@ class gomory::imp { TRACE("gomory_cut_detail", tout << "k = " << m_k << std::endl;); lp_assert(m_k.is_int()); } + + std::string var_name(unsigned j) const { + return std::string("x") + std::to_string(j); + } + + void dump_coeff_val(std::ostream & out, const mpq & a) const { + if (a.is_int()) { + if ( a >= zero_of_type()) + out << a; + else { + out << "( - " << - a << ") "; + } + } else { + if ( a >= zero_of_type()) + out << "(div " << numerator(a) << " " << denominator(a) << ")"; + else { + out << "(- ( div " << numerator(-a) << " " << denominator(-a) << "))"; + } + + } + } + + template + void dump_coeff(std::ostream & out, const T& c) const { + out << "( * "; + dump_coeff_val(out, c.coeff()); + out << " " << var_name(c.var()) << ")"; + } + + void dump_row_coefficients(std::ostream & out) const { + for (const auto& p : m_row) { + dump_coeff(out, p); + } + } + + void dump_the_row(std::ostream& out) const { + out << "; the row\n"; + out << "(assert ( = ( + "; + dump_row_coefficients(out); + out << ") 0))\n"; + } + + void dump_declarations(std::ostream& out) const { + // for a column j the var name is vj + for (const auto & p : m_row) { + out << "(declare-fun " << var_name(p.var()) << " () " + << (is_int(p.var())? "Int" : "Real") << ")\n"; + } + } + + void dump_lower_bound_expl(std::ostream & out, unsigned j) const { + out << "(assert ( >= " << var_name(j) << " " << lower_bound(j).x << "))\n"; + } + void dump_upper_bound_expl(std::ostream & out, unsigned j) const { + out << "(assert ( <= " << var_name(j) << " " << upper_bound(j).x << "))\n"; + } + + void dump_explanations(std::ostream& out) const { + for (const auto & p : m_row) { + unsigned j = p.var(); + if (j == m_inf_col) { + continue; + } + + if (column_is_fixed(j)) { + dump_lower_bound_expl(out, j); + dump_upper_bound_expl(out, j); + continue; + } + + if (at_lower(j)) { + dump_lower_bound_expl(out, j); + } else { + dump_upper_bound_expl(out, j); + } + } + } + + void dump_terms_coefficients(std::ostream & out) const { + for (const auto& p : m_t) { + dump_coeff(out, p); + } + } + + void dump_term_sum(std::ostream & out) const { + out << "( + "; + dump_terms_coefficients(out); + out << ")"; + } + + void dump_term_le_k(std::ostream & out) const { + out << "( <= "; + dump_term_sum(out); + out << m_k << ")"; + } + void dump_the_cut_assert(std::ostream & out) const { + out <<"(assert (not "; + dump_term_le_k(out); + out << "))\n"; + } + void dump_cut_and_constraints_as_smt_lemma(std::ostream& out) const { + dump_declarations(out); + dump_the_row(out); + dump_explanations(out); + dump_the_cut_assert(out); + out << "(check-sat)\n"; + } public: lia_move create_cut() { TRACE("gomory_cut", - tout << "applying cut at:\n"; m_int_solver.m_lar_solver->print_row(m_row, tout); tout << std::endl; + tout << "applying cut at:\n"; print_linear_combination_of_column_indices_only(m_row, tout); tout << std::endl; for (auto & p : m_row) { m_int_solver.m_lar_solver->m_mpq_lar_core_solver.m_r_solver.print_column_info(p.var(), tout); } @@ -164,7 +280,11 @@ public: m_k = 1; mpq lcm_den(1); bool some_int_columns = false; - mpq f0 = int_solver::fractional_part(get_value(m_inf_col)); + mpq f0 = fractional_part(get_value(m_inf_col)); + TRACE("gomory_cut_detail", tout << "f0: " << f0 << ", "; + tout << "1 - f0: " << 1 - f0 << ", get_value(m_inf_col).x - f0 = " << get_value(m_inf_col).x - f0;); + lp_assert(f0.is_pos() && (get_value(m_inf_col).x - f0).is_int()); + mpq one_min_f0 = 1 - f0; for (const auto & p : m_row) { unsigned j = p.var(); @@ -194,7 +314,8 @@ public: adjust_term_and_k_for_some_ints_case_gomory(lcm_den); lp_assert(m_int_solver.current_solution_is_inf_on_cut()); m_int_solver.m_lar_solver->subs_term_columns(m_t, m_k); - TRACE("gomory_cut", tout<<"gomory cut:"; m_int_solver.m_lar_solver->print_term(m_t, tout); tout << " <= " << m_k << std::endl;); + TRACE("gomory_cut", tout<<"gomory cut:"; print_linear_combination_of_column_indices_only(m_t, tout); tout << " <= " << m_k << std::endl;); + TRACE("gomory_cut_detail", dump_cut_and_constraints_as_smt_lemma(tout);); return lia_move::cut; } imp(lar_term & t, mpq & k, explanation& ex, unsigned basic_inf_int_j, const row_strip& row, const int_solver& int_slv ) : diff --git a/src/util/lp/int_solver.h b/src/util/lp/int_solver.h index dfe51711c..013f53ce0 100644 --- a/src/util/lp/int_solver.h +++ b/src/util/lp/int_solver.h @@ -113,17 +113,9 @@ private: bool has_low(unsigned j) const; bool has_upper(unsigned j) const; unsigned row_of_basic_column(unsigned j) const; - inline static bool is_rational(const impq & n) { - return is_zero(n.y); - } public: void display_column(std::ostream & out, unsigned j) const; - inline static - mpq fractional_part(const impq & n) { - lp_assert(is_rational(n)); - return n.x - floor(n.x); - } constraint_index column_upper_bound_constraint(unsigned j) const; constraint_index column_lower_bound_constraint(unsigned j) const; bool current_solution_is_inf_on_cut() const; diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp index 09c3bd5b9..56a61177c 100644 --- a/src/util/lp/lar_solver.cpp +++ b/src/util/lp/lar_solver.cpp @@ -1273,7 +1273,7 @@ std::ostream& lar_solver::print_term_as_indices(lar_term const& term, std::ostre if (!numeric_traits::is_zero(term.m_v)) { out << term.m_v << " + "; } - print_linear_combination_of_column_indices_only(term.coeffs_as_vector(), out); + print_linear_combination_of_column_indices_only(term, out); return out; } diff --git a/src/util/lp/lp_utils.h b/src/util/lp/lp_utils.h index e776092a2..573fc319c 100644 --- a/src/util/lp/lp_utils.h +++ b/src/util/lp/lp_utils.h @@ -50,11 +50,34 @@ bool contains(const std::unordered_map & map, const A& key) { namespace lp { - - inline void throw_exception(std::string && str) { - throw default_exception(std::move(str)); +template +void print_linear_combination_of_column_indices_only(const T & coeffs, std::ostream & out) { + bool first = true; + for (const auto & it : coeffs) { + auto val = it.coeff(); + if (first) { + first = false; + } else { + if (val.is_pos()) { + out << " + "; + } else { + out << " - "; + val = -val; + } + } + if (val == 1) + out << " "; + else + out << T_to_string(val); + + out << "x" << it.var(); } - typedef z3_exception exception; +} + +inline void throw_exception(std::string && str) { + throw default_exception(std::move(str)); +} +typedef z3_exception exception; #define lp_assert(_x_) { SASSERT(_x_); } inline void lp_unreachable() { lp_assert(false); } From b940b7873bbac7b2420aa0cb6bcc04e0a79a8d3f Mon Sep 17 00:00:00 2001 From: Lev Date: Tue, 18 Sep 2018 13:47:18 -0700 Subject: [PATCH 059/138] work on Gomory cut Signed-off-by: Lev --- src/util/lp/gomory.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/util/lp/gomory.cpp b/src/util/lp/gomory.cpp index f936397d4..e1e6fc0ea 100644 --- a/src/util/lp/gomory.cpp +++ b/src/util/lp/gomory.cpp @@ -190,12 +190,13 @@ class gomory::imp { void dump_row_coefficients(std::ostream & out) const { for (const auto& p : m_row) { - dump_coeff(out, p); + if (!column_is_fixed(p.var())) + dump_coeff(out, p); } } void dump_the_row(std::ostream& out) const { - out << "; the row\n"; + out << "; the row, excluding fixed vars\n"; out << "(assert ( = ( + "; dump_row_coefficients(out); out << ") 0))\n"; @@ -204,6 +205,7 @@ class gomory::imp { void dump_declarations(std::ostream& out) const { // for a column j the var name is vj for (const auto & p : m_row) { + if (column_is_fixed(p.var())) continue; out << "(declare-fun " << var_name(p.var()) << " () " << (is_int(p.var())? "Int" : "Real") << ")\n"; } @@ -217,8 +219,9 @@ class gomory::imp { } void dump_explanations(std::ostream& out) const { - for (const auto & p : m_row) { + for (const auto & p : m_row) { unsigned j = p.var(); + if (column_is_fixed(j)) continue; if (j == m_inf_col) { continue; } From 041458f97ae81d52884e4993c042b594c0297552 Mon Sep 17 00:00:00 2001 From: Lev Date: Tue, 18 Sep 2018 14:42:32 -0700 Subject: [PATCH 060/138] fixes the +- bug in gomory cut Signed-off-by: Lev --- src/util/lp/gomory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/lp/gomory.cpp b/src/util/lp/gomory.cpp index e1e6fc0ea..377c6125c 100644 --- a/src/util/lp/gomory.cpp +++ b/src/util/lp/gomory.cpp @@ -71,7 +71,7 @@ class gomory::imp { else { lp_assert(at_upper(j)); // the upper terms are inverted: therefore we have the minus - new_a = - (fj < f0? fj / f0 : (- one_minus_fj / one_minus_f0)); + new_a = fj < f0? (- fj / f0 ) : (one_minus_fj / one_minus_f0); m_k.addmul(new_a, upper_bound(j).x); m_ex.push_justification(column_upper_bound_constraint(j), new_a); } From b90d571d9a4c35cb38bcc91bea1f696e9ff25132 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 18 Sep 2018 15:36:01 -0700 Subject: [PATCH 061/138] fixing the build Signed-off-by: Lev Nachmanson --- src/test/lp/gomory_test.h | 5 +++-- src/util/lp/gomory.cpp | 11 +---------- src/util/lp/lp_settings.h | 8 ++++++++ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/test/lp/gomory_test.h b/src/test/lp/gomory_test.h index 03ad5b187..501ad9e1a 100644 --- a/src/test/lp/gomory_test.h +++ b/src/test/lp/gomory_test.h @@ -1,4 +1,5 @@ namespace lp { +#include "util/lp/lp_utils.h" struct gomory_test { gomory_test( std::function name_function_p, @@ -88,7 +89,7 @@ struct gomory_test { lp_assert(is_int(x_j)); lp_assert(!a.is_int()); lp_assert(f_0 > zero_of_type() && f_0 < one_of_type()); - mpq f_j = int_solver::fractional_part(a); + mpq f_j = fractional_part(a); TRACE("gomory_cut_detail", tout << a << " x_j = " << x_j << ", k = " << k << "\n"; tout << "f_j: " << f_j << "\n"; @@ -206,7 +207,7 @@ struct gomory_test { unsigned x_j; mpq a; bool some_int_columns = false; - mpq f_0 = int_solver::fractional_part(get_value(inf_col)); + mpq f_0 = fractional_part(get_value(inf_col)); mpq one_min_f_0 = 1 - f_0; for ( auto pp : row) { a = pp.first; diff --git a/src/util/lp/gomory.cpp b/src/util/lp/gomory.cpp index 377c6125c..f0bbd2348 100644 --- a/src/util/lp/gomory.cpp +++ b/src/util/lp/gomory.cpp @@ -20,19 +20,10 @@ #include "util/lp/gomory.h" #include "util/lp/int_solver.h" #include "util/lp/lar_solver.h" +#include "util/lp/lp_utils.h" namespace lp { class gomory::imp { - inline static bool is_rational(const impq & n) { return is_zero(n.y); } - - inline static mpq fractional_part(const impq & n) { - lp_assert(is_rational(n)); - return n.x - floor(n.x); - } - inline static mpq fractional_part(const mpq & n) { - return n - floor(n); - } - lar_term & m_t; // the term to return in the cut mpq & m_k; // the right side of the cut explanation& m_ex; // the conflict explanation diff --git a/src/util/lp/lp_settings.h b/src/util/lp/lp_settings.h index 71be7258a..1bbefd154 100644 --- a/src/util/lp/lp_settings.h +++ b/src/util/lp/lp_settings.h @@ -433,7 +433,15 @@ inline void ensure_increasing(vector & v) { } } +inline static bool is_rational(const impq & n) { return is_zero(n.y); } +inline static mpq fractional_part(const impq & n) { + lp_assert(is_rational(n)); + return n.x - floor(n.x); +} +inline static mpq fractional_part(const mpq & n) { + return n - floor(n); +} #if Z3DEBUG bool D(); From ed19af4c4e7fc2d4387650c7c0087ccbfa5a7d63 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 19 Sep 2018 09:02:37 -0700 Subject: [PATCH 062/138] merge Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 1 + src/util/lp/gomory.cpp | 4 +++- src/util/lp/lp_core_solver_base.h | 6 +++--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index fd2dc0da2..f096d1dd5 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1629,6 +1629,7 @@ public: expr_ref term2expr(lp::lar_term const& term) { expr_ref t(m); expr_ref_vector ts(m); + ts.push_back(a.mk_numeral(term.m_v, true)); for (auto const& p : term) { lp::var_index wi = p.var(); if (m_solver->is_term(wi)) { diff --git a/src/util/lp/gomory.cpp b/src/util/lp/gomory.cpp index 55098dccf..9974eda7f 100644 --- a/src/util/lp/gomory.cpp +++ b/src/util/lp/gomory.cpp @@ -168,11 +168,13 @@ public: mpq one_min_f0 = 1 - f0; for (const auto & p : m_row) { unsigned j = p.var(); +#if 1 if (column_is_fixed(j)) { m_ex.push_justification(column_lower_bound_constraint(j)); m_ex.push_justification(column_upper_bound_constraint(j)); continue; } +#endif if (j == m_inf_col) { lp_assert(p.coeff() == one_of_type()); TRACE("gomory_cut_detail", tout << "seeing basic var";); @@ -194,7 +196,7 @@ public: adjust_term_and_k_for_some_ints_case_gomory(lcm_den); lp_assert(m_int_solver.current_solution_is_inf_on_cut()); m_int_solver.m_lar_solver->subs_term_columns(m_t, m_k); - TRACE("gomory_cut", tout<<"gomory cut:"; m_int_solver.m_lar_solver->print_term(m_t, tout); tout << " <= " << m_k << std::endl;); + TRACE("gomory_cut", tout<<"gomory cut:"; m_int_solver.m_lar_solver->print_term(m_t, tout) << " <= " << m_k << std::endl;); return lia_move::cut; } imp(lar_term & t, mpq & k, explanation& ex, unsigned basic_inf_int_j, const row_strip& row, const int_solver& int_slv ) : diff --git a/src/util/lp/lp_core_solver_base.h b/src/util/lp/lp_core_solver_base.h index 41b6fe31d..9a6549917 100644 --- a/src/util/lp/lp_core_solver_base.h +++ b/src/util/lp/lp_core_solver_base.h @@ -577,7 +577,7 @@ public: } void print_column_info(unsigned j, std::ostream & out) const { - out << "j = " << j << ", name = "<< column_name(j); + out << "j = " << j << ",\tname = "<< column_name(j) << "\t"; switch (m_column_types[j]) { case column_type::fixed: case column_type::boxed: @@ -596,11 +596,11 @@ public: lp_assert(false); } // out << "basis heading = " << m_basis_heading[j] << std::endl; - out << " x = " << m_x[j]; + out << "\tx = " << m_x[j]; if (m_basis_heading[j] >= 0) out << " base\n"; else - out << " nbas\n"; + out << " \n"; } bool column_is_free(unsigned j) const { return this->m_column_type[j] == free; } From a99ebed907e3a620a751a0d63b6ed7ce62e49dcd Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Wed, 19 Sep 2018 10:17:27 -0700 Subject: [PATCH 063/138] keep the coefficients of 'at lower' variables positive, and the rest negative for Gomory cuts Signed-off-by: Lev Nachmanson --- src/util/lp/gomory.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/util/lp/gomory.cpp b/src/util/lp/gomory.cpp index f0bbd2348..53e12c7ec 100644 --- a/src/util/lp/gomory.cpp +++ b/src/util/lp/gomory.cpp @@ -55,14 +55,14 @@ class gomory::imp { mpq new_a; mpq one_minus_fj = 1 - fj; if (at_lower(j)) { - new_a = fj < one_minus_f0? fj / one_minus_f0 : (- one_minus_fj / f0); + new_a = fj < one_minus_f0? fj / one_minus_f0 : one_minus_fj / f0; m_k.addmul(new_a, lower_bound(j).x); m_ex.push_justification(column_lower_bound_constraint(j), new_a); } else { lp_assert(at_upper(j)); // the upper terms are inverted: therefore we have the minus - new_a = fj < f0? (- fj / f0 ) : (one_minus_fj / one_minus_f0); + new_a = - (fj < f0? fj / f0 : one_minus_fj / one_minus_f0); m_k.addmul(new_a, upper_bound(j).x); m_ex.push_justification(column_upper_bound_constraint(j), new_a); } From dcda39e76e68fd2264e681c42fb3ae25e10e3ade Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 19 Sep 2018 17:12:32 -0700 Subject: [PATCH 064/138] merge Signed-off-by: Nikolaj Bjorner --- src/util/lp/gomory.cpp | 15 ++++++++++++--- src/util/lp/lar_solver.h | 4 +--- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/util/lp/gomory.cpp b/src/util/lp/gomory.cpp index 2dd349354..2136f5f3e 100644 --- a/src/util/lp/gomory.cpp +++ b/src/util/lp/gomory.cpp @@ -197,12 +197,21 @@ class gomory::imp { out << "(assert ( = ( +"; dump_row_coefficients(out) << ") 0))\n"; } + + void dump_declaration(std::ostream& out, unsigned v) const { + out << "(declare-const " << var_name(v) << (is_int(v) ? " Int" : " Real") << ")\n"; + } void dump_declarations(std::ostream& out) const { // for a column j the var name is vj for (const auto & p : m_row) { - out << "(declare-const " << var_name(p.var()) - << (is_int(p.var())? " Int" : " Real") << ")\n"; + dump_declaration(out, p.var()); + } + for (const auto& p : m_t) { + unsigned v = p.var(); + if (m_int_solver.m_lar_solver->is_term(v)) { + dump_declaration(out, v); + } } } @@ -298,7 +307,7 @@ public: lp_assert(m_int_solver.current_solution_is_inf_on_cut()); TRACE("gomory_cut_detail", dump_cut_and_constraints_as_smt_lemma(tout);); m_int_solver.m_lar_solver->subs_term_columns(m_t, m_k); - // TBD: validate result of subs_term_columns + TRACE("gomory_cut_detail", dump_cut_and_constraints_as_smt_lemma(tout);); TRACE("gomory_cut", print_linear_combination_of_column_indices_only(m_t, tout << "gomory cut:"); tout << " <= " << m_k << std::endl;); return lia_move::cut; } diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index 3c0ed4fbf..9e9edacc8 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -239,8 +239,7 @@ public: void analyze_new_bounds_on_row_tableau( unsigned row_index, - bound_propagator & bp - ); + bound_propagator & bp); void substitute_basis_var_in_terms_for_row(unsigned i); @@ -549,7 +548,6 @@ public: mpq v = it->second; t.m_coeffs.erase(it); t.m_coeffs[p.second] = v; - if (lt.m_v.is_zero()) continue; rs -= v * lt.m_v; } } From d75b6fd9c1536b9c8f98392ad292be7859e0ce63 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 20 Sep 2018 11:06:05 -0700 Subject: [PATCH 065/138] remove offsets from terms Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 128 +++++++++++++++++++------------ src/util/lp/bound_propagator.cpp | 4 - src/util/lp/gomory.cpp | 16 ++-- src/util/lp/int_solver.cpp | 51 ++++++------ src/util/lp/int_solver.h | 16 ++-- src/util/lp/lar_constraints.h | 2 +- src/util/lp/lar_solver.cpp | 69 ++++++----------- src/util/lp/lar_solver.h | 14 ++-- src/util/lp/lar_term.h | 12 ++- 9 files changed, 159 insertions(+), 153 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index f096d1dd5..ca59a2c27 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -55,7 +55,7 @@ std::ostream& operator<<(std::ostream& out, bound_kind const& k) { } class bound { - smt::bool_var m_bv; + smt::bool_var m_bv; smt::theory_var m_var; bool m_is_int; rational m_value; @@ -165,13 +165,13 @@ class theory_lra::imp { expr_ref_vector m_terms; vector m_coeffs; svector m_vars; - rational m_coeff; + rational m_offset; ptr_vector m_terms_to_internalize; internalize_state(ast_manager& m): m_terms(m) {} void reset() { m_terms.reset(); m_coeffs.reset(); - m_coeff.reset(); + m_offset.reset(); m_vars.reset(); m_terms_to_internalize.reset(); } @@ -197,7 +197,7 @@ class theory_lra::imp { expr_ref_vector& terms() { return m_st.m_terms; } vector& coeffs() { return m_st.m_coeffs; } svector& vars() { return m_st.m_vars; } - rational& coeff() { return m_st.m_coeff; } + rational& offset() { return m_st.m_offset; } ptr_vector& terms_to_internalize() { return m_st.m_terms_to_internalize; } void push(expr* e, rational c) { m_st.m_terms.push_back(e); m_st.m_coeffs.push_back(c); } void set_back(unsigned i) { @@ -216,6 +216,8 @@ class theory_lra::imp { svector m_term_index2theory_var; // reverse map from lp_solver variables to theory variables var_coeffs m_left_side; // constraint left side mutable std::unordered_map m_variable_values; // current model + lp::var_index m_one_var; + lp::var_index m_zero_var; enum constraint_source { inequality_source, @@ -331,6 +333,32 @@ class theory_lra::imp { } } + void add_const(int c, lp::var_index& var) { + if (var != UINT_MAX) { + return; + } + app_ref cnst(a.mk_int(c), m); + TRACE("arith", tout << "add " << cnst << "\n";); + enode* e = mk_enode(cnst); + theory_var v = mk_var(cnst); + var = m_solver->add_var(v, true); + m_theory_var2var_index.setx(v, var, UINT_MAX); + m_var_index2theory_var.setx(var, v, UINT_MAX); + m_var_trail.push_back(v); + add_def_constraint(m_solver->add_var_bound(var, lp::GE, rational(c))); + add_def_constraint(m_solver->add_var_bound(var, lp::LE, rational(c))); + } + + lp::var_index get_one() { + add_const(1, m_one_var); + return m_one_var; + } + + lp::var_index get_zero() { + add_const(0, m_zero_var); + return m_zero_var; + } + void found_not_handled(expr* n) { m_not_handled = n; @@ -375,7 +403,7 @@ class theory_lra::imp { expr_ref_vector & terms = st.terms(); svector& vars = st.vars(); vector& coeffs = st.coeffs(); - rational& coeff = st.coeff(); + rational& offset = st.offset(); rational r; expr* n1, *n2; unsigned index = 0; @@ -415,7 +443,7 @@ class theory_lra::imp { ++index; } else if (a.is_numeral(n, r)) { - coeff += coeffs[index]*r; + offset += coeffs[index]*r; ++index; } else if (a.is_uminus(n, n1)) { @@ -427,7 +455,6 @@ class theory_lra::imp { app* t = to_app(n); internalize_args(t); mk_enode(t); - theory_var v = mk_var(n); coeffs[vars.size()] = coeffs[index]; vars.push_back(v); @@ -738,7 +765,15 @@ class theory_lra::imp { } bool is_unit_var(scoped_internalize_state& st) { - return st.coeff().is_zero() && st.vars().size() == 1 && st.coeffs()[0].is_one(); + return st.offset().is_zero() && st.vars().size() == 1 && st.coeffs()[0].is_one(); + } + + bool is_one(scoped_internalize_state& st) { + return st.offset().is_one() && st.vars().empty(); + } + + bool is_zero(scoped_internalize_state& st) { + return st.offset().is_zero() && st.vars().empty(); } theory_var internalize_def(app* term, scoped_internalize_state& st) { @@ -771,13 +806,24 @@ class theory_lra::imp { if (is_unit_var(st)) { return st.vars()[0]; } + else if (is_one(st)) { + return get_one(); + } + else if (is_zero(st)) { + return get_zero(); + } else { init_left_side(st); theory_var v = mk_var(term); lp::var_index vi = m_theory_var2var_index.get(v, UINT_MAX); - TRACE("arith", tout << mk_pp(term, m) << " " << v << " " << vi << "\n";); + TRACE("arith", tout << mk_pp(term, m) << " v" << v << "\n";); if (vi == UINT_MAX) { - vi = m_solver->add_term(m_left_side, st.coeff()); + rational const& offset = st.offset(); + if (!offset.is_zero()) { + m_left_side.push_back(std::make_pair(offset, get_one())); + } + SASSERT(!m_left_side.empty()); + vi = m_solver->add_term(m_left_side); m_theory_var2var_index.setx(v, vi, UINT_MAX); if (m_solver->is_term(vi)) { m_term_index2theory_var.setx(m_solver->adjust_term_index(vi), v, UINT_MAX); @@ -806,6 +852,8 @@ public: m_has_int(false), m_arith_eq_adapter(th, ap, a), m_internalize_head(0), + m_one_var(UINT_MAX), + m_zero_var(UINT_MAX), m_not_handled(nullptr), m_asserted_qhead(0), m_assume_eq_head(0), @@ -879,7 +927,7 @@ public: } void internalize_eq_eh(app * atom, bool_var) { - expr* lhs = 0, *rhs = 0; + expr* lhs = nullptr, *rhs = nullptr; VERIFY(m.is_eq(atom, lhs, rhs)); enode * n1 = get_enode(lhs); enode * n2 = get_enode(rhs); @@ -1197,7 +1245,6 @@ public: m_todo_terms.pop_back(); if (m_solver->is_term(vi)) { const lp::lar_term& term = m_solver->get_term(vi); - result += term.m_v * coeff; for (const auto & i: term) { m_todo_terms.push_back(std::make_pair(i.var(), coeff * i.coeff())); } @@ -1234,7 +1281,6 @@ public: m_todo_terms.pop_back(); if (m_solver->is_term(wi)) { const lp::lar_term& term = m_solver->get_term(wi); - result += term.m_v * coeff; for (const auto & i : term) { if (m_variable_values.count(i.var()) > 0) { result += m_variable_values[i.var()] * coeff * i.coeff(); @@ -1481,8 +1527,8 @@ public: bool all_bounded = true; for (unsigned v = 0; v < nv; ++v) { lp::var_index vi = m_theory_var2var_index[v]; - if (vi == UINT_MAX) - continue; + if (vi == UINT_MAX) + continue; if (!m_solver->is_term(vi) && !var_has_bound(vi, true) && !var_has_bound(vi, false)) { lp::lar_term term; term.add_monomial(rational::one(), vi); @@ -1516,23 +1562,10 @@ public: theory_var v = mk_var(n); theory_var v1 = mk_var(p); theory_var v2 = mk_var(q); - rational r = get_value(v); rational r1 = get_value(v1); - rational r2 = get_value(v2); - rational r3; - if (r2.is_zero()) { - continue; - } - if (r1.is_int() && r2.is_int() && r == div(r1, r2)) { - continue; - } - if (r2.is_neg() || r1.is_neg()) { - // TBD - continue; - } + rational r2; - if (!r1.is_int() || !r2.is_int()) { - // std::cout << r1 << " " << r2 << " " << r << " " << expr_ref(n, m) << "\n"; + if (!r1.is_int() || r1.is_neg()) { // TBD // r1 = 223/4, r2 = 2, r = 219/8 // take ceil(r1), floor(r1), ceil(r2), floor(r2), for floor(r2) > 0 @@ -1542,16 +1575,18 @@ public: continue; } - if (a.is_numeral(q, r3)) { + if (a.is_numeral(q, r2) && r2.is_pos()) { + if (get_value(v) == div(r1, r2)) continue; - SASSERT(r3 == r2 && r2.is_int()); - SASSERT(r1.is_int() && r3.is_int()); rational div_r = div(r1, r2); // p <= q * div(r1, q) + q - 1 => div(p, q) <= div(r1, r2) // p >= q * div(r1, q) => div(r1, q) <= div(p, q) rational mul(1); rational hi = r2 * div_r + r2 - 1; rational lo = r2 * div_r; + + // used to normalize inequalities so they + // don't appear as 8*x >= 15, but x >= 2 expr *n1 = nullptr, *n2 = nullptr; if (a.is_mul(p, n1, n2) && is_numeral(n1, mul) && mul.is_pos()) { p = n2; @@ -1568,7 +1603,7 @@ public: all_divs_valid = false; TRACE("arith", - tout << r1 << " div " << r2 << " = " << r3 << "\n"; + tout << r1 << " div " << r2 << "\n"; literal_vector lits; lits.push_back(~p_le_r1); lits.push_back(n_le_div); @@ -1578,8 +1613,10 @@ public: ctx().display_literals_verbose(tout, lits) << "\n";); continue; } +#if 0 - + // TBD similar for non-linear division. + // better to deal with in nla_solver: all_divs_valid = false; @@ -1610,6 +1647,7 @@ public: lits[0] = pq_rhs; lits[1] = n_ge_div; ctx().display_literals_verbose(tout, lits) << "\n";); +#endif } return all_divs_valid; @@ -1629,7 +1667,6 @@ public: expr_ref term2expr(lp::lar_term const& term) { expr_ref t(m); expr_ref_vector ts(m); - ts.push_back(a.mk_numeral(term.m_v, true)); for (auto const& p : term) { lp::var_index wi = p.var(); if (m_solver->is_term(wi)) { @@ -1709,17 +1746,13 @@ public: TRACE("arith", tout << "idiv bounds check\n";); return l_false; } - lp::lar_term term; - lp::mpq k; - lp::explanation ex; // TBD, this should be streamlined accross different explanations - bool upper; m_explanation.reset(); - switch(m_lia->check(term, k, ex, upper)) { + switch (m_lia->check()) { case lp::lia_move::sat: return l_true; case lp::lia_move::branch: { TRACE("arith", tout << "branch\n";); - app_ref b = mk_bound(term, k, !upper); + app_ref b = mk_bound(m_lia->get_term(), m_lia->get_offset(), !m_lia->is_upper()); IF_VERBOSE(2, verbose_stream() << "branch " << b << "\n";); // branch on term >= k + 1 // branch on term <= k @@ -1732,13 +1765,13 @@ public: TRACE("arith", tout << "cut\n";); ++m_stats.m_gomory_cuts; // m_explanation implies term <= k - app_ref b = mk_bound(term, k, !upper); + app_ref b = mk_bound(m_lia->get_term(), m_lia->get_offset(), !m_lia->is_upper()); IF_VERBOSE(2, verbose_stream() << "cut " << b << "\n"); - TRACE("arith", dump_cut_lemma(tout, term, k, ex, upper);); + TRACE("arith", dump_cut_lemma(tout, m_lia->get_term(), m_lia->get_offset(), m_lia->get_explanation(), m_lia->is_upper());); m_eqs.reset(); m_core.reset(); m_params.reset(); - for (auto const& ev : ex.m_explanation) { + for (auto const& ev : m_lia->get_explanation().m_explanation) { if (!ev.first.is_zero()) { set_evidence(ev.second); } @@ -1753,7 +1786,7 @@ public: case lp::lia_move::conflict: TRACE("arith", tout << "conflict\n";); // ex contains unsat core - m_explanation = ex.m_explanation; + m_explanation = m_lia->get_explanation().m_explanation; set_conflict1(); return l_false; case lp::lia_move::undef: @@ -2922,7 +2955,7 @@ public: lp::lar_term const& term = m_solver->get_term(vi); TRACE("arith", m_solver->print_term(term, tout) << "\n";); scoped_anum r1(m_nra->am()); - rational c1 = term.m_v * wcoeff; + rational c1(0); m_nra->am().set(r1, c1.to_mpq()); m_nra->am().add(r, r1, r); for (auto const & arg : term) { @@ -3197,7 +3230,6 @@ public: coeffs.find(w, c0); coeffs.insert(w, c0 + ti.coeff() * coeff); } - offset += coeff * term.m_v; } app_ref coeffs2app(u_map const& coeffs, rational const& offset, bool is_int) { diff --git a/src/util/lp/bound_propagator.cpp b/src/util/lp/bound_propagator.cpp index c4fa2aefa..a5c7c976a 100644 --- a/src/util/lp/bound_propagator.cpp +++ b/src/util/lp/bound_propagator.cpp @@ -17,10 +17,6 @@ const impq & bound_propagator::get_upper_bound(unsigned j) const { } void bound_propagator::try_add_bound(mpq v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) { j = m_lar_solver.adjust_column_index_to_term_index(j); - if (m_lar_solver.is_term(j)) { - // lp treats terms as not having a free coefficient, restoring it below for the outside consumption - v += m_lar_solver.get_term(j).m_v; - } lconstraint_kind kind = is_low? GE : LE; if (strict) diff --git a/src/util/lp/gomory.cpp b/src/util/lp/gomory.cpp index 2136f5f3e..96b3ab395 100644 --- a/src/util/lp/gomory.cpp +++ b/src/util/lp/gomory.cpp @@ -45,7 +45,7 @@ class gomory::imp { void int_case_in_gomory_cut(const mpq & a, unsigned j, mpq & lcm_den, const mpq& f0, const mpq& one_minus_f0) { lp_assert(is_int(j) && !a.is_int()); - mpq fj = fractional_part(a); + mpq fj = fractional_part(a); TRACE("gomory_cut_detail", tout << a << " j=" << j << " k = " << m_k; tout << ", fj: " << fj << ", "; @@ -56,10 +56,9 @@ class gomory::imp { mpq new_a; if (at_lower(j)) { new_a = fj <= one_minus_f0 ? fj / one_minus_f0 : ((1 - fj) / f0); - m_k.addmul(new_a, lower_bound(j).x); - // m_k += (new_a * lower_bound(j).x); lp_assert(new_a.is_pos()); - m_ex.push_justification(column_lower_bound_constraint(j), new_a); + m_k.addmul(new_a, lower_bound(j).x); + m_ex.push_justification(column_lower_bound_constraint(j), new_a); } else { lp_assert(at_upper(j)); @@ -67,7 +66,6 @@ class gomory::imp { new_a = - (fj <= f0 ? fj / f0 : ((1 - fj) / one_minus_f0)); lp_assert(new_a.is_neg()); m_k.addmul(new_a, upper_bound(j).x); - // m_k += (new_a * upper_bound(j).x); m_ex.push_justification(column_upper_bound_constraint(j), new_a); } m_t.add_monomial(new_a, j); @@ -251,9 +249,12 @@ class gomory::imp { std::ostream& dump_term_le_k(std::ostream & out) const { return dump_term_sum(out << "(<= ") << " " << m_k << ")"; } + void dump_the_cut_assert(std::ostream & out) const { dump_term_le_k(out << "(assert (not ") << "))\n"; } + + void dump_cut_and_constraints_as_smt_lemma(std::ostream& out) const { dump_declarations(out); dump_the_row(out); @@ -284,7 +285,6 @@ public: mpq one_min_f0 = 1 - f0; for (const auto & p : m_row) { unsigned j = p.var(); - if (j == m_inf_col) { lp_assert(p.coeff() == one_of_type()); TRACE("gomory_cut_detail", tout << "seeing basic var";); @@ -306,11 +306,11 @@ public: adjust_term_and_k_for_some_ints_case_gomory(lcm_den); lp_assert(m_int_solver.current_solution_is_inf_on_cut()); TRACE("gomory_cut_detail", dump_cut_and_constraints_as_smt_lemma(tout);); - m_int_solver.m_lar_solver->subs_term_columns(m_t, m_k); - TRACE("gomory_cut_detail", dump_cut_and_constraints_as_smt_lemma(tout);); + m_int_solver.m_lar_solver->subs_term_columns(m_t); TRACE("gomory_cut", print_linear_combination_of_column_indices_only(m_t, tout << "gomory cut:"); tout << " <= " << m_k << std::endl;); return lia_move::cut; } + imp(lar_term & t, mpq & k, explanation& ex, unsigned basic_inf_int_j, const row_strip& row, const int_solver& int_slv ) : m_t(t), m_k(k), diff --git a/src/util/lp/int_solver.cpp b/src/util/lp/int_solver.cpp index cd3a88669..83fbe3961 100644 --- a/src/util/lp/int_solver.cpp +++ b/src/util/lp/int_solver.cpp @@ -125,19 +125,19 @@ constraint_index int_solver::column_lower_bound_constraint(unsigned j) const { bool int_solver::current_solution_is_inf_on_cut() const { const auto & x = m_lar_solver->m_mpq_lar_core_solver.m_r_x; - impq v = m_t->apply(x); - mpq sign = *m_upper ? one_of_type() : -one_of_type(); - CTRACE("current_solution_is_inf_on_cut", v * sign <= (*m_k) * sign, - tout << "m_upper = " << *m_upper << std::endl; - tout << "v = " << v << ", k = " << (*m_k) << std::endl; + impq v = m_t.apply(x); + mpq sign = m_upper ? one_of_type() : -one_of_type(); + CTRACE("current_solution_is_inf_on_cut", v * sign <= m_k * sign, + tout << "m_upper = " << m_upper << std::endl; + tout << "v = " << v << ", k = " << m_k << std::endl; ); - return v * sign > (*m_k) * sign; + return v * sign > m_k * sign; } lia_move int_solver::mk_gomory_cut( unsigned inf_col, const row_strip & row) { lp_assert(column_is_int_inf(inf_col)); - gomory gc(*m_t, *m_k, *m_ex, inf_col, row, *this); + gomory gc(m_t, m_k, m_ex, inf_col, row, *this); return gc.create_cut(); } @@ -147,7 +147,7 @@ lia_move int_solver::proceed_with_gomory_cut(unsigned j) { if (!is_gomory_cut_target(row)) return create_branch_on_column(j); - *m_upper = true; + m_upper = true; return mk_gomory_cut(j, row); } @@ -373,21 +373,21 @@ lia_move int_solver::make_hnf_cut() { #else vector x0; #endif - lia_move r = m_hnf_cutter.create_cut(*m_t, *m_k, *m_ex, *m_upper, x0); + lia_move r = m_hnf_cutter.create_cut(m_t, m_k, m_ex, m_upper, x0); if (r == lia_move::cut) { TRACE("hnf_cut", - m_lar_solver->print_term(*m_t, tout << "cut:"); - tout << " <= " << *m_k << std::endl; + m_lar_solver->print_term(m_t, tout << "cut:"); + tout << " <= " << m_k << std::endl; for (unsigned i : m_hnf_cutter.constraints_for_explanation()) { m_lar_solver->print_constraint(i, tout); } ); lp_assert(current_solution_is_inf_on_cut()); settings().st().m_hnf_cuts++; - m_ex->clear(); + m_ex.clear(); for (unsigned i : m_hnf_cutter.constraints_for_explanation()) { - m_ex->push_justification(i); + m_ex.push_justification(i); } } return r; @@ -403,10 +403,13 @@ lia_move int_solver::hnf_cut() { return lia_move::undef; } -lia_move int_solver::check(lar_term& t, mpq& k, explanation& ex, bool & upper) { +lia_move int_solver::check() { if (!has_inf_int()) return lia_move::sat; - m_t = &t; m_k = &k; m_ex = &ex; m_upper = &upper; + m_t.clear(); + m_k.reset(); + m_ex.clear(); + m_upper = false; lia_move r = run_gcd_test(); if (r != lia_move::undef) return r; @@ -646,8 +649,8 @@ bool int_solver::gcd_test_for_row(static_matrix> & A, uns void int_solver::add_to_explanation_from_fixed_or_boxed_column(unsigned j) { constraint_index lc, uc; m_lar_solver->get_bound_constraint_witnesses_for_column(j, lc, uc); - m_ex->m_explanation.push_back(std::make_pair(mpq(1), lc)); - m_ex->m_explanation.push_back(std::make_pair(mpq(1), uc)); + m_ex.m_explanation.push_back(std::make_pair(mpq(1), lc)); + m_ex.m_explanation.push_back(std::make_pair(mpq(1), uc)); } void int_solver::fill_explanation_from_fixed_columns(const row_strip & row) { for (const auto & c : row) { @@ -1042,20 +1045,20 @@ const impq& int_solver::lower_bound(unsigned j) const { lia_move int_solver::create_branch_on_column(int j) { TRACE("check_main_int", tout << "branching" << std::endl;); - lp_assert(m_t->is_empty()); + lp_assert(m_t.is_empty()); lp_assert(j != -1); - m_t->add_monomial(mpq(1), m_lar_solver->adjust_column_index_to_term_index(j)); + m_t.add_monomial(mpq(1), m_lar_solver->adjust_column_index_to_term_index(j)); if (is_free(j)) { - *m_upper = true; - *m_k = mpq(0); + m_upper = true; + m_k = mpq(0); } else { - *m_upper = left_branch_is_more_narrow_than_right(j); - *m_k = *m_upper? floor(get_value(j)) : ceil(get_value(j)); + m_upper = left_branch_is_more_narrow_than_right(j); + m_k = m_upper? floor(get_value(j)) : ceil(get_value(j)); } TRACE("arith_int", tout << "branching v" << j << " = " << get_value(j) << "\n"; display_column(tout, j); - tout << "k = " << *m_k << std::endl; + tout << "k = " << m_k << std::endl; ); return lia_move::branch; diff --git a/src/util/lp/int_solver.h b/src/util/lp/int_solver.h index 013f53ce0..17ce20481 100644 --- a/src/util/lp/int_solver.h +++ b/src/util/lp/int_solver.h @@ -39,19 +39,23 @@ public: // fields lar_solver *m_lar_solver; unsigned m_number_of_calls; - lar_term *m_t; // the term to return in the cut - mpq *m_k; // the right side of the cut - explanation *m_ex; // the conflict explanation - bool *m_upper; // we have a cut m_t*x <= k if m_upper is true nad m_t*x >= k otherwise + lar_term m_t; // the term to return in the cut + mpq m_k; // the right side of the cut + explanation m_ex; // the conflict explanation + bool m_upper; // we have a cut m_t*x <= k if m_upper is true nad m_t*x >= k otherwise hnf_cutter m_hnf_cutter; // methods int_solver(lar_solver* lp); // main function to check that the solution provided by lar_solver is valid for integral values, // or provide a way of how it can be adjusted. - lia_move check(lar_term& t, mpq& k, explanation& ex, bool & upper); + lia_move check(); + lar_term const& get_term() const { return m_t; } + mpq const& get_offset() const { return m_k; } + explanation const& get_explanation() const { return m_ex; } + bool is_upper() const { return m_upper; } + bool move_non_basic_column_to_bounds(unsigned j); - lia_move check_wrapper(lar_term& t, mpq& k, explanation& ex); bool is_base(unsigned j) const; bool is_real(unsigned j) const; const impq & lower_bound(unsigned j) const; diff --git a/src/util/lp/lar_constraints.h b/src/util/lp/lar_constraints.h index ac15028bb..6305089b4 100644 --- a/src/util/lp/lar_constraints.h +++ b/src/util/lp/lar_constraints.h @@ -75,7 +75,7 @@ struct lar_term_constraint: public lar_base_constraint { } unsigned size() const override { return m_term->size();} lar_term_constraint(const lar_term *t, lconstraint_kind kind, const mpq& right_side) : lar_base_constraint(kind, right_side), m_term(t) { } - mpq get_free_coeff_of_left_side() const override { return m_term->m_v;} + // mpq get_free_coeff_of_left_side() const override { return m_term->m_v;} }; diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp index 56a61177c..30494aa1c 100644 --- a/src/util/lp/lar_solver.cpp +++ b/src/util/lp/lar_solver.cpp @@ -137,7 +137,7 @@ bool lar_solver::implied_bound_is_correctly_explained(implied_bound const & be, kind = static_cast(-kind); } rs_of_evidence /= ratio; - rs_of_evidence += t->m_v * ratio; + // rs_of_evidence += t->m_v * ratio; } return kind == be.kind() && rs_of_evidence == be.m_bound; @@ -602,7 +602,7 @@ void lar_solver::register_monoid_in_map(std::unordered_map & coe void lar_solver::substitute_terms_in_linear_expression(const vector>& left_side_with_terms, - vector> &left_side, mpq & free_coeff) const { + vector> &left_side) const { std::unordered_map coeffs; for (auto & t : left_side_with_terms) { unsigned j = t.second; @@ -613,7 +613,6 @@ void lar_solver::substitute_terms_in_linear_expression(const vector= constr.m_right_side; - case GT: return left_side_val > constr.m_right_side; + case GT: return left_side_val > constr.m_right_side; case EQ: return left_side_val == constr.m_right_side; default: lp_unreachable(); @@ -976,8 +975,10 @@ bool lar_solver::the_relations_are_of_same_type(const vectorm_kind); if (kind == GT || kind == LT) strict = true; - if (kind == GE || kind == GT) n_of_G++; - else if (kind == LE || kind == LT) n_of_L++; + if (kind == GE || kind == GT) + n_of_G++; + else if (kind == LE || kind == LT) + n_of_L++; } the_kind_of_sum = n_of_G ? GE : (n_of_L ? LE : EQ); if (strict) @@ -1117,7 +1118,7 @@ bool lar_solver::has_upper_bound(var_index var, constraint_index& ci, mpq& value bool lar_solver::has_value(var_index var, mpq& value) const { if (is_term(var)) { lar_term const& t = get_term(var); - value = t.m_v; + value = 0; for (auto const& cv : t) { impq const& r = get_column_value(cv.var()); if (!numeric_traits::is_zero(r.y)) return false; @@ -1229,8 +1230,7 @@ std::ostream& lar_solver::print_constraints(std::ostream& out) const { std::ostream& lar_solver::print_terms(std::ostream& out) const { for (auto it : m_terms) { - print_term(*it, out); - out << "\n"; + print_term(*it, out) << "\n"; } return out; } @@ -1244,9 +1244,6 @@ std::ostream& lar_solver::print_left_side_of_constraint(const lar_base_constrain } std::ostream& lar_solver::print_term(lar_term const& term, std::ostream & out) const { - if (!numeric_traits::is_zero(term.m_v)) { - out << term.m_v << " + "; - } bool first = true; for (const auto p : term) { mpq val = p.coeff(); @@ -1270,9 +1267,6 @@ std::ostream& lar_solver::print_term(lar_term const& term, std::ostream & out) c } std::ostream& lar_solver::print_term_as_indices(lar_term const& term, std::ostream & out) const { - if (!numeric_traits::is_zero(term.m_v)) { - out << term.m_v << " + "; - } print_linear_combination_of_column_indices_only(term, out); return out; } @@ -1497,7 +1491,7 @@ bool lar_solver::term_is_int(const lar_term * t) const { for (auto const & p : t->m_coeffs) if (! (column_is_int(p.first) && p.second.is_int())) return false; - return t->m_v.is_int(); + return true; } bool lar_solver::var_is_int(var_index v) const { @@ -1598,17 +1592,13 @@ void lar_solver::add_new_var_to_core_fields_for_mpq(bool register_in_basis) { } -var_index lar_solver::add_term_undecided(const vector> & coeffs, - const mpq &m_v) { - push_and_register_term(new lar_term(coeffs, m_v)); +var_index lar_solver::add_term_undecided(const vector> & coeffs) { + push_and_register_term(new lar_term(coeffs)); return m_terms_start_index + m_terms.size() - 1; } #if Z3DEBUG_CHECK_UNIQUE_TERMS -bool lar_solver::term_coeffs_are_ok(const vector> & coeffs, const mpq& v) { - if (coeffs.empty()) { - return is_zero(v); - } +bool lar_solver::term_coeffs_are_ok(const vector> & coeffs) { for (const auto & p : coeffs) { if (column_is_real(p.second)) @@ -1643,12 +1633,11 @@ void lar_solver::push_and_register_term(lar_term* t) { } // terms -var_index lar_solver::add_term(const vector> & coeffs, - const mpq &m_v) { +var_index lar_solver::add_term(const vector> & coeffs) { if (strategy_is_undecided()) - return add_term_undecided(coeffs, m_v); + return add_term_undecided(coeffs); - push_and_register_term(new lar_term(coeffs, m_v)); + push_and_register_term(new lar_term(coeffs)); unsigned adjusted_term_index = m_terms.size() - 1; var_index ret = m_terms_start_index + adjusted_term_index; if (use_tableau() && !coeffs.empty()) { @@ -1656,13 +1645,12 @@ var_index lar_solver::add_term(const vector> & coeffs, if (m_settings.bound_propagation()) m_rows_with_changed_bounds.insert(A_r().row_count() - 1); } - CTRACE("add_term_lar_solver", !m_v.is_zero(), print_term(*m_terms.back(), tout);); lp_assert(m_var_register.size() == A_r().column_count()); return ret; } void lar_solver::add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index) { - TRACE("dump_terms", print_term(*term, tout); tout << std::endl;); + TRACE("dump_terms", print_term(*term, tout) << std::endl;); register_new_ext_var_index(term_ext_index, term_is_int(term)); // j will be a new variable unsigned j = A_r().column_count(); @@ -1744,9 +1732,8 @@ void lar_solver::add_var_bound_on_constraint_for_term(var_index j, lconstraint_k // lp_assert(!term_is_int(m_terms[adjusted_term_index]) || right_side.is_int()); unsigned term_j; if (m_var_register.external_is_used(j, term_j)) { - mpq rs = right_side - m_terms[adjusted_term_index]->m_v; m_constraints.push_back(new lar_term_constraint(m_terms[adjusted_term_index], kind, right_side)); - update_column_type_and_bound(term_j, kind, rs, ci); + update_column_type_and_bound(term_j, kind, right_side, ci); } else { add_constraint_from_term_and_create_new_column_row(j, m_terms[adjusted_term_index], kind, right_side); @@ -1755,11 +1742,10 @@ void lar_solver::add_var_bound_on_constraint_for_term(var_index j, lconstraint_k constraint_index lar_solver::add_constraint(const vector>& left_side_with_terms, lconstraint_kind kind_par, const mpq& right_side_parm) { vector> left_side; - mpq rs = -right_side_parm; - substitute_terms_in_linear_expression(left_side_with_terms, left_side, rs); - unsigned term_index = add_term(left_side, zero_of_type()); + substitute_terms_in_linear_expression(left_side_with_terms, left_side); + unsigned term_index = add_term(left_side); constraint_index ci = m_constraints.size(); - add_var_bound_on_constraint_for_term(term_index, kind_par, -rs, ci); + add_var_bound_on_constraint_for_term(term_index, kind_par, right_side_parm, ci); return ci; } @@ -1768,7 +1754,7 @@ void lar_solver::add_constraint_from_term_and_create_new_column_row(unsigned ter add_row_from_term_no_constraint(term, term_j); unsigned j = A_r().column_count() - 1; - update_column_type_and_bound(j, kind, right_side - term->m_v, m_constraints.size()); + update_column_type_and_bound(j, kind, right_side, m_constraints.size()); m_constraints.push_back(new lar_term_constraint(term, kind, right_side)); lp_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size()); } @@ -2266,15 +2252,6 @@ void lar_solver::set_cut_strategy(unsigned cut_frequency) { } } -void lar_solver::adjust_cut_for_terms(const lar_term& t, mpq & rs) { - for (const auto& p : t) { - if (!is_term(p.var())) continue; - const lar_term & p_term = get_term(p.var()); - if (p_term.m_v.is_zero()) continue; - rs -= p.coeff() * p_term.m_v; - } -} - } // namespace lp diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index 9e9edacc8..f3aa4f23b 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -164,13 +164,11 @@ public: // terms - var_index add_term(const vector> & coeffs, - const mpq &m_v); + var_index add_term(const vector> & coeffs); - var_index add_term_undecided(const vector> & coeffs, - const mpq &m_v); + var_index add_term_undecided(const vector> & coeffs); - bool term_coeffs_are_ok(const vector> & coeffs, const mpq& v); + bool term_coeffs_are_ok(const vector> & coeffs); void push_and_register_term(lar_term* t); void add_row_for_term(const lar_term * term, unsigned term_ext_index); @@ -331,7 +329,7 @@ public: void substitute_terms_in_linear_expression( const vector>& left_side_with_terms, - vector> &left_side, mpq & free_coeff) const; + vector> &left_side) const; void detect_rows_of_bound_change_column_for_nbasic_column(unsigned j); @@ -534,7 +532,7 @@ public: return m_columns_to_ul_pairs()[j].lower_bound_witness(); } - void subs_term_columns(lar_term& t, mpq & rs) { + void subs_term_columns(lar_term& t) { vector> columns_to_subs; for (const auto & m : t.m_coeffs) { unsigned tj = adjust_column_index_to_term_index(m.first); @@ -548,7 +546,6 @@ public: mpq v = it->second; t.m_coeffs.erase(it); t.m_coeffs[p.second] = v; - rs -= v * lt.m_v; } } @@ -585,6 +582,5 @@ public: lar_term get_term_to_maximize(unsigned ext_j) const; void set_cut_strategy(unsigned cut_frequency); bool sum_first_coords(const lar_term& t, mpq & val) const; - void adjust_cut_for_terms(const lar_term& t, mpq & rs); }; } diff --git a/src/util/lp/lar_term.h b/src/util/lp/lar_term.h index 519847848..e9259b8c0 100644 --- a/src/util/lp/lar_term.h +++ b/src/util/lp/lar_term.h @@ -21,9 +21,9 @@ #include "util/lp/indexed_vector.h" namespace lp { struct lar_term { - // the term evaluates to sum of m_coeffs + m_v + // the term evaluates to sum of m_coeffs std::unordered_map m_coeffs; - mpq m_v; + // mpq m_v; lar_term() {} void add_monomial(const mpq& c, unsigned j) { auto it = m_coeffs.find(j); @@ -37,7 +37,7 @@ struct lar_term { } bool is_empty() const { - return m_coeffs.size() == 0 && is_zero(m_v); + return m_coeffs.size() == 0; // && is_zero(m_v); } unsigned size() const { return static_cast(m_coeffs.size()); } @@ -46,8 +46,7 @@ struct lar_term { return m_coeffs; } - lar_term(const vector>& coeffs, - const mpq & v) : m_v(v) { + lar_term(const vector>& coeffs) { for (const auto & p : coeffs) { add_monomial(p.first, p.second); } @@ -87,7 +86,7 @@ struct lar_term { template T apply(const vector& x) const { - T ret = T(m_v); + T ret(0); for (const auto & t : m_coeffs) { ret += t.second * x[t.first]; } @@ -96,7 +95,6 @@ struct lar_term { void clear() { m_coeffs.clear(); - m_v = zero_of_type(); } struct ival { From 91dbcbc36f1fe3ba51d6804e774ee70dd7b880cc Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 20 Sep 2018 18:57:47 -0700 Subject: [PATCH 066/138] fix test build Signed-off-by: Nikolaj Bjorner --- src/test/lp/gomory_test.h | 1 - src/test/lp/lp.cpp | 20 +++++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/test/lp/gomory_test.h b/src/test/lp/gomory_test.h index 501ad9e1a..972466dc3 100644 --- a/src/test/lp/gomory_test.h +++ b/src/test/lp/gomory_test.h @@ -185,7 +185,6 @@ struct gomory_test { } void print_term(lar_term & t, std::ostream & out) { - lp_assert(is_zero(t.m_v)); vector> row; for (auto p : t.m_coeffs) row.push_back(std::make_pair(p.second, p.first)); diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index 6e418fe68..ff9de0e58 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -2667,13 +2667,20 @@ void test_term() { lar_solver solver; unsigned _x = 0; unsigned _y = 1; + unsigned _one = 2; var_index x = solver.add_var(_x, false); var_index y = solver.add_var(_y, false); + var_index one = solver.add_var(_one, false); + + vector> term_one; + term_one.push_back(std::make_pair((int)1, one)); + solver.add_constraint(term_one, lconstraint_kind::EQ, mpq(0)); vector> term_ls; term_ls.push_back(std::pair((int)1, x)); term_ls.push_back(std::pair((int)1, y)); - var_index z = solver.add_term(term_ls, mpq(3)); + term_ls.push_back(std::make_pair((int)3, one)); + var_index z = solver.add_term(term_ls); vector> ls; ls.push_back(std::pair((int)1, x)); @@ -2743,10 +2750,10 @@ void test_bound_propagation_one_small_sample1() { vector> coeffs; coeffs.push_back(std::pair(1, a)); coeffs.push_back(std::pair(-1, c)); - ls.add_term(coeffs, zero_of_type()); + ls.add_term(coeffs); coeffs.pop_back(); coeffs.push_back(std::pair(-1, b)); - ls.add_term(coeffs, zero_of_type()); + ls.add_term(coeffs); coeffs.clear(); coeffs.push_back(std::pair(1, a)); coeffs.push_back(std::pair(-1, b)); @@ -3485,12 +3492,12 @@ void test_maximize_term() { vector> term_ls; term_ls.push_back(std::pair((int)1, x)); term_ls.push_back(std::pair((int)-1, y)); - unsigned term_x_min_y = solver.add_term(term_ls, mpq(0)); + unsigned term_x_min_y = solver.add_term(term_ls); term_ls.clear(); term_ls.push_back(std::pair((int)2, x)); term_ls.push_back(std::pair((int)2, y)); - unsigned term_2x_pl_2y = solver.add_term(term_ls, mpq(0)); + unsigned term_2x_pl_2y = solver.add_term(term_ls); solver.add_var_bound(term_x_min_y, LE, zero_of_type()); solver.add_var_bound(term_2x_pl_2y, LE, mpq((int)5)); solver.find_feasible_solution(); @@ -3502,8 +3509,7 @@ void test_maximize_term() { std::cout<< "v[" << p.first << "] = " << p.second << std::endl; } std::cout << "calling int_solver\n"; - lar_term t; mpq k; explanation ex; bool upper; - lia_move lm = i_solver.check(t, k, ex, upper); + lia_move lm = i_solver.check(); VERIFY(lm == lia_move::sat); impq term_max; lp_status st = solver.maximize_term(term_2x_pl_2y, term_max); From 382bce4bb79c4823af0605bd94c7cc824c641b4d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 20 Sep 2018 19:19:40 -0700 Subject: [PATCH 067/138] fix #1836 Signed-off-by: Nikolaj Bjorner --- src/api/ml/z3.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index 25b437bc4..d7d8aeddc 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1816,7 +1816,7 @@ struct let get_model x = let q = Z3native.solver_get_model (gc x) x in - if Z3native.is_null_model q then None else Some q + try if Z3native.is_null_model q then None else Some q with | _ -> None let get_proof x = let q = Z3native.solver_get_proof (gc x) x in From d6a1d17d695648e6e6b713323122a55a26283184 Mon Sep 17 00:00:00 2001 From: Daniel Selsam Date: Thu, 20 Sep 2018 16:28:45 -0700 Subject: [PATCH 068/138] extend(src/api/c++/z3++.h): support units() for solver class --- src/api/c++/z3++.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index e1f263e17..a65d6e4d8 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -2038,6 +2038,7 @@ namespace z3 { stats statistics() const { Z3_stats r = Z3_solver_get_statistics(ctx(), m_solver); check_error(); return stats(ctx(), r); } expr_vector unsat_core() const { Z3_ast_vector r = Z3_solver_get_unsat_core(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } expr_vector assertions() const { Z3_ast_vector r = Z3_solver_get_assertions(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } + expr_vector units() const { Z3_ast_vector r = Z3_solver_get_units(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } expr proof() const { Z3_ast r = Z3_solver_get_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); } friend std::ostream & operator<<(std::ostream & out, solver const & s); From 39ed27101ed9f465504f4e12e2bd2f676ea66fda Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 20 Sep 2018 19:56:55 -0700 Subject: [PATCH 069/138] include version.h in install include directory for cmake build #1833 Signed-off-by: Nikolaj Bjorner --- src/CMakeLists.txt | 2 ++ src/util/CMakeLists.txt | 1 + 2 files changed, 3 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 826f87e8c..c2d7d84a3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -166,6 +166,8 @@ foreach (header ${libz3_public_headers}) set_property(TARGET libz3 APPEND PROPERTY PUBLIC_HEADER "${CMAKE_SOURCE_DIR}/src/api/${header}") endforeach() +set_property(TARGET libz3 APPEND PROPERTY + PUBLIC_HEADER "${CMAKE_CURRENT_BINARY_DIR}/util/version.h") install(TARGETS libz3 EXPORT Z3_EXPORTED_TARGETS diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 85b6f955c..a84cc1f00 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -7,6 +7,7 @@ endif() set(Z3_FULL_VERSION "\"${Z3_FULL_VERSION_STR}\"") configure_file(version.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) + z3_add_component(util SOURCES approx_nat.cpp From c59a957737bdb04319df105cdd61130b228f23c6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 20 Sep 2018 20:37:14 -0700 Subject: [PATCH 070/138] add non-units method Signed-off-by: Nikolaj Bjorner --- src/api/api_solver.cpp | 15 +++++++++++++++ src/api/c++/z3++.h | 1 + src/api/z3_api.h | 8 ++++++++ src/solver/solver.cpp | 29 +++++++++++++++++++++++++++++ src/solver/solver.h | 2 ++ 5 files changed, 55 insertions(+) diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index fc42acbb9..5a4537a4a 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -372,6 +372,21 @@ extern "C" { Z3_CATCH_RETURN(0); } + Z3_ast_vector Z3_API Z3_solver_get_non_units(Z3_context c, Z3_solver s) { + Z3_TRY; + LOG_Z3_solver_get_non_units(c, s); + RESET_ERROR_CODE(); + init_solver(c, s); + Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); + mk_c(c)->save_object(v); + expr_ref_vector fmls = to_solver_ref(s)->get_non_units(mk_c(c)->m()); + for (expr* f : fmls) { + v->m_ast_vector.push_back(f); + } + RETURN_Z3(of_ast_vector(v)); + Z3_CATCH_RETURN(0); + } + static Z3_lbool _solver_check(Z3_context c, Z3_solver s, unsigned num_assumptions, Z3_ast const assumptions[]) { for (unsigned i = 0; i < num_assumptions; i++) { if (!is_expr(to_ast(assumptions[i]))) { diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index e1f263e17..378819682 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -2038,6 +2038,7 @@ namespace z3 { stats statistics() const { Z3_stats r = Z3_solver_get_statistics(ctx(), m_solver); check_error(); return stats(ctx(), r); } expr_vector unsat_core() const { Z3_ast_vector r = Z3_solver_get_unsat_core(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } expr_vector assertions() const { Z3_ast_vector r = Z3_solver_get_assertions(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } + expr_vector non_units() const { Z3_ast_vector r = Z3_solver_get_non_units(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } expr proof() const { Z3_ast r = Z3_solver_get_proof(ctx(), m_solver); check_error(); return expr(ctx(), r); } friend std::ostream & operator<<(std::ostream & out, solver const & s); diff --git a/src/api/z3_api.h b/src/api/z3_api.h index 2657e558d..03bce5d5e 100644 --- a/src/api/z3_api.h +++ b/src/api/z3_api.h @@ -6121,6 +6121,14 @@ extern "C" { */ Z3_ast_vector Z3_API Z3_solver_get_units(Z3_context c, Z3_solver s); + + /** + \brief Return the set of non units in the solver state. + + def_API('Z3_solver_get_non_units', AST_VECTOR, (_in(CONTEXT), _in(SOLVER))) + */ + Z3_ast_vector Z3_API Z3_solver_get_non_units(Z3_context c, Z3_solver s); + /** \brief Check whether the assertions in a given solver are consistent or not. diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index 4044c4a85..149fe0d65 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -256,3 +256,32 @@ expr_ref_vector solver::get_units(ast_manager& m) { return result; } + +expr_ref_vector solver::get_non_units(ast_manager& m) { + expr_ref_vector result(m), fmls(m); + get_assertions(fmls); + family_id bfid = m.get_basic_family_id(); + expr_mark marked; + for (unsigned i = 0; i < fmls.size(); ++i) { + expr* f = fmls.get(i); + if (marked.is_marked(f)) continue; + marked.mark(f); + if (!is_app(f)) { + result.push_back(f); + continue; + } + app* _f = to_app(f); + if (_f->get_family_id() == bfid) { + if (_f->get_num_args() > 0 && m.is_bool(_f->get_arg(0))) { + fmls.append(_f->get_num_args(), _f->get_args()); + } + else if (m.is_eq(f) || m.is_distinct(f)) { + result.push_back(f); + } + } + else { + result.push_back(f); + } + } + return result; +} diff --git a/src/solver/solver.h b/src/solver/solver.h index c371be284..5329161cd 100644 --- a/src/solver/solver.h +++ b/src/solver/solver.h @@ -236,6 +236,8 @@ public: */ expr_ref_vector get_units(ast_manager& m); + expr_ref_vector get_non_units(ast_manager& m); + class scoped_push { solver& s; bool m_nopop; From 0b7918c52eaadee049e28056b134e4621035d85f Mon Sep 17 00:00:00 2001 From: Nuno Lopes Date: Fri, 21 Sep 2018 09:37:36 +0100 Subject: [PATCH 071/138] remove spurious pragma --- src/smt/smt_arith_value.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/smt/smt_arith_value.cpp b/src/smt/smt_arith_value.cpp index ce4c0d9a9..41679851a 100644 --- a/src/smt/smt_arith_value.cpp +++ b/src/smt/smt_arith_value.cpp @@ -1,4 +1,3 @@ - /*++ Copyright (c) 2018 Microsoft Corporation @@ -17,7 +16,6 @@ Author: Revision History: --*/ -#pragma once; #include "smt/smt_arith_value.h" #include "smt/theory_lra.h" From 0c4754d94bdfaf07077120f5cbff780d8fb0971d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 21 Sep 2018 20:13:58 -0700 Subject: [PATCH 072/138] rename version.h to z3_version.h to differentiate name in install include directory. Add support for z3_version.h in python build system. #1833 Signed-off-by: Nikolaj Bjorner --- scripts/mk_project.py | 2 +- scripts/mk_util.py | 4 ++-- src/CMakeLists.txt | 2 +- src/shell/main.cpp | 2 +- src/solver/solver.cpp | 3 ++- src/util/CMakeLists.txt | 6 +++--- src/util/{version.h.cmake.in => z3_version.h.cmake.in} | 0 src/util/{version.h.in => z3_version.h.in} | 0 8 files changed, 10 insertions(+), 9 deletions(-) rename src/util/{version.h.cmake.in => z3_version.h.cmake.in} (100%) rename src/util/{version.h.in => z3_version.h.in} (100%) diff --git a/scripts/mk_project.py b/scripts/mk_project.py index ca62f5c5f..1ec5f05b5 100644 --- a/scripts/mk_project.py +++ b/scripts/mk_project.py @@ -10,7 +10,7 @@ from mk_util import * # Z3 Project definition def init_project_def(): set_version(4, 8, 0, 0) - add_lib('util', []) + add_lib('util', [], includes2install = ['z3_version.h']) add_lib('polynomial', ['util'], 'math/polynomial') add_lib('sat', ['util']) add_lib('nlsat', ['polynomial', 'sat']) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 770e118ee..ebe017739 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -2805,8 +2805,8 @@ def get_full_version_string(major, minor, build, revision): # Update files with the version number def mk_version_dot_h(major, minor, build, revision): c = get_component(UTIL_COMPONENT) - version_template = os.path.join(c.src_dir, 'version.h.in') - version_header_output = os.path.join(c.src_dir, 'version.h') + version_template = os.path.join(c.src_dir, 'z3_version.h.in') + version_header_output = os.path.join(c.src_dir, 'z3_version.h') # Note the substitution names are what is used by the CMake # builds system. If you change these you should change them # in the CMake build too diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c2d7d84a3..c497c19ee 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -167,7 +167,7 @@ foreach (header ${libz3_public_headers}) PUBLIC_HEADER "${CMAKE_SOURCE_DIR}/src/api/${header}") endforeach() set_property(TARGET libz3 APPEND PROPERTY - PUBLIC_HEADER "${CMAKE_CURRENT_BINARY_DIR}/util/version.h") + PUBLIC_HEADER "${CMAKE_CURRENT_BINARY_DIR}/util/z3_version.h") install(TARGETS libz3 EXPORT Z3_EXPORTED_TARGETS diff --git a/src/shell/main.cpp b/src/shell/main.cpp index bb1c19b47..1c8b6908d 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -26,7 +26,7 @@ Revision History: #include "shell/smtlib_frontend.h" #include "shell/z3_log_frontend.h" #include "util/warning.h" -#include "util/version.h" +#include "util/z3_version.h" #include "shell/dimacs_frontend.h" #include "shell/datalog_frontend.h" #include "shell/opt_frontend.h" diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index e4fe09adf..a7c1372b3 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -273,7 +273,8 @@ expr_ref_vector solver::get_non_units(ast_manager& m) { } app* _f = to_app(f); if (_f->get_family_id() == bfid) { - // basic objects are true/false/and/or/not/=/distinct and proof objects (that are not Boolean) + // basic objects are true/false/and/or/not/=/distinct + // and proof objects (that are not Boolean). if (_f->get_num_args() > 0 && m.is_bool(_f->get_arg(0))) { fmls.append(_f->get_num_args(), _f->get_args()); } diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index a84cc1f00..b6abb785f 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1,11 +1,11 @@ -if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/version.h") - message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/version.h\"" +if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/z3_version.h") + message(FATAL_ERROR "\"${CMAKE_CURRENT_SOURCE_DIR}/z3_version.h\"" ${z3_polluted_tree_msg} ) endif() set(Z3_FULL_VERSION "\"${Z3_FULL_VERSION_STR}\"") -configure_file(version.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/version.h) +configure_file(z3_version.h.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/z3_version.h) z3_add_component(util diff --git a/src/util/version.h.cmake.in b/src/util/z3_version.h.cmake.in similarity index 100% rename from src/util/version.h.cmake.in rename to src/util/z3_version.h.cmake.in diff --git a/src/util/version.h.in b/src/util/z3_version.h.in similarity index 100% rename from src/util/version.h.in rename to src/util/z3_version.h.in From e391416855aa5298c1ce880109e1b4fbfc2b7a49 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 21 Sep 2018 20:30:50 -0700 Subject: [PATCH 073/138] fix include path for z3_version.h Signed-off-by: Nikolaj Bjorner --- src/cmd_context/basic_cmds.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cmd_context/basic_cmds.cpp b/src/cmd_context/basic_cmds.cpp index e65eb1b32..ea5994ece 100644 --- a/src/cmd_context/basic_cmds.cpp +++ b/src/cmd_context/basic_cmds.cpp @@ -17,7 +17,7 @@ Notes: --*/ #include "util/gparams.h" #include "util/env_params.h" -#include "util/version.h" +#include "util/z3_version.h" #include "ast/ast_smt_pp.h" #include "ast/ast_smt2_pp.h" #include "ast/ast_pp_dot.h" From 8e0eebf507307b8a3ddc59f305a6486d78a5f4c5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 21 Sep 2018 20:37:13 -0700 Subject: [PATCH 074/138] fix include path for z3_version.h 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 cc2a13aed..4b3b85399 100644 --- a/src/api/api_context.cpp +++ b/src/api/api_context.cpp @@ -19,7 +19,7 @@ Revision History: --*/ #include #include "api/api_context.h" -#include "util/version.h" +#include "util/z3_version.h" #include "ast/ast_pp.h" #include "ast/ast_ll_pp.h" #include "api/api_log_macros.h" From 984e74428aedbc36ea5930ba78c9890594524444 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 21 Sep 2018 20:41:26 -0700 Subject: [PATCH 075/138] fix include path for z3_version.h Signed-off-by: Nikolaj Bjorner --- src/api/api_log.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/api_log.cpp b/src/api/api_log.cpp index 1bdbb8735..0f531b98e 100644 --- a/src/api/api_log.cpp +++ b/src/api/api_log.cpp @@ -19,7 +19,7 @@ Revision History: #include "api/z3.h" #include "api/api_log_macros.h" #include "util/util.h" -#include "util/version.h" +#include "util/z3_version.h" std::ostream * g_z3_log = nullptr; bool g_z3_log_enabled = false; From f349d3d0137c0ad09cf6881a4291743110b87630 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 21 Sep 2018 21:15:28 -0700 Subject: [PATCH 076/138] fix extraction of non-units Signed-off-by: Nikolaj Bjorner --- src/solver/solver.cpp | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index a7c1372b3..66fedb36f 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -257,6 +257,15 @@ expr_ref_vector solver::get_units(ast_manager& m) { return result; } +static bool is_atom(ast_manager& m, expr* f) { + if (!is_app(f)) return true; + app* _f = to_app(f); + family_id bfid = m.get_basic_family_id(); + if (_f->get_family_id() != bfid) return true; + if (_f->get_num_args() > 0 && m.is_bool(_f->get_arg(0))) return false; + return m.is_eq(f) || m.is_distinct(f); +} + expr_ref_vector solver::get_non_units(ast_manager& m) { expr_ref_vector result(m), fmls(m); get_assertions(fmls); @@ -275,13 +284,17 @@ expr_ref_vector solver::get_non_units(ast_manager& m) { if (_f->get_family_id() == bfid) { // basic objects are true/false/and/or/not/=/distinct // and proof objects (that are not Boolean). - if (_f->get_num_args() > 0 && m.is_bool(_f->get_arg(0))) { + if (i < sz0 && m.is_not(f) && is_atom(m, _f->get_arg(0))) { + marked.mark(_f->get_arg(0)); + } + else if (_f->get_num_args() > 0 && m.is_bool(_f->get_arg(0))) { fmls.append(_f->get_num_args(), _f->get_args()); } - else if (m.is_eq(f) || m.is_distinct(f)) { - if (i >= sz0) result.push_back(f); + else if (i >= sz0 && is_atom(m, f)) { + result.push_back(f); } } + else { if (i >= sz0) result.push_back(f); } From 3113901c8fecf70ce0284bce0797ab2f7013ee67 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 21 Sep 2018 23:15:57 -0700 Subject: [PATCH 077/138] rename is_atom Signed-off-by: Nikolaj Bjorner --- src/solver/solver.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index 66fedb36f..1f0dac0ce 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -257,7 +257,7 @@ expr_ref_vector solver::get_units(ast_manager& m) { return result; } -static bool is_atom(ast_manager& m, expr* f) { +static bool is_m_atom(ast_manager& m, expr* f) { if (!is_app(f)) return true; app* _f = to_app(f); family_id bfid = m.get_basic_family_id(); @@ -284,13 +284,13 @@ expr_ref_vector solver::get_non_units(ast_manager& m) { if (_f->get_family_id() == bfid) { // basic objects are true/false/and/or/not/=/distinct // and proof objects (that are not Boolean). - if (i < sz0 && m.is_not(f) && is_atom(m, _f->get_arg(0))) { + if (i < sz0 && m.is_not(f) && is_m_atom(m, _f->get_arg(0))) { marked.mark(_f->get_arg(0)); } else if (_f->get_num_args() > 0 && m.is_bool(_f->get_arg(0))) { fmls.append(_f->get_num_args(), _f->get_args()); } - else if (i >= sz0 && is_atom(m, f)) { + else if (i >= sz0 && is_m_atom(m, f)) { result.push_back(f); } } From 43f89dc2ccfbb24f2f13f394595abfd3e29b50bd Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 22 Sep 2018 12:01:24 -0700 Subject: [PATCH 078/138] changes in column_info of lar_solver Signed-off-by: Lev Nachmanson --- src/util/lp/column_info.h | 10 ---------- src/util/lp/lar_solver.cpp | 9 ++------- src/util/lp/lar_solver.h | 2 +- src/util/lp/lp_primal_core_solver_def.h | 1 + src/util/lp/lp_solver_def.h | 2 +- 5 files changed, 5 insertions(+), 19 deletions(-) diff --git a/src/util/lp/column_info.h b/src/util/lp/column_info.h index 407f40dfc..2a38900c1 100644 --- a/src/util/lp/column_info.h +++ b/src/util/lp/column_info.h @@ -69,16 +69,6 @@ public: m_column_index(static_cast(-1)) {} - column_info(unsigned column_index) : - m_lower_bound_is_set(false), - m_lower_bound_is_strict(false), - m_upper_bound_is_set (false), - m_upper_bound_is_strict (false), - m_is_fixed(false), - m_cost(numeric_traits::zero()), - m_column_index(column_index) { - } - column_info(const column_info & ci) { m_name = ci.m_name; m_lower_bound_is_set = ci.m_lower_bound_is_set; diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp index 30494aa1c..c83268602 100644 --- a/src/util/lp/lar_solver.cpp +++ b/src/util/lp/lar_solver.cpp @@ -909,13 +909,8 @@ bool lar_solver::try_to_set_fixed(column_info & ci) { return false; } -column_type lar_solver::get_column_type(const column_info & ci) { - auto ret = ci.get_column_type_no_flipping(); - if (ret == column_type::boxed) { // changing boxed to fixed because of the no span - if (ci.get_lower_bound() == ci.get_upper_bound()) - ret = column_type::fixed; - } - return ret; +column_type lar_solver::get_column_type(unsigned j) const{ + return m_mpq_lar_core_solver.m_column_types[j]; } std::string lar_solver::get_column_name(unsigned j) const { diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index f3aa4f23b..cfe581ba3 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -395,7 +395,7 @@ public: bool try_to_set_fixed(column_info & ci); - column_type get_column_type(const column_info & ci); + column_type get_column_type(unsigned j) const; std::string get_column_name(unsigned j) const; diff --git a/src/util/lp/lp_primal_core_solver_def.h b/src/util/lp/lp_primal_core_solver_def.h index 1e9edbd31..872922f60 100644 --- a/src/util/lp/lp_primal_core_solver_def.h +++ b/src/util/lp/lp_primal_core_solver_def.h @@ -1238,6 +1238,7 @@ template void lp_primal_core_solver::print_column break; case column_type::free_column: out << "( _" << this->m_x[j] << "_)" << std::endl; + break; default: lp_unreachable(); } diff --git a/src/util/lp/lp_solver_def.h b/src/util/lp/lp_solver_def.h index 10c7a6feb..9b385dee6 100644 --- a/src/util/lp/lp_solver_def.h +++ b/src/util/lp/lp_solver_def.h @@ -24,7 +24,7 @@ Revision History: namespace lp { template column_info * lp_solver::get_or_create_column_info(unsigned column) { auto it = m_map_from_var_index_to_column_info.find(column); - return (it == m_map_from_var_index_to_column_info.end())? (m_map_from_var_index_to_column_info[column] = new column_info(static_cast(-1))) : it->second; + return (it == m_map_from_var_index_to_column_info.end())? (m_map_from_var_index_to_column_info[column] = new column_info()) : it->second; } template From 7b3b1b6e9f70231d29981def70522913bc96ec20 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 22 Sep 2018 14:04:15 -0700 Subject: [PATCH 079/138] pop to base before incremental internalization to ensure that units are not lost Signed-off-by: Nikolaj Bjorner --- src/sat/sat_solver/inc_sat_solver.cpp | 3 ++- src/solver/solver.cpp | 21 +++++++++++---------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index ff55598c2..097d3f0fa 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -259,7 +259,7 @@ public: return m_num_scopes; } - void assert_expr_core2(expr * t, expr * a) override { + void assert_expr_core2(expr * t, expr * a) override { if (a) { m_asmsf.push_back(a); assert_expr_core(m.mk_implies(a, t)); @@ -473,6 +473,7 @@ public: } void convert_internalized() { + m_solver.pop_to_base_level(); if (!is_internalized() && m_fmls_head > 0) { internalize_formulas(); } diff --git a/src/solver/solver.cpp b/src/solver/solver.cpp index 1f0dac0ce..0e2128990 100644 --- a/src/solver/solver.cpp +++ b/src/solver/solver.cpp @@ -178,10 +178,19 @@ lbool solver::preferred_sat(expr_ref_vector const& asms, vector return check_sat(0, nullptr); } -bool solver::is_literal(ast_manager& m, expr* e) { - return is_uninterp_const(e) || (m.is_not(e, e) && is_uninterp_const(e)); + +static bool is_m_atom(ast_manager& m, expr* f) { + if (!is_app(f)) return true; + app* _f = to_app(f); + family_id bfid = m.get_basic_family_id(); + if (_f->get_family_id() != bfid) return true; + if (_f->get_num_args() > 0 && m.is_bool(_f->get_arg(0))) return false; + return m.is_eq(f) || m.is_distinct(f); } +bool solver::is_literal(ast_manager& m, expr* e) { + return is_m_atom(m, e) || (m.is_not(e, e) && is_m_atom(m, e)); +} void solver::assert_expr(expr* f) { expr_ref fml(f, get_manager()); @@ -257,14 +266,6 @@ expr_ref_vector solver::get_units(ast_manager& m) { return result; } -static bool is_m_atom(ast_manager& m, expr* f) { - if (!is_app(f)) return true; - app* _f = to_app(f); - family_id bfid = m.get_basic_family_id(); - if (_f->get_family_id() != bfid) return true; - if (_f->get_num_args() > 0 && m.is_bool(_f->get_arg(0))) return false; - return m.is_eq(f) || m.is_distinct(f); -} expr_ref_vector solver::get_non_units(ast_manager& m) { expr_ref_vector result(m), fmls(m); From 9a09689dfab27059332092bf329db0c4abc258b2 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 22 Sep 2018 19:19:05 -0700 Subject: [PATCH 080/138] add documentation on the cuber Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 11 ++++++++++- src/sat/sat_lookahead.cpp | 2 ++ src/sat/sat_params.pyg | 28 +++++++++++++++++++++++++--- 3 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index b9e15d329..a6b05904c 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -6605,7 +6605,12 @@ class Solver(Z3PPObject): _handle_parse_error(e, self.ctx) def cube(self, vars = None): - """Get set of cubes""" + """Get set of cubes + The method takes an optional set of variables that restrict which + variables may be used as a starting point for cubing. + If vars is not None, then the first case split is based on a variable in + this set. + """ self.cube_vs = AstVector(None, self.ctx) if vars is not None: for v in vars: @@ -6621,6 +6626,10 @@ class Solver(Z3PPObject): return def cube_vars(self): + """Access the set of variables that were touched by the most recently generated cube. + This set of variables can be used as a starting point for additional cubes. + The idea is that variables that appear in clauses that are reduced by the most recent + cube are likely more useful to cube on.""" return self.cube_vs def proof(self): diff --git a/src/sat/sat_lookahead.cpp b/src/sat/sat_lookahead.cpp index 3833e2a52..4ca2c5f84 100644 --- a/src/sat/sat_lookahead.cpp +++ b/src/sat/sat_lookahead.cpp @@ -16,6 +16,8 @@ Author: Notes: + + --*/ #include diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index 89776c479..113a8133e 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -50,17 +50,39 @@ def_module_params('sat', ('unit_walk', BOOL, False, 'use unit-walk search instead of CDCL'), ('unit_walk_threads', UINT, 0, 'number of unit-walk search threads to find satisfiable solution'), ('lookahead.cube.cutoff', SYMBOL, 'depth', 'cutoff type used to create lookahead cubes: depth, freevars, psat, adaptive_freevars, adaptive_psat'), + # - depth: the maximal cutoff is fixed to the value of lookahead.cube.depth. + # So if the value is 10, at most 1024 cubes will be generated of length 10. + # - freevars: cutoff based on a variable fraction of lookahead.cube.freevars. + # Cut if the number of current unassigned variables drops below a fraction of number of initial variables. + # - psat: Let psat_heur := (Sum_{clause C} (psat.clause_base ^ {-|C|+1})) / |freevars|^psat.var_exp + # Cut if the value of psat_heur exceeds psat.trigger + # - adaptive_freevars: Cut if the number of current unassigned variables drops below a fraction of free variables + # at the time of the last conflict. The fraction is increased every time the a cutoff is created. + # - adative_psat: Cut based on psat_heur in an adaptive way. ('lookahead.cube.fraction', DOUBLE, 0.4, 'adaptive fraction to create lookahead cubes. Used when lookahead.cube.cutoff is adaptive_freevars or adaptive_psat'), ('lookahead.cube.depth', UINT, 1, 'cut-off depth to create cubes. Used when lookahead.cube.cutoff is depth.'), - ('lookahead.cube.freevars', DOUBLE, 0.8, 'cube free fariable fraction. Used when lookahead.cube.cutoff is freevars'), + ('lookahead.cube.freevars', DOUBLE, 0.8, 'cube free variable fraction. Used when lookahead.cube.cutoff is freevars'), ('lookahead.cube.psat.var_exp', DOUBLE, 1, 'free variable exponent for PSAT cutoff'), ('lookahead.cube.psat.clause_base', DOUBLE, 2, 'clause base for PSAT cutoff'), ('lookahead.cube.psat.trigger', DOUBLE, 5, 'trigger value to create lookahead cubes for PSAT cutoff. Used when lookahead.cube.cutoff is psat'), - ('lookahead_search', BOOL, False, 'use lookahead solver'), ('lookahead.preselect', BOOL, False, 'use pre-selection of subset of variables for branching'), ('lookahead_simplify', BOOL, False, 'use lookahead solver during simplification'), ('lookahead.use_learned', BOOL, False, 'use learned clauses when selecting lookahead literal'), ('lookahead_simplify.bca', BOOL, True, 'add learned binary clauses as part of lookahead simplification'), ('lookahead.global_autarky', BOOL, False, 'prefer to branch on variables that occur in clauses that are reduced'), - ('lookahead.reward', SYMBOL, 'march_cu', 'select lookahead heuristic: ternary, heule_schur (Heule Schur), heuleu (Heule Unit), unit, or march_cu'))) + ('lookahead.reward', SYMBOL, 'march_cu', 'select lookahead heuristic: ternary, heule_schur (Heule Schur), heuleu (Heule Unit), unit, or march_cu')) + # reward function used to determine which literal to cube on. + # - ternary: reward function useful for random 3-SAT instances. Used by Heule and Knuth in March. + # - heule_schur: reward function based on "Schur Number 5", Heule, AAAI 2018 + # The score of a literal lit is: + # Sum_{C in Clauses | lit in C} 2 ^ (- |C|+1) + # * Sum_{lit' in C | lit' != lit} lit_occs(~lit') + # / | C | + # where lit_occs(lit) is the number of clauses containing lit. + # - heuleu: The score of a literal lit is: Sum_{C in Clauses | lit in C} 2 ^ (-|C| + 1) + # - unit: heule_schur + also counts number of unit clauses. + # - march_cu: default reward function used in a version of March + # Each reward function also comes with its own variant of "mix_diff", which + # is the function for combining reward metrics for the positive and negative variant of a literal. + ) From 066b5334ad5882dff635abd354928f14b9a3c4d2 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Sat, 22 Sep 2018 20:57:59 -0700 Subject: [PATCH 081/138] refactor some parameters into fields in Gomory cuts Signed-off-by: Lev Nachmanson --- src/util/lp/gomory.cpp | 119 +++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 57 deletions(-) diff --git a/src/util/lp/gomory.cpp b/src/util/lp/gomory.cpp index 96b3ab395..ad1c02625 100644 --- a/src/util/lp/gomory.cpp +++ b/src/util/lp/gomory.cpp @@ -27,11 +27,15 @@ class gomory::imp { lar_term & m_t; // the term to return in the cut mpq & m_k; // the right side of the cut explanation& m_ex; // the conflict explanation - unsigned m_inf_col; // a basis column which has to be an integer but has a not integral value + unsigned m_inf_col; // a basis column which has to be an integer but has a non integral value const row_strip& m_row; - const int_solver& m_int_solver; - - + const int_solver& m_int_solver; + mpq m_lcm_den; + mpq m_f; + mpq m_one_minus_f; + mpq m_fj; + mpq m_one_minus_fj; + const impq & get_value(unsigned j) const { return m_int_solver.get_value(j); } bool is_real(unsigned j) const { return m_int_solver.is_real(j); } bool at_lower(unsigned j) const { return m_int_solver.at_lower(j); } @@ -42,66 +46,60 @@ class gomory::imp { constraint_index column_upper_bound_constraint(unsigned j) const { return m_int_solver.column_upper_bound_constraint(j); } bool column_is_fixed(unsigned j) const { return m_int_solver.m_lar_solver->column_is_fixed(j); } - void int_case_in_gomory_cut(const mpq & a, unsigned j, - mpq & lcm_den, const mpq& f0, const mpq& one_minus_f0) { - lp_assert(is_int(j) && !a.is_int()); - mpq fj = fractional_part(a); + void int_case_in_gomory_cut(unsigned j) { + lp_assert(is_int(j) && m_fj.is_pos()); TRACE("gomory_cut_detail", - tout << a << " j=" << j << " k = " << m_k; - tout << ", fj: " << fj << ", "; - tout << "a - fj = " << a - fj << ", "; + tout << " k = " << m_k; + tout << ", fj: " << m_fj << ", "; tout << (at_lower(j)?"at_lower":"at_upper")<< std::endl; ); - lp_assert(fj.is_pos() && (a - fj).is_int()); mpq new_a; if (at_lower(j)) { - new_a = fj <= one_minus_f0 ? fj / one_minus_f0 : ((1 - fj) / f0); + new_a = m_fj <= m_one_minus_f ? m_fj / m_one_minus_f : ((1 - m_fj) / m_f); lp_assert(new_a.is_pos()); m_k.addmul(new_a, lower_bound(j).x); - m_ex.push_justification(column_lower_bound_constraint(j), new_a); + m_ex.push_justification(column_lower_bound_constraint(j)); } else { lp_assert(at_upper(j)); // the upper terms are inverted: therefore we have the minus - new_a = - (fj <= f0 ? fj / f0 : ((1 - fj) / one_minus_f0)); + new_a = - (m_fj <= m_f ? m_fj / m_f : ((1 - m_fj) / m_one_minus_f)); lp_assert(new_a.is_neg()); m_k.addmul(new_a, upper_bound(j).x); - m_ex.push_justification(column_upper_bound_constraint(j), new_a); + m_ex.push_justification(column_upper_bound_constraint(j)); } m_t.add_monomial(new_a, j); - lcm_den = lcm(lcm_den, denominator(new_a)); - TRACE("gomory_cut_detail", tout << "v" << j << " new_a = " << new_a << ", k = " << m_k << ", lcm_den = " << lcm_den << "\n";); + m_lcm_den = lcm(m_lcm_den, denominator(new_a)); + TRACE("gomory_cut_detail", tout << "v" << j << " new_a = " << new_a << ", k = " << m_k << ", m_lcm_den = " << m_lcm_den << "\n";); } - void real_case_in_gomory_cut(const mpq & a, unsigned x_j, const mpq& f0, const mpq& one_minus_f0) { + void real_case_in_gomory_cut(const mpq & a, unsigned j) { TRACE("gomory_cut_detail_real", tout << "real\n";); mpq new_a; - if (at_lower(x_j)) { + if (at_lower(j)) { if (a.is_pos()) { - new_a = a / one_minus_f0; + new_a = a / m_one_minus_f; } else { - new_a = a / f0; - new_a.neg(); + new_a = - a / m_f; } - m_k.addmul(new_a, lower_bound(x_j).x); // is it a faster operation than - // k += lower_bound(x_j).x * new_a; - m_ex.push_justification(column_lower_bound_constraint(x_j), new_a); + m_k.addmul(new_a, lower_bound(j).x); // is it a faster operation than + // k += lower_bound(j).x * new_a; + m_ex.push_justification(column_lower_bound_constraint(j)); } else { - lp_assert(at_upper(x_j)); + lp_assert(at_upper(j)); if (a.is_pos()) { - new_a = a / f0; - new_a.neg(); // the upper terms are inverted. + new_a = - a / m_f; } else { - new_a = a / one_minus_f0; + new_a = a / m_one_minus_f; } - m_k.addmul(new_a, upper_bound(x_j).x); // k += upper_bound(x_j).x * new_a; - m_ex.push_justification(column_upper_bound_constraint(x_j), new_a); + m_k.addmul(new_a, upper_bound(j).x); // k += upper_bound(j).x * new_a; + m_ex.push_justification(column_upper_bound_constraint(j)); } - TRACE("gomory_cut_detail_real", tout << a << "*v" << x_j << " k: " << m_k << "\n";); - m_t.add_monomial(new_a, x_j); + TRACE("gomory_cut_detail_real", tout << a << "*v" << j << " k: " << m_k << "\n";); + m_t.add_monomial(new_a, j); } lia_move report_conflict_from_gomory_cut() { @@ -111,7 +109,7 @@ class gomory::imp { return lia_move::conflict; } - void adjust_term_and_k_for_some_ints_case_gomory(mpq &lcm_den) { + void adjust_term_and_k_for_some_ints_case_gomory() { lp_assert(!m_t.is_empty()); // k = 1 + sum of m_t at bounds auto pol = m_t.coeffs_as_vector(); @@ -134,16 +132,16 @@ class gomory::imp { m_t.add_monomial(mpq(1), v); } } else { - lcm_den = lcm(lcm_den, denominator(m_k)); - lp_assert(lcm_den.is_pos()); - TRACE("gomory_cut_detail", tout << "pol.size() > 1 den: " << lcm_den << std::endl;); - if (!lcm_den.is_one()) { + m_lcm_den = lcm(m_lcm_den, denominator(m_k)); + lp_assert(m_lcm_den.is_pos()); + TRACE("gomory_cut_detail", tout << "pol.size() > 1 den: " << m_lcm_den << std::endl;); + if (!m_lcm_den.is_one()) { // normalize coefficients of integer parameters to be integers. for (auto & pi: pol) { - pi.first *= lcm_den; + pi.first *= m_lcm_den; SASSERT(!is_int(pi.second) || pi.first.is_int()); } - m_k *= lcm_den; + m_k *= m_lcm_den; } // negate everything to return -pol <= -m_k for (const auto & pi: pol) { @@ -275,14 +273,14 @@ public: // gomory will be t <= k and the current solution has a property t > k m_k = 1; m_t.clear(); - mpq lcm_den(1); + mpq m_lcm_den(1); bool some_int_columns = false; - mpq f0 = fractional_part(get_value(m_inf_col)); - TRACE("gomory_cut_detail", tout << "f0: " << f0 << ", "; - tout << "1 - f0: " << 1 - f0 << ", get_value(m_inf_col).x - f0 = " << get_value(m_inf_col).x - f0;); - lp_assert(f0.is_pos() && (get_value(m_inf_col).x - f0).is_int()); + mpq m_f = fractional_part(get_value(m_inf_col)); + TRACE("gomory_cut_detail", tout << "m_f: " << m_f << ", "; + tout << "1 - m_f: " << 1 - m_f << ", get_value(m_inf_col).x - m_f = " << get_value(m_inf_col).x - m_f;); + lp_assert(m_f.is_pos() && (get_value(m_inf_col).x - m_f).is_int()); - mpq one_min_f0 = 1 - f0; + mpq one_min_m_f = 1 - m_f; for (const auto & p : m_row) { unsigned j = p.var(); if (j == m_inf_col) { @@ -290,20 +288,26 @@ public: TRACE("gomory_cut_detail", tout << "seeing basic var";); continue; } - // make the format compatible with the format used in: Integrating Simplex with DPLL(T) - mpq a = - p.coeff(); - if (is_real(j)) - real_case_in_gomory_cut(a, j, f0, one_min_f0); - else if (!a.is_int()) { // fj will be zero and no monomial will be added + + // use -p.coeff() to make the format compatible with the format used in: Integrating Simplex with DPLL(T) + if (is_real(j)) { + real_case_in_gomory_cut(- p.coeff(), j); + } else { + if (p.coeff().is_int()) { + // m_fj will be zero and no monomial will be added + continue; + } some_int_columns = true; - int_case_in_gomory_cut(a, j, lcm_den, f0, one_min_f0); + m_fj = fractional_part(-p.coeff()); + m_one_minus_fj = 1 - m_fj; + int_case_in_gomory_cut(j); } } if (m_t.is_empty()) return report_conflict_from_gomory_cut(); if (some_int_columns) - adjust_term_and_k_for_some_ints_case_gomory(lcm_den); + adjust_term_and_k_for_some_ints_case_gomory(); lp_assert(m_int_solver.current_solution_is_inf_on_cut()); TRACE("gomory_cut_detail", dump_cut_and_constraints_as_smt_lemma(tout);); m_int_solver.m_lar_solver->subs_term_columns(m_t); @@ -317,9 +321,10 @@ public: m_ex(ex), m_inf_col(basic_inf_int_j), m_row(row), - m_int_solver(int_slv) - { - } + m_int_solver(int_slv), + m_lcm_den(1), + m_f(fractional_part(get_value(basic_inf_int_j).x)), + m_one_minus_f(1 - m_f) {} }; From 80d0c5cf8217e344e1a18de7dadda53298927074 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 24 Sep 2018 16:52:25 -0700 Subject: [PATCH 082/138] fix #1836 again Signed-off-by: Nikolaj Bjorner --- src/api/ml/z3.ml | 12 ++++++++---- src/sat/sat_solver/inc_sat_solver.cpp | 4 ++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index d7d8aeddc..231587729 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1815,8 +1815,10 @@ struct | _ -> UNKNOWN let get_model x = - let q = Z3native.solver_get_model (gc x) x in - try if Z3native.is_null_model q then None else Some q with | _ -> None + try + let q = Z3native.solver_get_model (gc x) x in + if Z3native.is_null_model q then None else Some q + with | _ -> None let get_proof x = let q = Z3native.solver_get_proof (gc x) x in @@ -1952,8 +1954,10 @@ struct | _ -> Solver.UNKNOWN let get_model (x:optimize) = - let q = Z3native.optimize_get_model (gc x) x in - if Z3native.is_null_model q then None else Some q + try + let q = Z3native.optimize_get_model (gc x) x in + if Z3native.is_null_model q then None else Some q + with | _ -> None let get_lower (x:handle) = Z3native.optimize_get_lower (gc x.opt) x.opt x.h let get_upper (x:handle) = Z3native.optimize_get_upper (gc x.opt) x.opt x.h diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 097d3f0fa..f0fe44160 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -113,6 +113,7 @@ public: if (m_num_scopes > 0) { throw default_exception("Cannot translate sat solver at non-base level"); } + std::cout << "translate\n"; std::cout.flush(); ast_translation tr(m, dst_m); m_solver.pop_to_base_level(); inc_sat_solver* result = alloc(inc_sat_solver, dst_m, p, is_incremental()); @@ -167,6 +168,7 @@ public: lbool check_sat(unsigned sz, expr * const * assumptions) override { m_solver.pop_to_base_level(); m_core.reset(); + std::cout << "#inconsistent: " << m_solver.inconsistent() << "\n"; if (m_solver.inconsistent()) return l_false; expr_ref_vector _assumptions(m); obj_map asm2fml; @@ -777,6 +779,8 @@ private: } m_core.push_back(e); } + std::cout << "core " << core << "\n"; + std::cout.flush(); } void check_assumptions(dep2asm_t& dep2asm) { From 7335b3bf565a0c6a50bd98c21fab67bee0ff810b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 24 Sep 2018 16:53:15 -0700 Subject: [PATCH 083/138] remove debug Signed-off-by: Nikolaj Bjorner --- src/sat/sat_solver/inc_sat_solver.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index f0fe44160..097d3f0fa 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -113,7 +113,6 @@ public: if (m_num_scopes > 0) { throw default_exception("Cannot translate sat solver at non-base level"); } - std::cout << "translate\n"; std::cout.flush(); ast_translation tr(m, dst_m); m_solver.pop_to_base_level(); inc_sat_solver* result = alloc(inc_sat_solver, dst_m, p, is_incremental()); @@ -168,7 +167,6 @@ public: lbool check_sat(unsigned sz, expr * const * assumptions) override { m_solver.pop_to_base_level(); m_core.reset(); - std::cout << "#inconsistent: " << m_solver.inconsistent() << "\n"; if (m_solver.inconsistent()) return l_false; expr_ref_vector _assumptions(m); obj_map asm2fml; @@ -779,8 +777,6 @@ private: } m_core.push_back(e); } - std::cout << "core " << core << "\n"; - std::cout.flush(); } void check_assumptions(dep2asm_t& dep2asm) { From af41255a9d01e50e328900dbbaeb48959e5d779c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Sep 2018 10:00:13 -0700 Subject: [PATCH 084/138] fix regression in model generation for UFLRA Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index ca59a2c27..5b1de851e 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -218,6 +218,8 @@ class theory_lra::imp { mutable std::unordered_map m_variable_values; // current model lp::var_index m_one_var; lp::var_index m_zero_var; + lp::var_index m_rone_var; + lp::var_index m_rzero_var; enum constraint_source { inequality_source, @@ -333,11 +335,11 @@ class theory_lra::imp { } } - void add_const(int c, lp::var_index& var) { + lp::var_index add_const(int c, lp::var_index& var, bool is_int) { if (var != UINT_MAX) { - return; + return var; } - app_ref cnst(a.mk_int(c), m); + app_ref cnst(a.mk_numeral(rational(c), is_int), m); TRACE("arith", tout << "add " << cnst << "\n";); enode* e = mk_enode(cnst); theory_var v = mk_var(cnst); @@ -347,16 +349,15 @@ class theory_lra::imp { m_var_trail.push_back(v); add_def_constraint(m_solver->add_var_bound(var, lp::GE, rational(c))); add_def_constraint(m_solver->add_var_bound(var, lp::LE, rational(c))); + return var; } - lp::var_index get_one() { - add_const(1, m_one_var); - return m_one_var; + lp::var_index get_one(bool is_int) { + return add_const(1, is_int ? m_one_var : m_rone_var, is_int); } - lp::var_index get_zero() { - add_const(0, m_zero_var); - return m_zero_var; + lp::var_index get_zero(bool is_int) { + return add_const(0, is_int ? m_zero_var : m_rzero_var, is_int); } @@ -577,6 +578,7 @@ class theory_lra::imp { } enode * mk_enode(app * n) { + TRACE("arith", tout << expr_ref(n, m) << "\n";); if (ctx().e_internalized(n)) { return get_enode(n); } @@ -777,6 +779,7 @@ class theory_lra::imp { } theory_var internalize_def(app* term, scoped_internalize_state& st) { + TRACE("arith", tout << expr_ref(term, m) << "\n";); if (ctx().e_internalized(term)) { IF_VERBOSE(0, verbose_stream() << "repeated term\n";); return mk_var(term, false); @@ -807,10 +810,10 @@ class theory_lra::imp { return st.vars()[0]; } else if (is_one(st)) { - return get_one(); + return get_one(a.is_int(term)); } else if (is_zero(st)) { - return get_zero(); + return get_zero(a.is_int(term)); } else { init_left_side(st); @@ -820,7 +823,7 @@ class theory_lra::imp { if (vi == UINT_MAX) { rational const& offset = st.offset(); if (!offset.is_zero()) { - m_left_side.push_back(std::make_pair(offset, get_one())); + m_left_side.push_back(std::make_pair(offset, get_one(a.is_int(term)))); } SASSERT(!m_left_side.empty()); vi = m_solver->add_term(m_left_side); @@ -854,6 +857,8 @@ public: m_internalize_head(0), m_one_var(UINT_MAX), m_zero_var(UINT_MAX), + m_rone_var(UINT_MAX), + m_rzero_var(UINT_MAX), m_not_handled(nullptr), m_asserted_qhead(0), m_assume_eq_head(0), @@ -925,16 +930,18 @@ public: } return true; } + + bool is_arith(enode* n) { + return n && n->get_th_var(get_id()) != null_theory_var; + } void internalize_eq_eh(app * atom, bool_var) { + TRACE("arith_verbose", tout << mk_pp(atom, m) << "\n";); expr* lhs = nullptr, *rhs = nullptr; VERIFY(m.is_eq(atom, lhs, rhs)); enode * n1 = get_enode(lhs); enode * n2 = get_enode(rhs); - if (n1->get_th_var(get_id()) != null_theory_var && - n2->get_th_var(get_id()) != null_theory_var && - n1 != n2) { - TRACE("arith_verbose", tout << mk_pp(atom, m) << "\n";); + if (is_arith(n1) && is_arith(n2) && n1 != n2) { m_arith_eq_adapter.mk_axioms(n1, n2); } } @@ -1301,6 +1308,7 @@ public: void init_variable_values() { reset_variable_values(); if (!m.canceled() && m_solver.get() && th.get_num_vars() > 0) { + TRACE("arith", tout << "update variable values\n";); m_solver->get_model(m_variable_values); } } @@ -3002,6 +3010,7 @@ public: if (!can_get_bound(v)) return false; lp::var_index vi = m_theory_var2var_index[v]; if (m_solver->has_value(vi, val)) { + TRACE("arith", tout << expr_ref(n->get_owner(), m) << " := " << val << "\n";); if (is_int(n) && !val.is_int()) return false; return true; } From 0b2b6b13061c1fccf4dac1c295d73813d169c522 Mon Sep 17 00:00:00 2001 From: Lev Nachmanson Date: Tue, 25 Sep 2018 13:33:30 -0700 Subject: [PATCH 085/138] assert all_constraints_hold() rarely Signed-off-by: Lev Nachmanson --- src/util/lp/lar_solver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp index 56a61177c..a9bdf19d1 100644 --- a/src/util/lp/lar_solver.cpp +++ b/src/util/lp/lar_solver.cpp @@ -782,7 +782,7 @@ void lar_solver::solve_with_core_solver() { update_x_and_inf_costs_for_columns_with_changed_bounds(); m_mpq_lar_core_solver.solve(); set_status(m_mpq_lar_core_solver.m_r_solver.get_status()); - lp_assert(m_status != lp_status::OPTIMAL || all_constraints_hold()); + lp_assert((m_settings.random_next() % 100) != 0 || m_status != lp_status::OPTIMAL || all_constraints_hold()); } From 26d40865faddb888dfd72afc55b85777c34e2c1c Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 25 Sep 2018 23:54:48 -0700 Subject: [PATCH 086/138] add verbose output to capture cases for empty cube Signed-off-by: Nikolaj Bjorner --- src/sat/sat_solver/inc_sat_solver.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 097d3f0fa..5de64b496 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -311,7 +311,10 @@ public: expr_ref_vector cube(expr_ref_vector& vs, unsigned backtrack_level) override { if (!is_internalized()) { lbool r = internalize_formulas(); - if (r != l_true) return expr_ref_vector(m); + if (r != l_true) { + IF_VERBOSE(0, verbose_stream() << "internalize produced " << r << "\n"); + return expr_ref_vector(m); + } } convert_internalized(); obj_hashtable _vs; @@ -329,6 +332,7 @@ public: return result; } if (result == l_true) { + IF_VERBOSE(1, verbose_stream() << "formulas are SAT\n"); return expr_ref_vector(m); } expr_ref_vector fmls(m); @@ -345,6 +349,7 @@ public: vs.push_back(x); } } + if (fmls.empty()) { IF_VERBOSE(0, verbose_stream() << "no literals were produced in cube\n"); } return fmls; } From e0490450f3ae0811ee9650ab9e8ce685184ecfe5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 28 Sep 2018 13:23:28 -0700 Subject: [PATCH 087/138] add capabilities to python API, fix model extraction for qsat Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 28 ++++++++++++++++++++++------ src/qe/qsat.cpp | 4 ++-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index a6b05904c..253541a91 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -1258,6 +1258,11 @@ def Consts(names, sort): names = names.split(" ") return [Const(name, sort) for name in names] +def FreshConst(sort, prefix='c'): + """Create a fresh constant of a specified sort""" + ctx = _get_ctx(sort.ctx) + return _to_expr_ref(Z3_mk_fresh_const(ctx.ref(), prefix, sort.ast), ctx) + def Var(idx, s): """Create a Z3 free variable. Free variables are used to create quantified formulas. @@ -4280,7 +4285,7 @@ def get_map_func(a): _z3_assert(is_map(a), "Z3 array map expression expected.") return FuncDeclRef(Z3_to_func_decl(a.ctx_ref(), Z3_get_decl_ast_parameter(a.ctx_ref(), a.decl().ast, 0)), a.ctx) -def ArraySort(d, r): +def ArraySort(*sig): """Return the Z3 array sort with the given domain and range sorts. >>> A = ArraySort(IntSort(), BoolSort()) @@ -4294,12 +4299,23 @@ def ArraySort(d, r): >>> AA Array(Int, Array(Int, Bool)) """ + sig = _get_args(sig) if __debug__: - _z3_assert(is_sort(d), "Z3 sort expected") - _z3_assert(is_sort(r), "Z3 sort expected") - _z3_assert(d.ctx == r.ctx, "Context mismatch") + z3_assert(len(sig) > 1, "At least two arguments expected") + arity = len(sig) - 1 + r = sig[arity] + d = sig[0] + if __debug__: + for s in sig: + _z3_assert(is_sort(s), "Z3 sort expected") + _z3_assert(s.ctx == r.ctx, "Context mismatch") ctx = d.ctx - return ArraySortRef(Z3_mk_array_sort(ctx.ref(), d.ast, r.ast), ctx) + if len(sig) == 2: + return ArraySortRef(Z3_mk_array_sort(ctx.ref(), d.ast, r.ast), ctx) + dom = (Sort * arity)() + for i in range(arity): + dom[i] = sig[i].ast + return ArraySortRef(Z3_mk_array_sort_n(ctx.ref(), arity, dom, r.ast), ctx) def Array(name, dom, rng): """Return an array constant named `name` with the given domain and range sorts. @@ -8048,7 +8064,7 @@ def substitute(t, *m): """ if isinstance(m, tuple): m1 = _get_args(m) - if isinstance(m1, list): + if isinstance(m1, list) and all(isinstance(p, tuple) for p in m1): m = m1 if __debug__: _z3_assert(is_expr(t), "Z3 expression expected") diff --git a/src/qe/qsat.cpp b/src/qe/qsat.cpp index c87b1c2eb..2ad5b9b96 100644 --- a/src/qe/qsat.cpp +++ b/src/qe/qsat.cpp @@ -1266,9 +1266,9 @@ namespace qe { in->reset(); in->inc_depth(); result.push_back(in.get()); - if (in->models_enabled()) { + if (in->models_enabled()) { model_converter_ref mc; - mc = model2model_converter(m_model.get()); + mc = model2model_converter(m_model_save.get()); mc = concat(m_pred_abs.fmc(), mc.get()); in->add(mc.get()); } From 6dcec4ce79bf4b63606b67cb45c934ae2abd8ea9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Fri, 28 Sep 2018 16:38:43 -0700 Subject: [PATCH 088/138] z3_assert -> _z3_assert Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 253541a91..8b37fb802 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -4301,7 +4301,7 @@ def ArraySort(*sig): """ sig = _get_args(sig) if __debug__: - z3_assert(len(sig) > 1, "At least two arguments expected") + _z3_assert(len(sig) > 1, "At least two arguments expected") arity = len(sig) - 1 r = sig[arity] d = sig[0] From 5d586c8fd1817ce56f96c9cd594fce8bc1c22160 Mon Sep 17 00:00:00 2001 From: Lev Date: Fri, 28 Sep 2018 14:14:25 -0700 Subject: [PATCH 089/138] set lar_solver.m_status = UNKNOWN in the constructor Signed-off-by: Lev --- src/util/lp/int_solver.cpp | 4 ---- src/util/lp/lar_solver.cpp | 3 ++- src/util/lp/lar_solver.h | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/util/lp/int_solver.cpp b/src/util/lp/int_solver.cpp index 83fbe3961..0967c6cc6 100644 --- a/src/util/lp/int_solver.cpp +++ b/src/util/lp/int_solver.cpp @@ -187,10 +187,6 @@ struct check_return_helper { ~check_return_helper() { TRACE("pivoted_rows", tout << "pivoted rows = " << m_lar_solver->m_mpq_lar_core_solver.m_r_solver.m_pivoted_rows->size() << std::endl;); m_lar_solver->set_track_pivoted_rows(m_track_pivoted_rows); - if (m_r == lia_move::cut || m_r == lia_move::branch) { - int_solver * s = m_lar_solver->get_int_solver(); - // m_lar_solver->adjust_cut_for_terms(*(s->m_t), *(s->m_k)); - } } }; diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp index 436e6ab04..5b3028a98 100644 --- a/src/util/lp/lar_solver.cpp +++ b/src/util/lp/lar_solver.cpp @@ -27,7 +27,7 @@ void clear() {lp_assert(false); // not implemented } -lar_solver::lar_solver() : m_status(lp_status::OPTIMAL), +lar_solver::lar_solver() : m_status(lp_status::UNKNOWN), m_infeasible_column_index(-1), m_terms_start_index(1000000), m_mpq_lar_core_solver(m_settings, *this), @@ -1174,6 +1174,7 @@ void lar_solver::get_model(std::unordered_map & variable_values) std::unordered_set set_of_different_pairs; std::unordered_set set_of_different_singles; delta = m_mpq_lar_core_solver.find_delta_for_strict_bounds(delta); + TRACE("get_model", tout << "delta=" << delta << "size = " << m_mpq_lar_core_solver.m_r_x.size() << std::endl;); for (i = 0; i < m_mpq_lar_core_solver.m_r_x.size(); i++ ) { const numeric_pair & rp = m_mpq_lar_core_solver.m_r_x[i]; set_of_different_pairs.insert(rp); diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index cfe581ba3..4189bad4e 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -542,7 +542,6 @@ public: for (const auto & p : columns_to_subs) { auto it = t.m_coeffs.find(p.first); lp_assert(it != t.m_coeffs.end()); - const lar_term& lt = get_term(p.second); mpq v = it->second; t.m_coeffs.erase(it); t.m_coeffs[p.second] = v; From a5762a78e94dea0d02ba101692b68b847fa72195 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 30 Sep 2018 17:39:18 -0700 Subject: [PATCH 090/138] change to ast-vector Signed-off-by: Nikolaj Bjorner --- src/api/ml/z3.ml | 5 +++-- src/api/ml/z3.mli | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index 25b437bc4..7415fc507 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1815,8 +1815,9 @@ struct | _ -> UNKNOWN let get_model x = - let q = Z3native.solver_get_model (gc x) x in - if Z3native.is_null_model q then None else Some q + let q = Z3native.solver_get_model (gc x) x in + try if Z3native.is_null_model q then None else Some q with | _ -> None + let get_proof x = let q = Z3native.solver_get_proof (gc x) x in diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli index f67966a0f..18ade29bf 100644 --- a/src/api/ml/z3.mli +++ b/src/api/ml/z3.mli @@ -3407,10 +3407,10 @@ sig (** Parse the given string using the SMT-LIB2 parser. @return A conjunction of assertions in the scope (up to push/pop) at the end of the string. *) - val parse_smtlib2_string : context -> string -> Symbol.symbol list -> Sort.sort list -> Symbol.symbol list -> FuncDecl.func_decl list -> Expr.expr + val parse_smtlib2_string : context -> string -> Symbol.symbol list -> Sort.sort list -> Symbol.symbol list -> FuncDecl.func_decl list -> AST.ASTVector.ast_vector (** Parse the given file using the SMT-LIB2 parser. *) - val parse_smtlib2_file : context -> string -> Symbol.symbol list -> Sort.sort list -> Symbol.symbol list -> FuncDecl.func_decl list -> Expr.expr + val parse_smtlib2_file : context -> string -> Symbol.symbol list -> Sort.sort list -> Symbol.symbol list -> FuncDecl.func_decl list -> AST.ASTVector.ast_vector end From 90fca8b378b493b4213711fa91edfe29a6a3031d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sun, 30 Sep 2018 17:44:28 -0700 Subject: [PATCH 091/138] add psat to available tactics Signed-off-by: Nikolaj Bjorner --- src/sat/sat_solver/CMakeLists.txt | 2 ++ src/sat/sat_solver/inc_sat_solver.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/sat/sat_solver/CMakeLists.txt b/src/sat/sat_solver/CMakeLists.txt index 14eb4ac25..45a673367 100644 --- a/src/sat/sat_solver/CMakeLists.txt +++ b/src/sat/sat_solver/CMakeLists.txt @@ -8,4 +8,6 @@ z3_add_component(sat_solver core_tactics sat_tactic solver + TACTIC_HEADERS + inc_sat_solver.h ) diff --git a/src/sat/sat_solver/inc_sat_solver.h b/src/sat/sat_solver/inc_sat_solver.h index 71ec48b99..b1cf7ad37 100644 --- a/src/sat/sat_solver/inc_sat_solver.h +++ b/src/sat/sat_solver/inc_sat_solver.h @@ -28,6 +28,9 @@ solver* mk_inc_sat_solver(ast_manager& m, params_ref const& p, bool incremental_ tactic* mk_psat_tactic(ast_manager& m, params_ref const& p); +/* + ADD_TACTIC('psat', '(try to) solve goal using a parallel SAT solver.', 'mk_psat_tactic(m, p)') +*/ void inc_sat_display(std::ostream& out, solver& s, unsigned sz, expr*const* soft, rational const* _weights); From f0e74b7f2a75ae105418b9f5bb462a29bb7e6343 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 1 Oct 2018 12:11:42 +0100 Subject: [PATCH 092/138] Fix for module name clash (and thus linking error) in the Visual Studio solution. --- src/util/lp/{bound_propagator.cpp => lp_bound_propagator.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/util/lp/{bound_propagator.cpp => lp_bound_propagator.cpp} (100%) diff --git a/src/util/lp/bound_propagator.cpp b/src/util/lp/lp_bound_propagator.cpp similarity index 100% rename from src/util/lp/bound_propagator.cpp rename to src/util/lp/lp_bound_propagator.cpp From 35bf63d563b682d794067c66f018a0fb06bf7ce2 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 1 Oct 2018 12:29:14 +0100 Subject: [PATCH 093/138] Fixed filename in CMakeLists.txt --- src/util/lp/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util/lp/CMakeLists.txt b/src/util/lp/CMakeLists.txt index 539c68712..edb73fdab 100644 --- a/src/util/lp/CMakeLists.txt +++ b/src/util/lp/CMakeLists.txt @@ -2,7 +2,7 @@ z3_add_component(lp SOURCES binary_heap_priority_queue.cpp binary_heap_upair_queue.cpp - bound_propagator.cpp + lp_bound_propagator.cpp core_solver_pretty_printer.cpp dense_matrix.cpp eta_matrix.cpp From 2a92de0aee7a77fee650f22f8b5acb3f16f57cf2 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 1 Oct 2018 15:20:00 +0100 Subject: [PATCH 094/138] Fixed side conditions for UFs translated from FP to BV. Fixes #1825. --- src/ast/fpa/fpa2bv_converter.cpp | 39 ++++++++++++++++++++++++++++++-- src/ast/fpa/fpa2bv_converter.h | 2 ++ src/ast/fpa/fpa2bv_rewriter.cpp | 11 +++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index dd3f35a2a..055f751c1 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -21,6 +21,8 @@ Notes: #include "ast/ast_smt2_pp.h" #include "ast/well_sorted.h" #include "ast/rewriter/th_rewriter.h" +#include "ast/used_vars.h" +#include "ast/rewriter/var_subst.h" #include "ast/fpa/fpa2bv_converter.h" #include "ast/rewriter/fpa_rewriter.h" @@ -230,6 +232,39 @@ void fpa2bv_converter::mk_var(unsigned base_inx, sort * srt, expr_ref & result) result = m_util.mk_fp(sgn, e, s); } +expr_ref fpa2bv_converter::extra_quantify(expr * e) +{ + used_vars uv; + unsigned nv; + + ptr_buffer new_decl_sorts; + sbuffer new_decl_names; + expr_ref_buffer subst_map(m); + + uv(e); + nv = uv.get_num_vars(); + subst_map.resize(uv.get_max_found_var_idx_plus_1()); + + for (unsigned i = 0; i < nv; i++) + { + if (uv.contains(i)) { + TRACE("fpa2bv", tout << "uv[" << i << "] = " << mk_ismt2_pp(uv.get(i), m) << std::endl; ); + sort * s = uv.get(i); + var * v = m.mk_var(i, s); + new_decl_sorts.push_back(s); + new_decl_names.push_back(symbol(i)); + subst_map.set(i, v); + } + } + + expr_ref res(m); + var_subst vsubst(m); + res = vsubst.operator()(e, nv, subst_map.c_ptr()); + TRACE("fpa2bv", tout << "subst'd = " << mk_ismt2_pp(res, m) << std::endl; ); + res = m.mk_forall(nv, new_decl_sorts.c_ptr(), new_decl_names.c_ptr(), res); + return res; +} + void fpa2bv_converter::mk_uf(func_decl * f, unsigned num, expr * const * args, expr_ref & result) { TRACE("fpa2bv", tout << "UF: " << mk_ismt2_pp(f, m) << std::endl; ); @@ -252,7 +287,7 @@ void fpa2bv_converter::mk_uf(func_decl * f, unsigned num, expr * const * args, e m_bv_util.mk_extract(sbits+ebits-2, sbits-1, bv_app), m_bv_util.mk_extract(sbits-2, 0, bv_app)); new_eq = m.mk_eq(fapp, flt_app); - m_extra_assertions.push_back(new_eq); + m_extra_assertions.push_back(extra_quantify(new_eq)); result = flt_app; } else if (m_util.is_rm(rng)) { @@ -263,7 +298,7 @@ void fpa2bv_converter::mk_uf(func_decl * f, unsigned num, expr * const * args, e bv_app = m.mk_app(bv_f, num, args); flt_app = m_util.mk_bv2rm(bv_app); new_eq = m.mk_eq(fapp, flt_app); - m_extra_assertions.push_back(new_eq); + m_extra_assertions.push_back(extra_quantify(new_eq)); result = flt_app; } else diff --git a/src/ast/fpa/fpa2bv_converter.h b/src/ast/fpa/fpa2bv_converter.h index 7637317b0..812c24155 100644 --- a/src/ast/fpa/fpa2bv_converter.h +++ b/src/ast/fpa/fpa2bv_converter.h @@ -220,6 +220,8 @@ private: func_decl * mk_bv_uf(func_decl * f, sort * const * domain, sort * range); expr_ref nan_wrap(expr * n); + + expr_ref extra_quantify(expr * e); }; #endif diff --git a/src/ast/fpa/fpa2bv_rewriter.cpp b/src/ast/fpa/fpa2bv_rewriter.cpp index cc25905f0..b2614e27d 100644 --- a/src/ast/fpa/fpa2bv_rewriter.cpp +++ b/src/ast/fpa/fpa2bv_rewriter.cpp @@ -215,6 +215,12 @@ bool fpa2bv_rewriter_cfg::reduce_quantifier(quantifier * old_q, new_decl_names.push_back(symbol(name_buffer.c_str())); new_decl_sorts.push_back(m_conv.bu().mk_sort(sbits+ebits)); } + else if (m_conv.is_rm(s)) { + name_buffer.reset(); + name_buffer << n << ".bv"; + new_decl_names.push_back(symbol(name_buffer.c_str())); + new_decl_sorts.push_back(m_conv.bu().mk_sort(3)); + } else { new_decl_sorts.push_back(s); new_decl_names.push_back(n); @@ -248,6 +254,11 @@ bool fpa2bv_rewriter_cfg::reduce_var(var * t, expr_ref & result, proof_ref & res m_conv.bu().mk_extract(ebits - 1, 0, new_var), m_conv.bu().mk_extract(sbits+ebits-2, ebits, new_var)); } + else if (m_conv.is_rm(s)) { + expr_ref new_var(m()); + new_var = m().mk_var(t->get_idx(), m_conv.bu().mk_sort(3)); + new_exp = m_conv.fu().mk_bv2rm(new_var); + } else new_exp = m().mk_var(t->get_idx(), s); From c92c431570e6fcf0359ed9e415415ae788d2dec8 Mon Sep 17 00:00:00 2001 From: nilsbecker Date: Mon, 1 Oct 2018 16:32:04 +0200 Subject: [PATCH 095/138] adding call to update_max_generation --- src/smt/mam.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/smt/mam.cpp b/src/smt/mam.cpp index 00e079989..70741fa67 100644 --- a/src/smt/mam.cpp +++ b/src/smt/mam.cpp @@ -2571,6 +2571,7 @@ namespace smt { m_n1 = m_context.get_enode_eq_to(static_cast(m_pc)->m_label, static_cast(m_pc)->m_num_args, m_args.c_ptr()); \ if (m_n1 == 0 || !m_context.is_relevant(m_n1)) \ goto backtrack; \ + update_max_generation(m_n1, nullptr); \ m_registers[static_cast(m_pc)->m_oreg] = m_n1; \ m_pc = m_pc->m_next; \ goto main_loop; From aaba1b9b15b787b020288dadc7c7298475a3007d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 1 Oct 2018 09:18:40 -0700 Subject: [PATCH 096/138] fix sort retrieval for lambdas Signed-off-by: Nikolaj Bjorner --- src/api/python/z3/z3.py | 4 +++- src/smt/theory_array_full.cpp | 15 ++++++++++++--- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index 8b37fb802..cefe8bbbb 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -1749,7 +1749,9 @@ class QuantifierRef(BoolRef): return Z3_get_ast_id(self.ctx_ref(), self.as_ast()) def sort(self): - """Return the Boolean sort.""" + """Return the Boolean sort or sort of Lambda.""" + if self.is_lambda(): + return _sort(self.ctx, self.as_ast()) return BoolSort(self.ctx) def is_forall(self): diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp index d872997c4..03cd3e1b2 100644 --- a/src/smt/theory_array_full.cpp +++ b/src/smt/theory_array_full.cpp @@ -339,6 +339,7 @@ namespace smt { SASSERT(v != null_theory_var); v = find(v); var_data* d = m_var_data[v]; + TRACE("array", tout << "v" << v << "\n";); for (enode * store : d->m_stores) { SASSERT(is_store(store)); instantiate_default_store_axiom(store); @@ -383,13 +384,21 @@ namespace smt { void theory_array_full::relevant_eh(app* n) { TRACE("array", tout << mk_pp(n, get_manager()) << "\n";); theory_array::relevant_eh(n); - if (!is_default(n) && !is_select(n) && !is_map(n) && !is_const(n) && !is_as_array(n)) { + if (!is_default(n) && !is_select(n) && !is_map(n) && !is_const(n) && !is_as_array(n) && !is_store(n)) { return; } context & ctx = get_context(); enode* node = ctx.get_enode(n); - - if (is_select(n)) { + if (is_store(n)) { + enode * arg = ctx.get_enode(n->get_arg(0)); + if (is_const(arg)) { + TRACE("array", tout << expr_ref(arg->get_owner(), get_manager()) << " " << is_const(arg) << "\n";); + theory_var v = arg->get_th_var(get_id()); + set_prop_upward(v); + add_parent_default(v); + } + } + else if (is_select(n)) { enode * arg = ctx.get_enode(n->get_arg(0)); theory_var v = arg->get_th_var(get_id()); SASSERT(v != null_theory_var); From 48ec7c1175af91f8e43ab51153f8cc289b9e96dd Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 1 Oct 2018 17:25:02 +0100 Subject: [PATCH 097/138] Follow-up fix for fpa2bv_converter. --- src/ast/fpa/fpa2bv_converter.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp index 055f751c1..1dc13ff9e 100644 --- a/src/ast/fpa/fpa2bv_converter.cpp +++ b/src/ast/fpa/fpa2bv_converter.cpp @@ -245,6 +245,9 @@ expr_ref fpa2bv_converter::extra_quantify(expr * e) nv = uv.get_num_vars(); subst_map.resize(uv.get_max_found_var_idx_plus_1()); + if (nv == 0) + return expr_ref(e, m); + for (unsigned i = 0; i < nv; i++) { if (uv.contains(i)) { From cdbfd9654f9fa9f73ab42827a185af776357f59f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Mon, 1 Oct 2018 21:14:25 +0200 Subject: [PATCH 098/138] Drop unused CV-qualifiers from scalar return values --- src/util/lp/lar_core_solver.h | 2 +- src/util/lp/numeric_pair.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/util/lp/lar_core_solver.h b/src/util/lp/lar_core_solver.h index 904550339..52290c69a 100644 --- a/src/util/lp/lar_core_solver.h +++ b/src/util/lp/lar_core_solver.h @@ -817,7 +817,7 @@ public: } - const bool column_is_bounded(unsigned j) const { + bool column_is_bounded(unsigned j) const { switch(m_column_types()[j]) { case column_type::fixed: case column_type::boxed: diff --git a/src/util/lp/numeric_pair.h b/src/util/lp/numeric_pair.h index e98d76cbb..ed740d645 100644 --- a/src/util/lp/numeric_pair.h +++ b/src/util/lp/numeric_pair.h @@ -57,10 +57,10 @@ public: template <> class numeric_traits { public: static bool precise() { return true; } - static int const zero() { return 0; } - static int const one() { return 1; } + static int zero() { return 0; } + static int one() { return 1; } static bool is_zero(int v) { return v == 0; } - static double const get_double(int const & d) { return d; } + static double get_double(int const & d) { return d; } static bool is_int(int) {return true;} static bool is_pos(int j) {return j > 0;} static bool is_neg(int j) {return j < 0;} From f145873603ca8b83a5a84e299528b7dfad7c845b Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 1 Oct 2018 20:22:20 +0100 Subject: [PATCH 099/138] CI Test From aaae3118de5d3faadffcdc9344089f377b6ecae6 Mon Sep 17 00:00:00 2001 From: "Christoph M. Wintersteiger" Date: Mon, 1 Oct 2018 20:26:05 +0100 Subject: [PATCH 100/138] CI Test From 661826e27f196967b1775eb4e419b5d8f9847535 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Mon, 1 Oct 2018 21:35:48 +0200 Subject: [PATCH 101/138] Add missing template instantion for lar_core_solver::m_r_solver --- src/util/lp/lp_core_solver_base.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/util/lp/lp_core_solver_base.cpp b/src/util/lp/lp_core_solver_base.cpp index 00c1322c2..e71f65d5d 100644 --- a/src/util/lp/lp_core_solver_base.cpp +++ b/src/util/lp/lp_core_solver_base.cpp @@ -84,6 +84,7 @@ template lp::lp_core_solver_base >::lp_core_s const vector >&); template bool lp::lp_core_solver_base >::print_statistics_with_cost_and_check_that_the_time_is_over(lp::numeric_pair, std::ostream&); template void lp::lp_core_solver_base >::snap_xN_to_bounds_and_fill_xB(); +template void lp::lp_core_solver_base >::solve_Ax_eq_b(); template void lp::lp_core_solver_base >::solve_Bd(unsigned int); template bool lp::lp_core_solver_base >::update_basis_and_x(int, int, lp::numeric_pair const&); template void lp::lp_core_solver_base >::update_x(unsigned int, const lp::numeric_pair&); From 5c9b1c7b11ba2aa048f868a278d15c54d2fff021 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Janiszewski?= Date: Mon, 1 Oct 2018 21:43:44 +0200 Subject: [PATCH 102/138] Add support for Intel Compiler --- CMakeLists.txt | 6 +++++- src/util/memory_manager.h | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a086afd71..71469e032 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -380,7 +380,11 @@ endif() ################################################################################ # FIXME: Support ARM "-mfpu=vfp -mfloat-abi=hard" if (("${TARGET_ARCHITECTURE}" STREQUAL "x86_64") OR ("${TARGET_ARCHITECTURE}" STREQUAL "i686")) - if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) + if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Intel")) + if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Intel") + # Intel's compiler requires linking with libiomp5 + list(APPEND Z3_DEPENDENT_LIBS "iomp5") + endif() set(SSE_FLAGS "-mfpmath=sse" "-msse" "-msse2") # FIXME: Remove "x.." when CMP0054 is set to NEW elseif ("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC") diff --git a/src/util/memory_manager.h b/src/util/memory_manager.h index 5cb7dc467..a5205f273 100644 --- a/src/util/memory_manager.h +++ b/src/util/memory_manager.h @@ -28,7 +28,7 @@ Revision History: #endif #ifdef __GNUC__ -# if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 || __has_builtin(returns_nonnull) +# if ((__GNUC__ * 100 + __GNUC_MINOR__) >= 409 || __has_builtin(returns_nonnull)) && !defined(__INTEL_COMPILER) # define GCC_RET_NON_NULL __attribute__((returns_nonnull)) # else # define GCC_RET_NON_NULL From 08c58ae61484a5b191da8cb39f4579b46d5fd411 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 1 Oct 2018 15:52:22 -0700 Subject: [PATCH 103/138] make the unsat/sat verdicts from cubing produce empty clause and models respectively Signed-off-by: Nikolaj Bjorner --- src/sat/sat_lookahead.cpp | 2 ++ src/sat/sat_solver.cpp | 28 ++++++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/src/sat/sat_lookahead.cpp b/src/sat/sat_lookahead.cpp index 4ca2c5f84..c252efb69 100644 --- a/src/sat/sat_lookahead.cpp +++ b/src/sat/sat_lookahead.cpp @@ -2137,6 +2137,8 @@ namespace sat { if (lit == null_literal) { vars.reset(); for (auto v : m_freevars) if (in_reduced_clause(v)) vars.push_back(v); + m_model.reset(); + init_model(); return l_true; } TRACE("sat", tout << "choose: " << lit << " cube: " << m_cube_state.m_cube << "\n";); diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 7ddc80813..59cb2aac4 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1014,14 +1014,38 @@ namespace sat { } lbool solver::cube(bool_var_vector& vars, literal_vector& lits, unsigned backtrack_level) { - if (!m_cuber) { + bool is_first = !m_cuber; + if (is_first) { m_cuber = alloc(lookahead, *this); } lbool result = m_cuber->cube(vars, lits, backtrack_level); m_cuber->update_cube_statistics(m_aux_stats); - if (result == l_false) { + switch (result) { + case l_false: dealloc(m_cuber); m_cuber = nullptr; + if (is_first) { + pop_to_base_level(); + set_conflict(justification()); + } + break; + case l_true: { + pop_to_base_level(); + model const& mdl = m_cuber->get_model(); + for (bool_var v = 0; v < mdl.size(); ++v) { + if (value(v) != l_undef) { + continue; + } + literal l(v, false); + if (mdl[v] != l_true) l.neg(); + push(); + assign_core(l, justification()); + } + mk_model(); + break; + } + default: + break; } return result; } From cdfc19a8856be9b803ac472bcddfda7dbe33f1d5 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Tue, 2 Oct 2018 09:11:19 +0700 Subject: [PATCH 104/138] Use nullptr. --- src/api/api_ast.cpp | 10 +++---- src/api/api_goal.cpp | 2 +- src/api/api_quant.cpp | 8 +++--- src/api/api_solver.cpp | 8 +++--- src/ast/ast.cpp | 2 +- src/ast/normal_forms/defined_names.cpp | 2 +- src/ast/normal_forms/name_exprs.cpp | 2 +- src/model/model.cpp | 4 +-- src/muz/spacer/spacer_context.cpp | 2 +- src/muz/spacer/spacer_proof_utils.cpp | 2 +- src/opt/opt_context.cpp | 2 +- src/parsers/smt2/smt2parser.cpp | 2 +- src/qe/qe_lite.cpp | 2 +- src/qe/qe_solve_plugin.cpp | 2 +- src/sat/ba_solver.cpp | 2 +- src/sat/sat_drat.cpp | 2 +- src/sat/sat_local_search.cpp | 2 +- src/sat/sat_local_search.h | 2 +- src/sat/sat_lookahead.cpp | 2 +- src/sat/sat_solver.cpp | 8 +++--- src/sat/tactic/goal2sat.cpp | 6 ++--- src/smt/smt_context.h | 2 +- src/smt/smt_model_checker.cpp | 2 +- src/smt/tactic/smt_tactic.cpp | 4 +-- src/smt/theory_lra.cpp | 14 +++++----- src/smt/theory_pb.cpp | 32 +++++++++++------------ src/smt/theory_pb.h | 2 +- src/smt/theory_seq.cpp | 4 +-- src/smt/theory_str.cpp | 26 +++++++++--------- src/smt/theory_str.h | 2 +- src/solver/parallel_tactic.cpp | 2 +- src/solver/solver_na2as.cpp | 2 +- src/tactic/arith/degree_shift_tactic.cpp | 2 +- src/tactic/arith/lia2card_tactic.cpp | 2 +- src/tactic/fd_solver/enum2bv_solver.cpp | 2 +- src/tactic/generic_model_converter.h | 2 +- src/tactic/portfolio/solver2lookahead.cpp | 2 +- src/tactic/proof_converter.cpp | 2 +- src/tactic/sine_filter.cpp | 2 +- src/tactic/tactical.cpp | 4 +-- src/test/main.cpp | 6 ++--- src/test/smt2print_parse.cpp | 2 +- src/util/lp/nra_solver.cpp | 2 +- src/util/obj_ref.h | 2 +- 44 files changed, 98 insertions(+), 98 deletions(-) diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp index 52be66e77..a28315cda 100644 --- a/src/api/api_ast.cpp +++ b/src/api/api_ast.cpp @@ -468,7 +468,7 @@ extern "C" { Z3_TRY; LOG_Z3_get_decl_symbol_parameter(c, d, idx); RESET_ERROR_CODE(); - CHECK_VALID_AST(d, 0); + CHECK_VALID_AST(d, nullptr); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB, nullptr); return nullptr; @@ -486,7 +486,7 @@ extern "C" { Z3_TRY; LOG_Z3_get_decl_sort_parameter(c, d, idx); RESET_ERROR_CODE(); - CHECK_VALID_AST(d, 0); + CHECK_VALID_AST(d, nullptr); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); @@ -504,7 +504,7 @@ extern "C" { Z3_TRY; LOG_Z3_get_decl_ast_parameter(c, d, idx); RESET_ERROR_CODE(); - CHECK_VALID_AST(d, 0); + CHECK_VALID_AST(d, nullptr); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); @@ -522,7 +522,7 @@ extern "C" { Z3_TRY; LOG_Z3_get_decl_func_decl_parameter(c, d, idx); RESET_ERROR_CODE(); - CHECK_VALID_AST(d, 0); + CHECK_VALID_AST(d, nullptr); if (idx >= to_func_decl(d)->get_num_parameters()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); @@ -596,7 +596,7 @@ extern "C" { Z3_TRY; LOG_Z3_get_domain(c, d, i); RESET_ERROR_CODE(); - CHECK_VALID_AST(d, 0); + CHECK_VALID_AST(d, nullptr); if (i >= to_func_decl(d)->get_arity()) { SET_ERROR_CODE(Z3_IOB, nullptr); RETURN_Z3(nullptr); diff --git a/src/api/api_goal.cpp b/src/api/api_goal.cpp index cb3bb7478..3d75c3dba 100644 --- a/src/api/api_goal.cpp +++ b/src/api/api_goal.cpp @@ -163,7 +163,7 @@ extern "C" { if (to_goal_ref(g)->mc()) (*to_goal_ref(g)->mc())(m_ref->m_model); RETURN_Z3(of_model(m_ref)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_goal Z3_API Z3_goal_translate(Z3_context c, Z3_goal g, Z3_context target) { diff --git a/src/api/api_quant.cpp b/src/api/api_quant.cpp index 6d6d19d56..49aa09727 100644 --- a/src/api/api_quant.cpp +++ b/src/api/api_quant.cpp @@ -155,7 +155,7 @@ extern "C" { expr_ref result(mk_c(c)->m()); if (num_decls == 0) { SET_ERROR_CODE(Z3_INVALID_USAGE, nullptr); - RETURN_Z3(0); + RETURN_Z3(nullptr); } sort* const* ts = reinterpret_cast(types); @@ -166,7 +166,7 @@ extern "C" { result = mk_c(c)->m().mk_lambda(names.size(), ts, names.c_ptr(), to_expr(body)); mk_c(c)->save_ast_trail(result.get()); return of_ast(result.get()); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast Z3_API Z3_mk_lambda_const(Z3_context c, @@ -178,7 +178,7 @@ extern "C" { RESET_ERROR_CODE(); if (num_decls == 0) { SET_ERROR_CODE(Z3_INVALID_USAGE, nullptr); - RETURN_Z3(0); + RETURN_Z3(nullptr); } svector _names; @@ -196,7 +196,7 @@ extern "C" { result = mk_c(c)->m().mk_lambda(_vars.size(), _vars.c_ptr(), _names.c_ptr(), result); mk_c(c)->save_ast_trail(result.get()); return of_ast(result.get()); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index 5a4537a4a..fc4e01f3a 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -369,7 +369,7 @@ extern "C" { v->m_ast_vector.push_back(f); } RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } Z3_ast_vector Z3_API Z3_solver_get_non_units(Z3_context c, Z3_solver s) { @@ -384,7 +384,7 @@ extern "C" { v->m_ast_vector.push_back(f); } RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } static Z3_lbool _solver_check(Z3_context c, Z3_solver s, unsigned num_assumptions, Z3_ast const assumptions[]) { @@ -631,7 +631,7 @@ extern "C" { } catch (z3_exception & ex) { mk_c(c)->handle_exception(ex); - return 0; + return nullptr; } } Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); @@ -644,7 +644,7 @@ extern "C" { to_ast_vector_ref(vs).push_back(a); } RETURN_Z3(of_ast_vector(v)); - Z3_CATCH_RETURN(0); + Z3_CATCH_RETURN(nullptr); } diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 65d3c7821..8750425a8 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -424,7 +424,7 @@ sort * get_sort(expr const * n) { return to_quantifier(n)->get_sort(); default: UNREACHABLE(); - return 0; + return nullptr; } } diff --git a/src/ast/normal_forms/defined_names.cpp b/src/ast/normal_forms/defined_names.cpp index b63c947a9..88ded842f 100644 --- a/src/ast/normal_forms/defined_names.cpp +++ b/src/ast/normal_forms/defined_names.cpp @@ -221,7 +221,7 @@ void defined_names::impl::mk_definition(expr * e, app * n, sort_ref_buffer & var args.push_back(m.mk_var(q->get_num_decls() - i - 1, q->get_decl_sort(i))); } array_util autil(m); - func_decl * f = 0; + func_decl * f = nullptr; if (autil.is_as_array(n2, f)) { n3 = m.mk_app(f, args.size()-1, args.c_ptr() + 1); } diff --git a/src/ast/normal_forms/name_exprs.cpp b/src/ast/normal_forms/name_exprs.cpp index 5e9af1c2d..bb2543b3e 100644 --- a/src/ast/normal_forms/name_exprs.cpp +++ b/src/ast/normal_forms/name_exprs.cpp @@ -127,7 +127,7 @@ class name_nested_formulas : public name_exprs_core { ast_manager & m; expr * m_root; - pred(ast_manager & m):m(m), m_root(0) {} + pred(ast_manager & m):m(m), m_root(nullptr) {} bool operator()(expr * t) override { TRACE("name_exprs", tout << "name_nested_formulas::pred:\n" << mk_ismt2_pp(t, m) << "\n";); diff --git a/src/model/model.cpp b/src/model/model.cpp index 83adfc87e..a1fdfc980 100644 --- a/src/model/model.cpp +++ b/src/model/model.cpp @@ -351,7 +351,7 @@ bool model::can_inline_def(top_sort& ts, func_decl* f) { expr_ref model::cleanup_expr(top_sort& ts, expr* e, unsigned current_partition) { - if (!e) return expr_ref(0, m); + if (!e) return expr_ref(nullptr, m); TRACE("model", tout << "cleaning up:\n" << mk_pp(e, m) << "\n";); @@ -453,7 +453,7 @@ void model::remove_decls(ptr_vector & decls, func_decl_set const & s) expr_ref model::get_inlined_const_interp(func_decl* f) { expr* v = get_const_interp(f); - if (!v) return expr_ref(0, m); + if (!v) return expr_ref(nullptr, m); top_sort st(m); expr_ref result1(v, m); expr_ref result2 = cleanup_expr(st, v, UINT_MAX); diff --git a/src/muz/spacer/spacer_context.cpp b/src/muz/spacer/spacer_context.cpp index dd6656954..30b68ebf6 100644 --- a/src/muz/spacer/spacer_context.cpp +++ b/src/muz/spacer/spacer_context.cpp @@ -1325,7 +1325,7 @@ bool pred_transformer::is_qblocked (pob &n) { // assert cti s->assert_expr(n.post()); - lbool res = s->check_sat(0, 0); + lbool res = s->check_sat(0, nullptr); // if (res == l_false) { // expr_ref_vector core(m); diff --git a/src/muz/spacer/spacer_proof_utils.cpp b/src/muz/spacer/spacer_proof_utils.cpp index ed02513f1..a73d24d49 100644 --- a/src/muz/spacer/spacer_proof_utils.cpp +++ b/src/muz/spacer/spacer_proof_utils.cpp @@ -600,7 +600,7 @@ namespace spacer { proof* hypothesis_reducer::reduce_core(proof* pf) { SASSERT(m.is_false(m.get_fact(pf))); - proof *res = NULL; + proof *res = nullptr; ptr_vector todo; todo.push_back(pf); diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index b1296f71e..87da85af5 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -281,7 +281,7 @@ namespace opt { symbol pri = optp.priority(); IF_VERBOSE(1, verbose_stream() << "(optimize:check-sat)\n"); - lbool is_sat = s.check_sat(0,0); + lbool is_sat = s.check_sat(0,nullptr); TRACE("opt", s.display(tout << "initial search result: " << is_sat << "\n");); if (is_sat != l_false) { s.get_model(m_model); diff --git a/src/parsers/smt2/smt2parser.cpp b/src/parsers/smt2/smt2parser.cpp index 403ea4c85..1a16b817b 100644 --- a/src/parsers/smt2/smt2parser.cpp +++ b/src/parsers/smt2/smt2parser.cpp @@ -2608,7 +2608,7 @@ namespace smt2 { check_rparen("invalid get-value command, ')' expected"); model_ref md; - if (!m_ctx.is_model_available(md) || m_ctx.get_check_sat_result() == 0) + if (!m_ctx.is_model_available(md) || m_ctx.get_check_sat_result() == nullptr) throw cmd_exception("model is not available"); if (index != 0) { m_ctx.get_opt()->get_box_model(md, index); diff --git a/src/qe/qe_lite.cpp b/src/qe/qe_lite.cpp index da45d17ca..d900bff5d 100644 --- a/src/qe/qe_lite.cpp +++ b/src/qe/qe_lite.cpp @@ -394,7 +394,7 @@ namespace eq { expr* const* args = &e; if (is_lambda(q)) { r = q; - pr = 0; + pr = nullptr; return; } flatten_args(q, num_args, args); diff --git a/src/qe/qe_solve_plugin.cpp b/src/qe/qe_solve_plugin.cpp index 6ec840de1..dae1faf6f 100644 --- a/src/qe/qe_solve_plugin.cpp +++ b/src/qe/qe_solve_plugin.cpp @@ -156,7 +156,7 @@ namespace qe { std::swap(e1, e2); } // y + -1*x == 0 --> y = x - expr *a0 = 0, *a1 = 0, *x = 0; + expr *a0 = nullptr, *a1 = nullptr, *x = nullptr; if (a.is_zero(e2) && a.is_add(e1, a0, a1)) { if (a.is_times_minus_one(a1, x)) { e1 = a0; diff --git a/src/sat/ba_solver.cpp b/src/sat/ba_solver.cpp index de6635d09..4f06c2e98 100644 --- a/src/sat/ba_solver.cpp +++ b/src/sat/ba_solver.cpp @@ -1527,7 +1527,7 @@ namespace sat { return p; } - ba_solver::ba_solver(): m_solver(0), m_lookahead(0), m_unit_walk(0), m_constraint_id(0), m_ba(*this), m_sort(m_ba) { + ba_solver::ba_solver(): m_solver(nullptr), m_lookahead(nullptr), m_unit_walk(nullptr), m_constraint_id(0), m_ba(*this), m_sort(m_ba) { TRACE("ba", tout << this << "\n";); m_num_propagations_since_pop = 0; } diff --git a/src/sat/sat_drat.cpp b/src/sat/sat_drat.cpp index 00a3fa076..932e9b35e 100644 --- a/src/sat/sat_drat.cpp +++ b/src/sat/sat_drat.cpp @@ -26,7 +26,7 @@ Notes: namespace sat { drat::drat(solver& s): s(s), - m_out(0), + m_out(nullptr), m_inconsistent(false), m_check_unsat(false), m_check_sat(false), diff --git a/src/sat/sat_local_search.cpp b/src/sat/sat_local_search.cpp index af2447fc2..9b79e2964 100644 --- a/src/sat/sat_local_search.cpp +++ b/src/sat/sat_local_search.cpp @@ -529,7 +529,7 @@ namespace sat { } lbool local_search::check() { - return check(0, 0); + return check(0, nullptr); } #define PROGRESS(tries, flips) \ diff --git a/src/sat/sat_local_search.h b/src/sat/sat_local_search.h index 8a63898c3..5fd69a740 100644 --- a/src/sat/sat_local_search.h +++ b/src/sat/sat_local_search.h @@ -277,7 +277,7 @@ namespace sat { lbool check(); - lbool check(unsigned sz, literal const* assumptions, parallel* p = 0); + lbool check(unsigned sz, literal const* assumptions, parallel* p = nullptr); local_search_config& config() { return m_config; } diff --git a/src/sat/sat_lookahead.cpp b/src/sat/sat_lookahead.cpp index c252efb69..bbc1106bb 100644 --- a/src/sat/sat_lookahead.cpp +++ b/src/sat/sat_lookahead.cpp @@ -33,7 +33,7 @@ namespace sat { } lookahead::scoped_ext::~scoped_ext() { - if (p.m_s.m_ext) p.m_s.m_ext->set_lookahead(0); + if (p.m_s.m_ext) p.m_s.m_ext->set_lookahead(nullptr); } lookahead::scoped_assumptions::scoped_assumptions(lookahead& p, literal_vector const& lits): p(p), lits(lits) { diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 59cb2aac4..b58d2296b 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -66,13 +66,13 @@ namespace sat { m_next_simplify = 0; m_num_checkpoints = 0; m_simplifications = 0; - m_ext = 0; + m_ext = nullptr; m_cuber = nullptr; m_mc.set_solver(this); } solver::~solver() { - m_ext = 0; + m_ext = nullptr; SASSERT(check_invariant()); TRACE("sat", tout << "Delete clauses\n";); del_clauses(m_clauses); @@ -1157,7 +1157,7 @@ namespace sat { srch.config().set_config(m_config); srch.import(*this, false); scoped_rl.push_child(&srch.rlimit()); - lbool r = srch.check(num_lits, lits, 0); + lbool r = srch.check(num_lits, lits, nullptr); m_model = srch.get_model(); // srch.collect_statistics(m_aux_stats); return r; @@ -1294,7 +1294,7 @@ namespace sat { if (!canceled) { rlimit().reset_cancel(); } - set_par(0, 0); + set_par(nullptr, 0); ls.reset(); uw.reset(); if (finished_id == -1) { diff --git a/src/sat/tactic/goal2sat.cpp b/src/sat/tactic/goal2sat.cpp index dc4dfc3a8..d42a28ac0 100644 --- a/src/sat/tactic/goal2sat.cpp +++ b/src/sat/tactic/goal2sat.cpp @@ -72,7 +72,7 @@ struct goal2sat::imp { imp(ast_manager & _m, params_ref const & p, sat::solver & s, atom2bool_var & map, dep2asm_map& dep2asm, bool default_external): m(_m), pb(m), - m_ext(0), + m_ext(nullptr), m_solver(s), m_map(map), m_dep2asm(dep2asm), @@ -1063,7 +1063,7 @@ void sat2goal::mc::insert(sat::bool_var v, app * atom, bool aux) { expr_ref sat2goal::mc::lit2expr(sat::literal l) { if (!m_var2expr.get(l.var())) { - app* aux = m.mk_fresh_const(0, m.mk_bool_sort()); + app* aux = m.mk_fresh_const(nullptr, m.mk_bool_sort()); m_var2expr.set(l.var(), aux); if (!m_gmc) m_gmc = alloc(generic_model_converter, m, "sat2goal"); m_gmc->hide(aux->get_decl()); @@ -1107,7 +1107,7 @@ struct sat2goal::imp { SASSERT(m_lit2expr.get((~l).index()) == 0); app* aux = mc ? mc->var2expr(l.var()) : nullptr; if (!aux) { - aux = m.mk_fresh_const(0, m.mk_bool_sort()); + aux = m.mk_fresh_const(nullptr, m.mk_bool_sort()); if (mc) { mc->insert(l.var(), aux, true); } diff --git a/src/smt/smt_context.h b/src/smt/smt_context.h index ff92f6f95..5733829a3 100644 --- a/src/smt/smt_context.h +++ b/src/smt/smt_context.h @@ -565,7 +565,7 @@ namespace smt { return m_asserted_formulas.has_quantifiers(); } - fingerprint * add_fingerprint(void * data, unsigned data_hash, unsigned num_args, enode * const * args, expr* def = 0) { + fingerprint * add_fingerprint(void * data, unsigned data_hash, unsigned num_args, enode * const * args, expr* def = nullptr) { return m_fingerprints.insert(data, data_hash, num_args, args, def); } diff --git a/src/smt/smt_model_checker.cpp b/src/smt/smt_model_checker.cpp index c3af41dcf..02b0e16be 100644 --- a/src/smt/smt_model_checker.cpp +++ b/src/smt/smt_model_checker.cpp @@ -577,7 +577,7 @@ namespace smt { } if (inst.m_def) { - m_context->internalize_assertion(inst.m_def, 0, gen); + m_context->internalize_assertion(inst.m_def, nullptr, gen); } TRACE("model_checker_bug_detail", tout << "instantiating... q:\n" << mk_pp(q, m) << "\n"; diff --git a/src/smt/tactic/smt_tactic.cpp b/src/smt/tactic/smt_tactic.cpp index 50fe47985..6aa365383 100644 --- a/src/smt/tactic/smt_tactic.cpp +++ b/src/smt/tactic/smt_tactic.cpp @@ -217,7 +217,7 @@ public: model_ref md; m_ctx->get_model(md); buffer r; - m_ctx->get_relevant_labels(0, r); + m_ctx->get_relevant_labels(nullptr, r); labels_vec rv; rv.append(r.size(), r.c_ptr()); model_converter_ref mc; @@ -270,7 +270,7 @@ public: model_ref md; m_ctx->get_model(md); buffer r; - m_ctx->get_relevant_labels(0, r); + m_ctx->get_relevant_labels(nullptr, r); labels_vec rv; rv.append(r.size(), r.c_ptr()); in->add(model_and_labels2model_converter(md.get(), rv)); diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 5b1de851e..db031d043 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1054,7 +1054,7 @@ public: // to_int (to_real x) = x // to_real(to_int(x)) <= x < to_real(to_int(x)) + 1 void mk_to_int_axiom(app* n) { - expr* x = 0, *y = 0; + expr* x = nullptr, *y = nullptr; VERIFY (a.is_to_int(n, x)); if (a.is_to_real(x, y)) { mk_axiom(th.mk_eq(y, n, false)); @@ -1070,7 +1070,7 @@ public: // is_int(x) <=> to_real(to_int(x)) = x void mk_is_int_axiom(app* n) { - expr* x = 0; + expr* x = nullptr; VERIFY(a.is_is_int(n, x)); literal eq = th.mk_eq(a.mk_to_real(a.mk_to_int(x)), x, false); literal is_int = ctx().get_literal(n); @@ -1450,7 +1450,7 @@ public: st = FC_GIVEUP; break; } - if (m_not_handled != 0) { + if (m_not_handled != nullptr) { TRACE("arith", tout << "unhandled operator " << mk_pp(m_not_handled, m) << "\n";); st = FC_GIVEUP; } @@ -2080,12 +2080,12 @@ public: m_core2.push_back(~c); } m_core2.push_back(lit); - justification * js = 0; + justification * js = nullptr; if (proofs_enabled()) { js = alloc(theory_lemma_justification, get_id(), ctx(), m_core2.size(), m_core2.c_ptr(), m_params.size(), m_params.c_ptr()); } - ctx().mk_clause(m_core2.size(), m_core2.c_ptr(), js, CLS_AUX_LEMMA, 0); + ctx().mk_clause(m_core2.size(), m_core2.c_ptr(), js, CLS_AUX_LEMMA, nullptr); } else { ctx().assign( @@ -2140,7 +2140,7 @@ public: rational const& k1 = b.get_value(); lp_bounds & bounds = m_bounds[v]; - lp_api::bound* end = 0; + lp_api::bound* end = nullptr; lp_api::bound* lo_inf = end, *lo_sup = end; lp_api::bound* hi_inf = end, *hi_sup = end; @@ -2798,7 +2798,7 @@ public: justification* js = ctx().mk_justification( ext_theory_eq_propagation_justification( - get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), x, y, 0, 0)); + get_id(), ctx().get_region(), m_core.size(), m_core.c_ptr(), m_eqs.size(), m_eqs.c_ptr(), x, y, 0, nullptr)); TRACE("arith", for (unsigned i = 0; i < m_core.size(); ++i) { diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index e389c819e..2fb0187f8 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -897,7 +897,7 @@ namespace smt { void theory_pb::watch_literal(literal lit, card* c) { init_watch(lit.var()); ptr_vector* cards = m_var_infos[lit.var()].m_lit_cwatch[lit.sign()]; - if (cards == 0) { + if (cards == nullptr) { cards = alloc(ptr_vector); m_var_infos[lit.var()].m_lit_cwatch[lit.sign()] = cards; } @@ -961,13 +961,13 @@ namespace smt { void theory_pb::add_clause(card& c, literal_vector const& lits) { m_stats.m_num_conflicts++; context& ctx = get_context(); - justification* js = 0; + justification* js = nullptr; c.inc_propagations(*this); if (!resolve_conflict(c, lits)) { if (proofs_enabled()) { js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); } - ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); + ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, nullptr); } SASSERT(ctx.inconsistent()); } @@ -1027,7 +1027,7 @@ namespace smt { } void theory_pb::assign_eh(bool_var v, bool is_true) { - ptr_vector* ineqs = 0; + ptr_vector* ineqs = nullptr; context& ctx = get_context(); literal nlit(v, is_true); init_watch(v); @@ -1060,7 +1060,7 @@ namespace smt { } ptr_vector* cards = m_var_infos[v].m_lit_cwatch[nlit.sign()]; - if (cards != 0 && !cards->empty() && !ctx.inconsistent()) { + if (cards != nullptr && !cards->empty() && !ctx.inconsistent()) { ptr_vector::iterator it = cards->begin(), it2 = it, end = cards->end(); for (; it != end; ++it) { if (ctx.get_assignment((*it)->lit()) != l_true) { @@ -1088,7 +1088,7 @@ namespace smt { } card* crd = m_var_infos[v].m_card; - if (crd != 0 && !ctx.inconsistent()) { + if (crd != nullptr && !ctx.inconsistent()) { crd->init_watch(*this, is_true); } @@ -1527,7 +1527,7 @@ namespace smt { else { z++; clear_watch(*c); - m_var_infos[v].m_card = 0; + m_var_infos[v].m_card = nullptr; dealloc(c); m_card_trail[i] = null_bool_var; ctx.remove_watch(v); @@ -1671,7 +1671,7 @@ namespace smt { if (v != null_bool_var) { card* c = m_var_infos[v].m_card; clear_watch(*c); - m_var_infos[v].m_card = 0; + m_var_infos[v].m_card = nullptr; dealloc(c); } } @@ -1774,11 +1774,11 @@ namespace smt { context& ctx = get_context(); TRACE("pb", tout << "#prop:" << c.m_num_propagations << " - " << lits << "\n"; display(tout, c, true);); - justification* js = 0; + justification* js = nullptr; if (proofs_enabled()) { js = alloc(theory_lemma_justification, get_id(), ctx, lits.size(), lits.c_ptr()); } - ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, 0); + ctx.mk_clause(lits.size(), lits.c_ptr(), js, CLS_AUX_LEMMA, nullptr); } @@ -1894,11 +1894,11 @@ namespace smt { break; case b_justification::JUSTIFICATION: { justification* j = js.get_justification(); - card_justification* pbj = 0; + card_justification* pbj = nullptr; if (j->get_from_theory() == get_id()) { pbj = dynamic_cast(j); } - if (pbj != 0) { + if (pbj != nullptr) { card& c2 = pbj->get_card(); result = card2expr(c2); } @@ -2170,11 +2170,11 @@ namespace smt { VERIFY(internalize_card(atl, false)); bool_var abv = ctx.get_bool_var(atl); m_antecedents.push_back(literal(abv)); - justification* js = 0; + justification* js = nullptr; if (proofs_enabled()) { - js = 0; // + js = nullptr; } - ctx.mk_clause(m_antecedents.size(), m_antecedents.c_ptr(), js, CLS_AUX_LEMMA, 0); + ctx.mk_clause(m_antecedents.size(), m_antecedents.c_ptr(), js, CLS_AUX_LEMMA, nullptr); } bool theory_pb::resolve_conflict(card& c, literal_vector const& confl) { @@ -2403,7 +2403,7 @@ namespace smt { } #endif SASSERT(validate_antecedents(m_antecedents)); - ctx.assign(alit, ctx.mk_justification(theory_propagation_justification(get_id(), ctx.get_region(), m_antecedents.size(), m_antecedents.c_ptr(), alit, 0, 0))); + ctx.assign(alit, ctx.mk_justification(theory_propagation_justification(get_id(), ctx.get_region(), m_antecedents.size(), m_antecedents.c_ptr(), alit, 0, nullptr))); DEBUG_CODE( m_antecedents.push_back(~alit); diff --git a/src/smt/theory_pb.h b/src/smt/theory_pb.h index 7e9c55a12..3a0ee723f 100644 --- a/src/smt/theory_pb.h +++ b/src/smt/theory_pb.h @@ -258,7 +258,7 @@ namespace smt { card_watch* m_lit_cwatch[2]; card* m_card; - var_info(): m_var_watch(0), m_ineq(0), m_card(0) + var_info(): m_var_watch(nullptr), m_ineq(nullptr), m_card(nullptr) { m_lit_watch[0] = nullptr; m_lit_watch[1] = nullptr; diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 857691b8b..233be9e31 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -1120,7 +1120,7 @@ bool theory_seq::find_better_rep(expr_ref_vector const& ls, expr_ref_vector cons break; } if (flag) { - expr* nl_fst = 0; + expr* nl_fst = nullptr; if (e.rs().size()>1 && is_var(e.rs().get(0))) nl_fst = e.rs().get(0); if (nl_fst && nl_fst != r_fst) { @@ -1173,7 +1173,7 @@ bool theory_seq::find_better_rep(expr_ref_vector const& ls, expr_ref_vector cons break; } if (flag) { - expr* nl_fst = 0; + expr* nl_fst = nullptr; if (e.rs().size()>1 && is_var(e.rs().get(0))) nl_fst = e.rs().get(0); if (nl_fst && nl_fst != r_fst) { diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index aadcb63a7..d902aa533 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -6745,8 +6745,8 @@ namespace smt { } unsigned theory_str::estimate_automata_intersection_difficulty(eautomaton * aut1, eautomaton * aut2) { - ENSURE(aut1 != NULL); - ENSURE(aut2 != NULL); + ENSURE(aut1 != nullptr); + ENSURE(aut2 != nullptr); return _qmul(aut1->num_states(), aut2->num_states()); } @@ -6999,7 +6999,7 @@ namespace smt { * and the equality with 0 is based on whether solutions of length 0 are allowed. */ void theory_str::find_automaton_initial_bounds(expr * str_in_re, eautomaton * aut) { - ENSURE(aut != NULL); + ENSURE(aut != nullptr); context & ctx = get_context(); ast_manager & m = get_manager(); @@ -7051,7 +7051,7 @@ namespace smt { * if it exists, or -1 otherwise. */ bool theory_str::refine_automaton_lower_bound(eautomaton * aut, rational current_lower_bound, rational & refined_lower_bound) { - ENSURE(aut != NULL); + ENSURE(aut != nullptr); if (aut->final_states().size() < 1) { // no solutions at all @@ -7161,7 +7161,7 @@ namespace smt { * if a shorter solution exists, or -1 otherwise. */ bool theory_str::refine_automaton_upper_bound(eautomaton * aut, rational current_upper_bound, rational & refined_upper_bound) { - ENSURE(aut != NULL); + ENSURE(aut != nullptr); if (aut->final_states().empty()) { // no solutions at all! @@ -7280,7 +7280,7 @@ namespace smt { return retval; } else { TRACE("str", tout << "ERROR: unrecognized automaton path constraint " << mk_pp(cond, m) << ", cannot translate" << std::endl;); - retval = NULL; + retval = nullptr; return retval; } } @@ -7293,7 +7293,7 @@ namespace smt { * are returned in `characterConstraints`. */ expr_ref theory_str::generate_regex_path_constraints(expr * stringTerm, eautomaton * aut, rational lenVal, expr_ref & characterConstraints) { - ENSURE(aut != NULL); + ENSURE(aut != nullptr); context & ctx = get_context(); ast_manager & m = get_manager(); @@ -10582,12 +10582,12 @@ namespace smt { } } // foreach(term in str_in_re_terms) - eautomaton * aut_inter = NULL; + eautomaton * aut_inter = nullptr; CTRACE("str", !intersect_constraints.empty(), tout << "check intersection of automata constraints for " << mk_pp(str, m) << std::endl;); for (svector::iterator aut_it = intersect_constraints.begin(); aut_it != intersect_constraints.end(); ++aut_it) { regex_automaton_under_assumptions aut = *aut_it; - if (aut_inter == NULL) { + if (aut_inter == nullptr) { // start somewhere aut_inter = aut.get_automaton(); used_intersect_constraints.push_back(aut); @@ -10637,7 +10637,7 @@ namespace smt { } } } // foreach(entry in intersect_constraints) - if (aut_inter != NULL) { + if (aut_inter != nullptr) { aut_inter->compress(); } TRACE("str", tout << "intersected " << used_intersect_constraints.size() << " constraints" << std::endl;); @@ -10668,7 +10668,7 @@ namespace smt { } conflict_lhs = mk_and(conflict_terms); - if (used_intersect_constraints.size() > 1 && aut_inter != NULL) { + if (used_intersect_constraints.size() > 1 && aut_inter != nullptr) { // check whether the intersection is only the empty string unsigned initial_state = aut_inter->init(); if (aut_inter->final_states().size() == 1 && aut_inter->is_final_state(initial_state)) { @@ -10686,7 +10686,7 @@ namespace smt { } } - if (aut_inter != NULL && aut_inter->is_empty()) { + if (aut_inter != nullptr && aut_inter->is_empty()) { TRACE("str", tout << "product automaton is empty; asserting conflict clause" << std::endl;); expr_ref conflict_clause(m.mk_not(mk_and(conflict_terms)), m); assert_axiom(conflict_clause); @@ -12231,7 +12231,7 @@ namespace smt { // - in the same EQC as freeVar if (term_appears_as_subterm(freeVar, re_str)) { TRACE("str", tout << "prevent value testing on free var " << mk_pp(freeVar, m) << " as it belongs to one or more regex constraints." << std::endl;); - return NULL; + return nullptr; } } } diff --git a/src/smt/theory_str.h b/src/smt/theory_str.h index 419084091..b626c5b07 100644 --- a/src/smt/theory_str.h +++ b/src/smt/theory_str.h @@ -164,7 +164,7 @@ protected: rational upper_bound; public: regex_automaton_under_assumptions() : - re_term(NULL), aut(NULL), polarity(false), + re_term(nullptr), aut(nullptr), polarity(false), assume_lower_bound(false), assume_upper_bound(false) {} regex_automaton_under_assumptions(expr * re_term, eautomaton * aut, bool polarity) : diff --git a/src/solver/parallel_tactic.cpp b/src/solver/parallel_tactic.cpp index 142ba4bb8..f0bb65315 100644 --- a/src/solver/parallel_tactic.cpp +++ b/src/solver/parallel_tactic.cpp @@ -676,7 +676,7 @@ public: fail_if_proof_generation("parallel-tactic", g); ast_manager& m = g->m(); solver* s = m_solver->translate(m, m_params); - solver_state* st = alloc(solver_state, 0, s, m_params); + solver_state* st = alloc(solver_state, nullptr, s, m_params); m_queue.add_task(st); expr_ref_vector clauses(m); ptr_vector assumptions; diff --git a/src/solver/solver_na2as.cpp b/src/solver/solver_na2as.cpp index 41853b19a..a3fcd0e0b 100644 --- a/src/solver/solver_na2as.cpp +++ b/src/solver/solver_na2as.cpp @@ -31,7 +31,7 @@ solver_na2as::solver_na2as(ast_manager & m): solver_na2as::~solver_na2as() {} void solver_na2as::assert_expr_core2(expr * t, expr * a) { - if (a == 0) { + if (a == nullptr) { assert_expr_core(t); } else { diff --git a/src/tactic/arith/degree_shift_tactic.cpp b/src/tactic/arith/degree_shift_tactic.cpp index c15285703..b713c3055 100644 --- a/src/tactic/arith/degree_shift_tactic.cpp +++ b/src/tactic/arith/degree_shift_tactic.cpp @@ -204,7 +204,7 @@ class degree_shift_tactic : public tactic { for (auto const& kv : m_var2degree) { SASSERT(kv.m_value.is_int()); SASSERT(kv.m_value >= rational(2)); - app * fresh = m.mk_fresh_const(0, kv.m_key->get_decl()->get_range()); + app * fresh = m.mk_fresh_const(nullptr, kv.m_key->get_decl()->get_range()); m_pinned.push_back(fresh); m_var2var.insert(kv.m_key, fresh); if (m_produce_models) { diff --git a/src/tactic/arith/lia2card_tactic.cpp b/src/tactic/arith/lia2card_tactic.cpp index 88e4a5583..027b6d91c 100644 --- a/src/tactic/arith/lia2card_tactic.cpp +++ b/src/tactic/arith/lia2card_tactic.cpp @@ -36,7 +36,7 @@ class lia2card_tactic : public tactic { expr* m_expr; bound(unsigned lo, unsigned hi, expr* b): m_lo(lo), m_hi(hi), m_expr(b) {} - bound(): m_lo(0), m_hi(0), m_expr(0) {} + bound(): m_lo(0), m_hi(0), m_expr(nullptr) {} }; struct lia_rewriter_cfg : public default_rewriter_cfg { diff --git a/src/tactic/fd_solver/enum2bv_solver.cpp b/src/tactic/fd_solver/enum2bv_solver.cpp index a864d9631..185f23d13 100644 --- a/src/tactic/fd_solver/enum2bv_solver.cpp +++ b/src/tactic/fd_solver/enum2bv_solver.cpp @@ -148,7 +148,7 @@ public: // translate enumeration constants to bit-vectors. for (expr* v : vars) { - func_decl* f = 0; + func_decl* f = nullptr; if (is_app(v) && is_uninterp_const(v) && m_rewriter.enum2bv().find(to_app(v)->get_decl(), f)) { bvars.push_back(m.mk_const(f)); } diff --git a/src/tactic/generic_model_converter.h b/src/tactic/generic_model_converter.h index c67dd5eff..69bc35a3f 100644 --- a/src/tactic/generic_model_converter.h +++ b/src/tactic/generic_model_converter.h @@ -45,7 +45,7 @@ public: void hide(expr* e) { SASSERT(is_app(e) && to_app(e)->get_num_args() == 0); hide(to_app(e)->get_decl()); } - void hide(func_decl * f) { m_entries.push_back(entry(f, 0, m, HIDE)); } + void hide(func_decl * f) { m_entries.push_back(entry(f, nullptr, m, HIDE)); } void add(func_decl * d, expr* e); diff --git a/src/tactic/portfolio/solver2lookahead.cpp b/src/tactic/portfolio/solver2lookahead.cpp index 0c18ab079..63b28793d 100644 --- a/src/tactic/portfolio/solver2lookahead.cpp +++ b/src/tactic/portfolio/solver2lookahead.cpp @@ -20,5 +20,5 @@ Notes: #include "solver/solver.h" solver * mk_solver2lookahead(solver* s) { - return 0; + return nullptr; } diff --git a/src/tactic/proof_converter.cpp b/src/tactic/proof_converter.cpp index f1a209487..faa9a36db 100644 --- a/src/tactic/proof_converter.cpp +++ b/src/tactic/proof_converter.cpp @@ -130,7 +130,7 @@ proof_ref apply(ast_manager & m, proof_converter_ref & pc1, proof_converter_ref_ for (unsigned i = 0; i < sz; i++) { proof_ref pr(m); SASSERT(pc2s[i]); // proof production is enabled - pr = pc2s[i]->operator()(m, 0, 0); + pr = pc2s[i]->operator()(m, 0, nullptr); prs.push_back(pr); } return (*pc1)(m, sz, prs.c_ptr()); diff --git a/src/tactic/sine_filter.cpp b/src/tactic/sine_filter.cpp index b1db12964..1612670a4 100644 --- a/src/tactic/sine_filter.cpp +++ b/src/tactic/sine_filter.cpp @@ -54,7 +54,7 @@ public: TRACE("sine", tout << new_forms.size();); g->reset(); for (unsigned i = 0; i < new_forms.size(); i++) { - g->assert_expr(new_forms.get(i), 0, 0); + g->assert_expr(new_forms.get(i), nullptr, nullptr); } g->inc_depth(); g->updt_prec(goal::OVER); diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index 43087f50f..a71ea738c 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -636,7 +636,7 @@ public: else { SASSERT(is_decided_unsat(r2)); - if (cores_enabled && r2[0]->dep(0) != 0) { + if (cores_enabled && r2[0]->dep(0) != nullptr) { expr_dependency_ref * new_dep = alloc(expr_dependency_ref, new_m); *new_dep = r2[0]->dep(0); core_buffer.set(i, new_dep); @@ -674,7 +674,7 @@ public: ast_translation translator(*(managers[i]), m, false); goal_ref_buffer * r = goals_vect[i]; unsigned j = result.size(); - if (r != 0) { + if (r != nullptr) { for (unsigned k = 0; k < r->size(); k++) { result.push_back((*r)[k]->translate(translator)); } diff --git a/src/test/main.cpp b/src/test/main.cpp index d330610d9..811b67407 100644 --- a/src/test/main.cpp +++ b/src/test/main.cpp @@ -78,11 +78,11 @@ void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage, bool& t int i = 1; while (i < argc) { char * arg = argv[i]; - char * eq_pos = 0; + char * eq_pos = nullptr; if (arg[0] == '-' || arg[0] == '/') { char * opt_name = arg + 1; - char * opt_arg = 0; + char * opt_arg = nullptr; char * colon = strchr(arg, ':'); if (colon) { opt_arg = colon + 1; @@ -97,7 +97,7 @@ void parse_cmd_line_args(int argc, char ** argv, bool& do_display_usage, bool& t else if (strcmp(opt_name, "v") == 0) { if (!opt_arg) error("option argument (/v:level) is missing."); - long lvl = strtol(opt_arg, 0, 10); + long lvl = strtol(opt_arg, nullptr, 10); set_verbosity_level(lvl); } else if (strcmp(opt_name, "w") == 0) { diff --git a/src/test/smt2print_parse.cpp b/src/test/smt2print_parse.cpp index 99ffb726c..81b7ba1e9 100644 --- a/src/test/smt2print_parse.cpp +++ b/src/test/smt2print_parse.cpp @@ -19,7 +19,7 @@ void test_print(Z3_context ctx, Z3_ast_vector av) { Z3_ast a = Z3_mk_and(ctx, Z3_ast_vector_size(ctx, av), args); Z3_inc_ref(ctx, a); delete[] args; - char const* spec1 = Z3_benchmark_to_smtlib_string(ctx, "test", 0, 0, 0, 0, 0, a); + char const* spec1 = Z3_benchmark_to_smtlib_string(ctx, "test", nullptr, nullptr, nullptr, 0, nullptr, a); Z3_dec_ref(ctx, a); std::cout << "spec1: benchmark->string\n" << spec1 << "\n"; diff --git a/src/util/lp/nra_solver.cpp b/src/util/lp/nra_solver.cpp index 0d28058e8..200bdd2bc 100644 --- a/src/util/lp/nra_solver.cpp +++ b/src/util/lp/nra_solver.cpp @@ -154,7 +154,7 @@ namespace nra { polynomial::polynomial* ps[1] = { p }; bool even[1] = { false }; nlsat::literal lit = m_nlsat->mk_ineq_literal(nlsat::atom::kind::EQ, 1, ps, even); - m_nlsat->mk_clause(1, &lit, 0); + m_nlsat->mk_clause(1, &lit, nullptr); } void add_constraint(unsigned idx) { diff --git a/src/util/obj_ref.h b/src/util/obj_ref.h index 885601375..8908f2cdd 100644 --- a/src/util/obj_ref.h +++ b/src/util/obj_ref.h @@ -115,7 +115,7 @@ public: */ T * steal() { T * r = m_obj; - m_obj = 0; + m_obj = nullptr; return r; } }; From 489582f7fa9986e6576442ebce4943a754f7bdbd Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Tue, 2 Oct 2018 09:19:14 +0700 Subject: [PATCH 105/138] Remove unused sat_par files. These look like they were replaced by `sat_parallel` files and aren't currently built or used. --- src/sat/sat_par.cpp | 45 -------------------------------------------- src/sat/sat_par.h | 39 -------------------------------------- src/sat/sat_solver.h | 1 - 3 files changed, 85 deletions(-) delete mode 100644 src/sat/sat_par.cpp delete mode 100644 src/sat/sat_par.h diff --git a/src/sat/sat_par.cpp b/src/sat/sat_par.cpp deleted file mode 100644 index e3d5727ed..000000000 --- a/src/sat/sat_par.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - sat_par.cpp - -Abstract: - - Utilities for parallel SAT solving. - -Author: - - Nikolaj Bjorner (nbjorner) 2017-1-29. - -Revision History: - ---*/ -#include "sat/sat_par.h" - - -namespace sat { - - par::par() {} - - void par::exchange(literal_vector const& in, unsigned& limit, literal_vector& out) { - #pragma omp critical (par_solver) - { - if (limit < m_units.size()) { - // this might repeat some literals. - out.append(m_units.size() - limit, m_units.c_ptr() + limit); - } - for (unsigned i = 0; i < in.size(); ++i) { - literal lit = in[i]; - if (!m_unit_set.contains(lit.index())) { - m_unit_set.insert(lit.index()); - m_units.push_back(lit); - } - } - limit = m_units.size(); - } - } - -}; - diff --git a/src/sat/sat_par.h b/src/sat/sat_par.h deleted file mode 100644 index 001036a98..000000000 --- a/src/sat/sat_par.h +++ /dev/null @@ -1,39 +0,0 @@ -/*++ -Copyright (c) 2017 Microsoft Corporation - -Module Name: - - sat_par.h - -Abstract: - - Utilities for parallel SAT solving. - -Author: - - Nikolaj Bjorner (nbjorner) 2017-1-29. - -Revision History: - ---*/ -#ifndef SAT_PAR_H_ -#define SAT_PAR_H_ - -#include "sat/sat_types.h" -#include "util/hashtable.h" -#include "util/map.h" - -namespace sat { - - class par { - typedef hashtable index_set; - literal_vector m_units; - index_set m_unit_set; - public: - par(); - void exchange(literal_vector const& in, unsigned& limit, literal_vector& out); - }; - -}; - -#endif diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index ad972b2af..e6e6c9d7b 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -37,7 +37,6 @@ Revision History: #include "sat/sat_drat.h" #include "sat/sat_parallel.h" #include "sat/sat_local_search.h" -#include "sat/sat_par.h" #include "util/params.h" #include "util/statistics.h" #include "util/stopwatch.h" From bb979a194e5baa3dc3c0632f00825e7c0627c40a Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 1 Oct 2018 19:32:44 -0700 Subject: [PATCH 106/138] remove unused return value of mk_enode following #1856 Signed-off-by: Nikolaj Bjorner --- src/smt/theory_lra.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 5b1de851e..c5dee4600 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -341,7 +341,7 @@ class theory_lra::imp { } app_ref cnst(a.mk_numeral(rational(c), is_int), m); TRACE("arith", tout << "add " << cnst << "\n";); - enode* e = mk_enode(cnst); + mk_enode(cnst); theory_var v = mk_var(cnst); var = m_solver->add_var(v, true); m_theory_var2var_index.setx(v, var, UINT_MAX); From be8a9c611e17d526792cb5b1b192726704b23a76 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 1 Oct 2018 19:49:18 -0700 Subject: [PATCH 107/138] incorporate #1854 Signed-off-by: Nikolaj Bjorner --- CMakeLists.txt | 4 ++++ src/util/memory_manager.h | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a086afd71..28cd9fa33 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -383,6 +383,10 @@ if (("${TARGET_ARCHITECTURE}" STREQUAL "x86_64") OR ("${TARGET_ARCHITECTURE}" ST if (("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU") OR ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) set(SSE_FLAGS "-mfpmath=sse" "-msse" "-msse2") # FIXME: Remove "x.." when CMP0054 is set to NEW + elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Intel") + set(SSE_FLAGS "-mfpmath=sse" "-msse" "-msse2") + # Intel's compiler requires linking with libiomp5 + list(APPEND Z3_DEPENDENT_LIBS "iomp5") elseif ("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC") set(SSE_FLAGS "/arch:SSE2") else() diff --git a/src/util/memory_manager.h b/src/util/memory_manager.h index 5cb7dc467..fec7c12b4 100644 --- a/src/util/memory_manager.h +++ b/src/util/memory_manager.h @@ -27,7 +27,9 @@ Revision History: # define __has_builtin(x) 0 #endif -#ifdef __GNUC__ +#ifdef __INTEL_COMPILER +# define ALLOC_ATTR __attribute__((malloc)) +#elif __GNUC__ # if (__GNUC__ * 100 + __GNUC_MINOR__) >= 409 || __has_builtin(returns_nonnull) # define GCC_RET_NON_NULL __attribute__((returns_nonnull)) # else From 9d0aa4d02d3c2fb5c33abbffa55358bed8e65f33 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Mon, 1 Oct 2018 20:22:02 -0700 Subject: [PATCH 108/138] update empty cube case for sat/undef cases Signed-off-by: Nikolaj Bjorner --- src/sat/sat_lookahead.cpp | 20 ++++++++++---------- src/sat/sat_lookahead.h | 2 ++ src/sat/sat_solver.cpp | 1 + src/sat/sat_solver/inc_sat_solver.cpp | 25 +++++++++++++++++-------- 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/sat/sat_lookahead.cpp b/src/sat/sat_lookahead.cpp index bbc1106bb..a6ff6a3b0 100644 --- a/src/sat/sat_lookahead.cpp +++ b/src/sat/sat_lookahead.cpp @@ -2057,6 +2057,15 @@ namespace sat { return h; } + bool lookahead::should_cutoff(unsigned depth) { + return depth > 0 && + ((m_config.m_cube_cutoff == depth_cutoff && depth == m_config.m_cube_depth) || + (m_config.m_cube_cutoff == freevars_cutoff && m_freevars.size() <= m_init_freevars * m_config.m_cube_freevars) || + (m_config.m_cube_cutoff == psat_cutoff && psat_heur() >= m_config.m_cube_psat_trigger) || + (m_config.m_cube_cutoff == adaptive_freevars_cutoff && m_freevars.size() < m_cube_state.m_freevars_threshold) || + (m_config.m_cube_cutoff == adaptive_psat_cutoff && psat_heur() >= m_cube_state.m_psat_threshold)); + } + lbool lookahead::cube(bool_var_vector& vars, literal_vector& lits, unsigned backtrack_level) { scoped_ext _scoped_ext(*this); lits.reset(); @@ -2102,22 +2111,13 @@ namespace sat { } backtrack_level = UINT_MAX; depth = m_cube_state.m_cube.size(); - if ((m_config.m_cube_cutoff == depth_cutoff && depth == m_config.m_cube_depth) || - (m_config.m_cube_cutoff == freevars_cutoff && m_freevars.size() <= m_init_freevars * m_config.m_cube_freevars) || - (m_config.m_cube_cutoff == psat_cutoff && psat_heur() >= m_config.m_cube_psat_trigger) || - (m_config.m_cube_cutoff == adaptive_freevars_cutoff && m_freevars.size() < m_cube_state.m_freevars_threshold) || - (m_config.m_cube_cutoff == adaptive_psat_cutoff && psat_heur() >= m_cube_state.m_psat_threshold)) { + if (should_cutoff(depth)) { double dec = (1.0 - pow(m_config.m_cube_fraction, depth)); m_cube_state.m_freevars_threshold *= dec; m_cube_state.m_psat_threshold *= 2.0 - dec; set_conflict(); m_cube_state.inc_cutoff(); -#if 0 - // return cube of all literals, not just the ones in the main cube - lits.append(m_trail.size() - init_trail, m_trail.c_ptr() + init_trail); -#else lits.append(m_cube_state.m_cube); -#endif vars.reset(); for (auto v : m_freevars) if (in_reduced_clause(v)) vars.push_back(v); backtrack(m_cube_state.m_cube, m_cube_state.m_is_decision); diff --git a/src/sat/sat_lookahead.h b/src/sat/sat_lookahead.h index df954bdf1..c2282e779 100644 --- a/src/sat/sat_lookahead.h +++ b/src/sat/sat_lookahead.h @@ -558,6 +558,8 @@ namespace sat { double psat_heur(); + bool should_cutoff(unsigned depth); + public: lookahead(solver& s) : m_s(s), diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index b58d2296b..b65abd41c 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1030,6 +1030,7 @@ namespace sat { } break; case l_true: { + lits.reset(); pop_to_base_level(); model const& mdl = m_cuber->get_model(); for (bool_var v = 0; v < mdl.size(); ++v) { diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 5de64b496..7a77e3b4e 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -308,6 +308,12 @@ public: return nullptr; } + expr_ref_vector last_cube() { + expr_ref_vector result(m); + result.push_back(m.mk_false()); + return result; + } + expr_ref_vector cube(expr_ref_vector& vs, unsigned backtrack_level) override { if (!is_internalized()) { lbool r = internalize_formulas(); @@ -326,15 +332,18 @@ public: } sat::literal_vector lits; lbool result = m_solver.cube(vars, lits, backtrack_level); - if (result == l_false || lits.empty()) { - expr_ref_vector result(m); - result.push_back(m.mk_false()); - return result; - } - if (result == l_true) { - IF_VERBOSE(1, verbose_stream() << "formulas are SAT\n"); + switch (result) { + case l_true: return expr_ref_vector(m); - } + case l_false: + return last_cube(); + default: + SASSERT(!lits.empty()); + if (lits.empty()) { + IF_VERBOSE(0, verbose_stream() << "empty cube for undef\n";); + } + break; + } expr_ref_vector fmls(m); expr_ref_vector lit2expr(m); lit2expr.resize(m_solver.num_vars() * 2); From 373b691709790b276e9b69c8c9c57ca1540c51fd Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Tue, 2 Oct 2018 10:26:38 +0700 Subject: [PATCH 109/138] Use 'override' where possible. --- src/muz/spacer/spacer_generalizers.cpp | 2 +- src/muz/spacer/spacer_generalizers.h | 8 +- src/muz/transforms/dl_mk_synchronize.h | 2 +- src/opt/maxres.cpp | 10 +- src/opt/opt_cmds.cpp | 54 +++++------ src/opt/sortmax.cpp | 4 +- src/opt/wmax.cpp | 4 +- src/sat/ba_solver.h | 106 ++++++++++----------- src/sat/sat_lookahead.cpp | 2 +- src/sat/tactic/goal2sat.h | 2 +- src/smt/theory_lra.cpp | 2 +- src/smt/theory_pb.cpp | 6 +- src/smt/theory_str.cpp | 2 +- src/solver/parallel_tactic.cpp | 12 +-- src/tactic/core/symmetry_reduce_tactic.cpp | 2 +- src/tactic/dependency_converter.cpp | 18 ++-- src/tactic/generic_model_converter.h | 2 +- src/tactic/tactical.cpp | 6 +- src/util/lp/lar_solver.h | 2 +- 19 files changed, 123 insertions(+), 123 deletions(-) diff --git a/src/muz/spacer/spacer_generalizers.cpp b/src/muz/spacer/spacer_generalizers.cpp index b0fb6c2d3..6f9758337 100644 --- a/src/muz/spacer/spacer_generalizers.cpp +++ b/src/muz/spacer/spacer_generalizers.cpp @@ -50,7 +50,7 @@ namespace{ contains_array_op_proc(ast_manager &manager) : m(manager), m_array_fid(m.mk_family_id("array")) {} - virtual bool operator()(expr *e) { + bool operator()(expr *e) override { return is_app(e) && to_app(e)->get_family_id() == m_array_fid; } }; diff --git a/src/muz/spacer/spacer_generalizers.h b/src/muz/spacer/spacer_generalizers.h index 45ae472bd..e83cc1bc9 100644 --- a/src/muz/spacer/spacer_generalizers.h +++ b/src/muz/spacer/spacer_generalizers.h @@ -117,11 +117,11 @@ class lemma_quantifier_generalizer : public lemma_generalizer { int m_offset; public: lemma_quantifier_generalizer(context &ctx, bool normalize_cube = true); - virtual ~lemma_quantifier_generalizer() {} - virtual void operator()(lemma_ref &lemma); + ~lemma_quantifier_generalizer() override {} + void operator()(lemma_ref &lemma) override; - virtual void collect_statistics(statistics& st) const; - virtual void reset_statistics() {m_st.reset();} + void collect_statistics(statistics& st) const override; + void reset_statistics() override {m_st.reset();} private: bool generalize(lemma_ref &lemma, app *term); diff --git a/src/muz/transforms/dl_mk_synchronize.h b/src/muz/transforms/dl_mk_synchronize.h index 6f4b3ca40..77f45e91f 100644 --- a/src/muz/transforms/dl_mk_synchronize.h +++ b/src/muz/transforms/dl_mk_synchronize.h @@ -126,7 +126,7 @@ namespace datalog { */ mk_synchronize(context & ctx, unsigned priority = 22500); - rule_set * operator()(rule_set const & source); + rule_set * operator()(rule_set const & source) override; }; }; diff --git a/src/opt/maxres.cpp b/src/opt/maxres.cpp index f52c56a60..0b5f20db8 100644 --- a/src/opt/maxres.cpp +++ b/src/opt/maxres.cpp @@ -144,7 +144,7 @@ public: } } - virtual ~maxres() {} + ~maxres() override {} bool is_literal(expr* l) { return @@ -332,7 +332,7 @@ public: } - virtual lbool operator()() { + lbool operator()() override { m_defs.reset(); switch(m_st) { case s_primal: @@ -343,7 +343,7 @@ public: return l_undef; } - virtual void collect_statistics(statistics& st) const { + void collect_statistics(statistics& st) const override { st.update("maxres-cores", m_stats.m_num_cores); st.update("maxres-correction-sets", m_stats.m_num_cs); } @@ -781,7 +781,7 @@ public: TRACE("opt", tout << "after remove: " << asms << "\n";); } - virtual void updt_params(params_ref& _p) { + void updt_params(params_ref& _p) override { maxsmt_solver_base::updt_params(_p); opt_params p(_p); m_hill_climb = p.maxres_hill_climb(); @@ -816,7 +816,7 @@ public: return l_true; } - virtual void commit_assignment() { + void commit_assignment() override { if (m_found_feasible_optimum) { TRACE("opt", tout << "Committing feasible solution\n" << m_defs << " " << m_asms;); s().assert_expr(m_defs); diff --git a/src/opt/opt_cmds.cpp b/src/opt/opt_cmds.cpp index d8e301e08..f01df0bdd 100644 --- a/src/opt/opt_cmds.cpp +++ b/src/opt/opt_cmds.cpp @@ -56,33 +56,33 @@ public: m_opt(opt) {} - virtual ~assert_soft_cmd() { + ~assert_soft_cmd() override { } - virtual void reset(cmd_context & ctx) { + void reset(cmd_context & ctx) override { m_idx = 0; m_formula = nullptr; } - virtual char const * get_usage() const { return " [:weight ] [:id ]"; } - virtual char const * get_main_descr() const { return "assert soft constraint with optional weight and identifier"; } + char const * get_usage() const override { return " [:weight ] [:id ]"; } + char const * get_main_descr() const override { return "assert soft constraint with optional weight and identifier"; } // command invocation - virtual void prepare(cmd_context & ctx) { + void prepare(cmd_context & ctx) override { reset(ctx); } - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { if (m_idx == 0) return CPK_EXPR; return parametric_cmd::next_arg_kind(ctx); } - virtual void init_pdescrs(cmd_context & ctx, param_descrs & p) { + void init_pdescrs(cmd_context & ctx, param_descrs & p) override { p.insert("weight", CPK_NUMERAL, "(default: 1) penalty of not satisfying constraint."); p.insert("id", CPK_SYMBOL, "(default: null) partition identifier for soft constraints."); } - virtual void set_next_arg(cmd_context & ctx, expr * t) { + void set_next_arg(cmd_context & ctx, expr * t) override { SASSERT(m_idx == 0); if (!ctx.m().is_bool(t)) { throw cmd_exception("Invalid type for expression. Expected Boolean type."); @@ -91,11 +91,11 @@ public: ++m_idx; } - virtual void failure_cleanup(cmd_context & ctx) { + void failure_cleanup(cmd_context & ctx) override { reset(ctx); } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { if (!m_formula) { throw cmd_exception("assert-soft requires a formulas as argument."); } @@ -107,7 +107,7 @@ public: reset(ctx); } - virtual void finalize(cmd_context & ctx) { + void finalize(cmd_context & ctx) override { } }; @@ -123,14 +123,14 @@ public: m_opt(opt) {} - virtual void reset(cmd_context & ctx) { } - virtual char const * get_usage() const { return ""; } - virtual char const * get_descr(cmd_context & ctx) const { return "check sat modulo objective function";} - virtual unsigned get_arity() const { return 1; } - virtual void prepare(cmd_context & ctx) {} - virtual cmd_arg_kind next_arg_kind(cmd_context & ctx) const { return CPK_EXPR; } + void reset(cmd_context & ctx) override { } + char const * get_usage() const override { return ""; } + char const * get_descr(cmd_context & ctx) const override { return "check sat modulo objective function";} + unsigned get_arity() const override { return 1; } + void prepare(cmd_context & ctx) override {} + cmd_arg_kind next_arg_kind(cmd_context & ctx) const override { return CPK_EXPR; } - virtual void set_next_arg(cmd_context & ctx, expr * t) { + void set_next_arg(cmd_context & ctx, expr * t) override { if (!is_app(t)) { throw cmd_exception("malformed objective term: it cannot be a quantifier or bound variable"); } @@ -138,11 +138,11 @@ public: ctx.print_success(); } - virtual void failure_cleanup(cmd_context & ctx) { + void failure_cleanup(cmd_context & ctx) override { reset(ctx); } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { } }; @@ -154,18 +154,18 @@ public: m_opt(opt) {} - virtual void reset(cmd_context & ctx) { } - virtual char const * get_usage() const { return "(get-objectives)"; } - virtual char const * get_descr(cmd_context & ctx) const { return "retrieve the objective values (after optimization)"; } - virtual unsigned get_arity() const { return 0; } - virtual void prepare(cmd_context & ctx) {} + void reset(cmd_context & ctx) override { } + char const * get_usage() const override { return "(get-objectives)"; } + char const * get_descr(cmd_context & ctx) const override { return "retrieve the objective values (after optimization)"; } + unsigned get_arity() const override { return 0; } + void prepare(cmd_context & ctx) override {} - virtual void failure_cleanup(cmd_context & ctx) { + void failure_cleanup(cmd_context & ctx) override { reset(ctx); } - virtual void execute(cmd_context & ctx) { + void execute(cmd_context & ctx) override { if (!ctx.ignore_check()) { get_opt(ctx, m_opt).display_assignment(ctx.regular_stream()); } diff --git a/src/opt/sortmax.cpp b/src/opt/sortmax.cpp index 1fafa12bd..df361f24a 100644 --- a/src/opt/sortmax.cpp +++ b/src/opt/sortmax.cpp @@ -39,9 +39,9 @@ namespace opt { sortmax(maxsat_context& c, weights_t& ws, expr_ref_vector const& soft): maxsmt_solver_base(c, ws, soft), m_sort(*this), m_trail(m), m_fresh(m) {} - virtual ~sortmax() {} + ~sortmax() override {} - lbool operator()() { + lbool operator()() override { obj_map soft; if (!init()) { return l_false; diff --git a/src/opt/wmax.cpp b/src/opt/wmax.cpp index e4eb7e06b..ee7f92a23 100644 --- a/src/opt/wmax.cpp +++ b/src/opt/wmax.cpp @@ -49,9 +49,9 @@ namespace opt { m_trail(m), m_defs(m) {} - virtual ~wmax() {} + ~wmax() override {} - lbool operator()() { + lbool operator()() override { TRACE("opt", tout << "weighted maxsat\n";); scoped_ensure_theory wth(*this); obj_map soft; diff --git a/src/sat/ba_solver.h b/src/sat/ba_solver.h index bae59f45a..26887e3b1 100644 --- a/src/sat/ba_solver.h +++ b/src/sat/ba_solver.h @@ -128,7 +128,7 @@ namespace sat { virtual void set_k(unsigned k) { VERIFY(k < 4000000000); m_k = k; } virtual unsigned get_coeff(unsigned i) const { UNREACHABLE(); return 0; } unsigned k() const { return m_k; } - virtual bool well_formed() const; + bool well_formed() const override; }; class card : public pb_base { @@ -140,13 +140,13 @@ namespace sat { literal& operator[](unsigned i) { return m_lits[i]; } literal const* begin() const { return m_lits; } literal const* end() const { return static_cast(m_lits) + m_size; } - virtual void negate(); - virtual void swap(unsigned i, unsigned j) { std::swap(m_lits[i], m_lits[j]); } - virtual literal_vector literals() const { return literal_vector(m_size, m_lits); } - virtual bool is_watching(literal l) const; - virtual literal get_lit(unsigned i) const { return m_lits[i]; } - virtual void set_lit(unsigned i, literal l) { m_lits[i] = l; } - virtual unsigned get_coeff(unsigned i) const { return 1; } + void negate() override; + void swap(unsigned i, unsigned j) override { std::swap(m_lits[i], m_lits[j]); } + literal_vector literals() const override { return literal_vector(m_size, m_lits); } + bool is_watching(literal l) const override; + literal get_lit(unsigned i) const override { return m_lits[i]; } + void set_lit(unsigned i, literal l) override { m_lits[i] = l; } + unsigned get_coeff(unsigned i) const override { return 1; } }; @@ -173,14 +173,14 @@ namespace sat { void update_max_sum(); void set_num_watch(unsigned s) { m_num_watch = s; } bool is_cardinality() const; - virtual void negate(); - virtual void set_k(unsigned k) { m_k = k; VERIFY(k < 4000000000); update_max_sum(); } - virtual void swap(unsigned i, unsigned j) { std::swap(m_wlits[i], m_wlits[j]); } - virtual literal_vector literals() const { literal_vector lits; for (auto wl : *this) lits.push_back(wl.second); return lits; } - virtual bool is_watching(literal l) const; - virtual literal get_lit(unsigned i) const { return m_wlits[i].second; } - virtual void set_lit(unsigned i, literal l) { m_wlits[i].second = l; } - virtual unsigned get_coeff(unsigned i) const { return m_wlits[i].first; } + void negate() override; + void set_k(unsigned k) override { m_k = k; VERIFY(k < 4000000000); update_max_sum(); } + void swap(unsigned i, unsigned j) override { std::swap(m_wlits[i], m_wlits[j]); } + literal_vector literals() const override { literal_vector lits; for (auto wl : *this) lits.push_back(wl.second); return lits; } + bool is_watching(literal l) const override; + literal get_lit(unsigned i) const override { return m_wlits[i].second; } + void set_lit(unsigned i, literal l) override { m_wlits[i].second = l; } + unsigned get_coeff(unsigned i) const override { return m_wlits[i].first; } }; class xr : public constraint { @@ -191,13 +191,13 @@ namespace sat { literal operator[](unsigned i) const { return m_lits[i]; } literal const* begin() const { return m_lits; } literal const* end() const { return begin() + m_size; } - virtual void negate() { m_lits[0].neg(); } - virtual void swap(unsigned i, unsigned j) { std::swap(m_lits[i], m_lits[j]); } - virtual bool is_watching(literal l) const; - virtual literal_vector literals() const { return literal_vector(size(), begin()); } - virtual literal get_lit(unsigned i) const { return m_lits[i]; } - virtual void set_lit(unsigned i, literal l) { m_lits[i] = l; } - virtual bool well_formed() const; + void negate() override { m_lits[0].neg(); } + void swap(unsigned i, unsigned j) override { std::swap(m_lits[i], m_lits[j]); } + bool is_watching(literal l) const override; + literal_vector literals() const override { return literal_vector(size(), begin()); } + literal get_lit(unsigned i) const override { return m_lits[i]; } + void set_lit(unsigned i, literal l) override { m_lits[i] = l; } + bool well_formed() const override; }; @@ -484,44 +484,44 @@ namespace sat { public: ba_solver(); - virtual ~ba_solver(); - virtual void set_solver(solver* s) { m_solver = s; } - virtual void set_lookahead(lookahead* l) { m_lookahead = l; } - virtual void set_unit_walk(unit_walk* u) { m_unit_walk = u; } + ~ba_solver() override; + void set_solver(solver* s) override { m_solver = s; } + void set_lookahead(lookahead* l) override { m_lookahead = l; } + void set_unit_walk(unit_walk* u) override { m_unit_walk = u; } void add_at_least(bool_var v, literal_vector const& lits, unsigned k); void add_pb_ge(bool_var v, svector const& wlits, unsigned k); void add_xr(literal_vector const& lits); - virtual bool propagate(literal l, ext_constraint_idx idx); - virtual lbool resolve_conflict(); - virtual void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r); - virtual void asserted(literal l); - virtual check_result check(); - virtual void push(); - virtual void pop(unsigned n); - virtual void simplify(); - virtual void clauses_modifed(); - virtual lbool get_phase(bool_var v); - virtual bool set_root(literal l, literal r); - virtual void flush_roots(); - virtual std::ostream& display(std::ostream& out) const; - virtual std::ostream& display_justification(std::ostream& out, ext_justification_idx idx) const; - virtual void collect_statistics(statistics& st) const; - virtual extension* copy(solver* s); - virtual extension* copy(lookahead* s, bool learned); - virtual void find_mutexes(literal_vector& lits, vector & mutexes); - virtual void pop_reinit(); - virtual void gc(); - virtual double get_reward(literal l, ext_justification_idx idx, literal_occs_fun& occs) const; - virtual bool is_extended_binary(ext_justification_idx idx, literal_vector & r); - virtual void init_use_list(ext_use_list& ul); - virtual bool is_blocked(literal l, ext_constraint_idx idx); - virtual bool check_model(model const& m) const; + bool propagate(literal l, ext_constraint_idx idx) override; + lbool resolve_conflict() override; + void get_antecedents(literal l, ext_justification_idx idx, literal_vector & r) override; + void asserted(literal l) override; + check_result check() override; + void push() override; + void pop(unsigned n) override; + void simplify() override; + void clauses_modifed() override; + lbool get_phase(bool_var v) override; + bool set_root(literal l, literal r) override; + void flush_roots() override; + std::ostream& display(std::ostream& out) const override; + std::ostream& display_justification(std::ostream& out, ext_justification_idx idx) const override; + void collect_statistics(statistics& st) const override; + extension* copy(solver* s) override; + extension* copy(lookahead* s, bool learned) override; + void find_mutexes(literal_vector& lits, vector & mutexes) override; + void pop_reinit() override; + void gc() override; + double get_reward(literal l, ext_justification_idx idx, literal_occs_fun& occs) const override; + bool is_extended_binary(ext_justification_idx idx, literal_vector & r) override; + void init_use_list(ext_use_list& ul) override; + bool is_blocked(literal l, ext_constraint_idx idx) override; + bool check_model(model const& m) const override; ptr_vector const & constraints() const { return m_constraints; } void display(std::ostream& out, constraint const& c, bool values) const; - virtual bool validate(); + bool validate() override; }; diff --git a/src/sat/sat_lookahead.cpp b/src/sat/sat_lookahead.cpp index bbc1106bb..0da914b71 100644 --- a/src/sat/sat_lookahead.cpp +++ b/src/sat/sat_lookahead.cpp @@ -1217,7 +1217,7 @@ namespace sat { lookahead& lh; public: lookahead_literal_occs_fun(lookahead& lh): lh(lh) {} - double operator()(literal l) { return lh.literal_occs(l); } + double operator()(literal l) override { return lh.literal_occs(l); } }; // Ternary clause managagement: diff --git a/src/sat/tactic/goal2sat.h b/src/sat/tactic/goal2sat.h index d86b2d7e4..514b65311 100644 --- a/src/sat/tactic/goal2sat.h +++ b/src/sat/tactic/goal2sat.h @@ -86,7 +86,7 @@ public: public: mc(ast_manager& m); - virtual ~mc() {} + ~mc() override {} // flush model converter from SAT solver to this structure. void flush_smc(sat::solver& s, atom2bool_var const& map); void operator()(model_ref& md) override; diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index db031d043..a27f4ff83 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -147,7 +147,7 @@ class theory_lra::imp { imp& m_imp; public: resource_limit(imp& i): m_imp(i) { } - virtual bool get_cancel_flag() { return m_imp.m.canceled(); } + bool get_cancel_flag() override { return m_imp.m.canceled(); } }; diff --git a/src/smt/theory_pb.cpp b/src/smt/theory_pb.cpp index 2fb0187f8..90d1a5481 100644 --- a/src/smt/theory_pb.cpp +++ b/src/smt/theory_pb.cpp @@ -759,18 +759,18 @@ namespace smt { card& get_card() { return m_card; } - virtual void get_antecedents(conflict_resolution& cr) { + void get_antecedents(conflict_resolution& cr) override { cr.mark_literal(m_card.lit()); for (unsigned i = m_card.k(); i < m_card.size(); ++i) { cr.mark_literal(~m_card.lit(i)); } } - virtual theory_id get_from_theory() const { + theory_id get_from_theory() const override { return m_fid; } - virtual proof* mk_proof(smt::conflict_resolution& cr) { + proof* mk_proof(smt::conflict_resolution& cr) override { ptr_buffer prs; ast_manager& m = cr.get_context().get_manager(); expr_ref fact(m); diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index d902aa533..324e1f54a 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -110,7 +110,7 @@ namespace smt { public: seq_expr_solver(ast_manager& m, smt_params& fp): m_kernel(m, fp) {} - virtual lbool check_sat(expr* e) { + lbool check_sat(expr* e) override { m_kernel.push(); m_kernel.assert_expr(e); lbool r = m_kernel.check(); diff --git a/src/solver/parallel_tactic.cpp b/src/solver/parallel_tactic.cpp index f0bb65315..4ee93ff37 100644 --- a/src/solver/parallel_tactic.cpp +++ b/src/solver/parallel_tactic.cpp @@ -672,7 +672,7 @@ public: init(); } - void operator ()(const goal_ref & g,goal_ref_buffer & result) { + void operator ()(const goal_ref & g,goal_ref_buffer & result) override { fail_if_proof_generation("parallel-tactic", g); ast_manager& m = g->m(); solver* s = m_solver->translate(m, m_params); @@ -719,29 +719,29 @@ public: return pp.conquer_batch_size(); } - void cleanup() { + void cleanup() override { m_queue.reset(); } - tactic* translate(ast_manager& m) { + tactic* translate(ast_manager& m) override { solver* s = m_solver->translate(m, m_params); return alloc(parallel_tactic, s, m_params); } - virtual void updt_params(params_ref const & p) { + void updt_params(params_ref const & p) override { m_params.copy(p); parallel_params pp(p); m_conquer_delay = pp.conquer_delay(); } - virtual void collect_statistics(statistics & st) const { + void collect_statistics(statistics & st) const override { st.copy(m_stats); st.update("par unsat", m_num_unsat); st.update("par models", m_models.size()); st.update("par progress", m_progress); } - virtual void reset_statistics() { + void reset_statistics() override { m_stats.reset(); } }; diff --git a/src/tactic/core/symmetry_reduce_tactic.cpp b/src/tactic/core/symmetry_reduce_tactic.cpp index d288adbc7..784686a5e 100644 --- a/src/tactic/core/symmetry_reduce_tactic.cpp +++ b/src/tactic/core/symmetry_reduce_tactic.cpp @@ -40,7 +40,7 @@ public: void operator()(goal_ref const & g, goal_ref_buffer & result) override; - virtual void cleanup() override; + void cleanup() override; }; class ac_rewriter { diff --git a/src/tactic/dependency_converter.cpp b/src/tactic/dependency_converter.cpp index 34c864526..dcd50717b 100644 --- a/src/tactic/dependency_converter.cpp +++ b/src/tactic/dependency_converter.cpp @@ -27,15 +27,15 @@ public: unit_dependency_converter(expr_dependency_ref& d) : m_dep(d) {} - virtual expr_dependency_ref operator()() { return m_dep; } + expr_dependency_ref operator()() override { return m_dep; } - virtual dependency_converter * translate(ast_translation & translator) { + dependency_converter * translate(ast_translation & translator) override { expr_dependency_translation tr(translator); expr_dependency_ref d(tr(m_dep), translator.to()); return alloc(unit_dependency_converter, d); } - virtual void display(std::ostream& out) { + void display(std::ostream& out) override { out << m_dep.get() << "\n"; } }; @@ -47,18 +47,18 @@ public: concat_dependency_converter(dependency_converter* c1, dependency_converter* c2) : m_dc1(c1), m_dc2(c2) {} - virtual expr_dependency_ref operator()() { + expr_dependency_ref operator()() override { expr_dependency_ref d1 = (*m_dc1)(); expr_dependency_ref d2 = (*m_dc2)(); ast_manager& m = d1.get_manager(); return expr_dependency_ref(m.mk_join(d1, d2), m); } - virtual dependency_converter * translate(ast_translation & translator) { + dependency_converter * translate(ast_translation & translator) override { return alloc(concat_dependency_converter, m_dc1->translate(translator), m_dc2->translate(translator)); } - virtual void display(std::ostream& out) { + void display(std::ostream& out) override { m_dc1->display(out); m_dc2->display(out); } @@ -73,7 +73,7 @@ public: for (unsigned i = 0; i < n; ++i) m_goals.push_back(goals[i]); } - virtual expr_dependency_ref operator()() { + expr_dependency_ref operator()() override { expr_dependency_ref result(m.mk_empty_dependencies(), m); for (goal_ref g : m_goals) { dependency_converter_ref dc = g->dc(); @@ -81,13 +81,13 @@ public: } return result; } - virtual dependency_converter * translate(ast_translation & translator) { + dependency_converter * translate(ast_translation & translator) override { goal_ref_buffer goals; for (goal_ref g : m_goals) goals.push_back(g->translate(translator)); return alloc(goal_dependency_converter, goals.size(), goals.c_ptr()); } - virtual void display(std::ostream& out) { out << "goal-dep\n"; } + void display(std::ostream& out) override { out << "goal-dep\n"; } }; diff --git a/src/tactic/generic_model_converter.h b/src/tactic/generic_model_converter.h index 69bc35a3f..750a22cd4 100644 --- a/src/tactic/generic_model_converter.h +++ b/src/tactic/generic_model_converter.h @@ -41,7 +41,7 @@ class generic_model_converter : public model_converter { public: generic_model_converter(ast_manager & m, char const* orig) : m(m), m_orig(orig) {} - virtual ~generic_model_converter(); + ~generic_model_converter() override; void hide(expr* e) { SASSERT(is_app(e) && to_app(e)->get_num_args() == 0); hide(to_app(e)->get_decl()); } diff --git a/src/tactic/tactical.cpp b/src/tactic/tactical.cpp index a71ea738c..98f6be343 100644 --- a/src/tactic/tactical.cpp +++ b/src/tactic/tactical.cpp @@ -744,7 +744,7 @@ public: SASSERT(t); } - virtual ~unary_tactical() { } + ~unary_tactical() override { } void operator()(goal_ref const & in, goal_ref_buffer& result) override { m_t->operator()(in, result); @@ -1003,7 +1003,7 @@ public: SASSERT(m_p); } - virtual ~cond_tactical() {} + ~cond_tactical() override {} void operator()(goal_ref const & in, goal_ref_buffer & result) override { if (m_p->operator()(*(in.get())).is_true()) @@ -1035,7 +1035,7 @@ public: SASSERT(m_p); } - virtual ~fail_if_tactic() {} + ~fail_if_tactic() override {} void cleanup() override {} diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index 4189bad4e..8541a9b86 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -397,7 +397,7 @@ public: column_type get_column_type(unsigned j) const; - std::string get_column_name(unsigned j) const; + std::string get_column_name(unsigned j) const override; bool all_constrained_variables_are_registered(const vector>& left_side); From 7bc283b84ec9e33b6fc72ef3240ee5ee8f5e6b7c Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Tue, 2 Oct 2018 10:38:41 +0700 Subject: [PATCH 110/138] Avoid unnecessary copies in for-range loops. --- src/ast/rewriter/pb2bv_rewriter.cpp | 2 +- src/smt/theory_seq.cpp | 4 ++-- src/test/lp/argument_parser.h | 6 +++--- src/test/lp/lp.cpp | 16 ++++++++-------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/ast/rewriter/pb2bv_rewriter.cpp b/src/ast/rewriter/pb2bv_rewriter.cpp index d1def83a1..0faf08515 100644 --- a/src/ast/rewriter/pb2bv_rewriter.cpp +++ b/src/ast/rewriter/pb2bv_rewriter.cpp @@ -458,7 +458,7 @@ struct pb2bv_rewriter::imp { result = m.mk_true(); expr_ref_vector carry(m), new_carry(m); m_base.push_back(bound + rational::one()); - for (rational b_i : m_base) { + for (const rational& b_i : m_base) { unsigned B = b_i.get_unsigned(); unsigned d_i = (bound % b_i).get_unsigned(); bound = div(bound, b_i); diff --git a/src/smt/theory_seq.cpp b/src/smt/theory_seq.cpp index 233be9e31..093a47146 100644 --- a/src/smt/theory_seq.cpp +++ b/src/smt/theory_seq.cpp @@ -1375,8 +1375,8 @@ bool theory_seq::branch_variable_mb() { continue; } rational l1, l2; - for (auto elem : len1) l1 += elem; - for (auto elem : len2) l2 += elem; + for (const auto& elem : len1) l1 += elem; + for (const auto& elem : len2) l2 += elem; if (l1 != l2) { TRACE("seq", tout << "lengths are not compatible\n";); expr_ref l = mk_concat(e.ls()); diff --git a/src/test/lp/argument_parser.h b/src/test/lp/argument_parser.h index c8566ce34..ce59632d2 100644 --- a/src/test/lp/argument_parser.h +++ b/src/test/lp/argument_parser.h @@ -112,7 +112,7 @@ public: std::string usage_string() { std::string ret = ""; std::vector unknown_options; - for (auto t : m_free_args) { + for (const auto & t : m_free_args) { if (starts_with(t, "-") || starts_with(t, "\\")) { unknown_options.push_back(t); } @@ -120,7 +120,7 @@ public: if (unknown_options.size()) { ret = "Unknown options:"; } - for (auto unknownOption : unknown_options) { + for (const auto & unknownOption : unknown_options) { ret += unknownOption; ret += ","; } @@ -140,7 +140,7 @@ public: return; } std::cout << "options are: " << std::endl; - for (std::string s : m_used_options) { + for (const std::string & s : m_used_options) { std::cout << s << std::endl; } for (auto & t : m_used_options_with_after_string) { diff --git a/src/test/lp/lp.cpp b/src/test/lp/lp.cpp index ff9de0e58..ffdbb5af8 100644 --- a/src/test/lp/lp.cpp +++ b/src/test/lp/lp.cpp @@ -1161,14 +1161,14 @@ void setup_solver(unsigned max_iterations, unsigned time_limit, bool look_for_mi bool values_are_one_percent_close(double a, double b); void print_x(mps_reader & reader, lp_solver * solver) { - for (auto name : reader.column_names()) { + for (const auto & name : reader.column_names()) { std::cout << name << "=" << solver->get_column_value_by_name(name) << ' '; } std::cout << std::endl; } void compare_solutions(mps_reader & reader, lp_solver * solver, lp_solver * solver0) { - for (auto name : reader.column_names()) { + for (const auto & name : reader.column_names()) { double a = solver->get_column_value_by_name(name); double b = solver0->get_column_value_by_name(name); if (!values_are_one_percent_close(a, b)) { @@ -1299,7 +1299,7 @@ void solve_mps_in_rational(std::string file_name, bool dual, argument_parser & / std::cout << "status is " << lp_status_to_string(solver->get_status()) << std::endl; if (solver->get_status() == lp_status::OPTIMAL) { if (reader.column_names().size() < 20) { - for (auto name : reader.column_names()) { + for (const auto & name : reader.column_names()) { std::cout << name << "=" << solver->get_column_value_by_name(name).get_double() << ' '; } } @@ -1414,7 +1414,7 @@ void solve_mps_with_known_solution(std::string file_name, std::unordered_map get_solution_map(lp_solver * lps, mps_reader & reader) { std::unordered_map ret; - for (auto it : reader.column_names()) { + for (const auto & it : reader.column_names()) { ret[it] = lps->get_column_value_by_name(it); } return ret; @@ -2487,7 +2487,7 @@ void test_lar_solver(argument_parser & args_parser) { std::string file_list = args_parser.get_option_value("--filelist"); if (file_list.size() > 0) { - for (std::string fn : get_file_names_from_file_list(file_list)) + for (const std::string & fn : get_file_names_from_file_list(file_list)) test_lar_on_file(fn, args_parser); return; } @@ -3624,7 +3624,7 @@ void test_lp_local(int argn, char**argv) { } std::string file_list = args_parser.get_option_value("--filelist"); if (file_list.size() > 0) { - for (std::string fn : get_file_names_from_file_list(file_list)) + for (const std::string & fn : get_file_names_from_file_list(file_list)) solve_mps(fn, args_parser); return finalize(0); } From 1067a5363f74c55f26f4146d993e6b923fdc478d Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Tue, 2 Oct 2018 08:34:57 +0700 Subject: [PATCH 111/138] theory_lra: Remove unused variable. --- src/smt/theory_lra.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index d3475bdc1..171c4de0a 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1569,7 +1569,6 @@ public: VERIFY(a.is_idiv(n, p, q)); theory_var v = mk_var(n); theory_var v1 = mk_var(p); - theory_var v2 = mk_var(q); rational r1 = get_value(v1); rational r2; From 6d2936e5fc20a26cac2ed37d39dd94d7d89f86f2 Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Tue, 2 Oct 2018 08:35:12 +0700 Subject: [PATCH 112/138] watch_list: Fix indentation. --- src/smt/watch_list.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/smt/watch_list.cpp b/src/smt/watch_list.cpp index 9835142f6..778e93021 100644 --- a/src/smt/watch_list.cpp +++ b/src/smt/watch_list.cpp @@ -36,10 +36,10 @@ namespace smt { void watch_list::expand() { if (m_data == nullptr) { - unsigned size = DEFAULT_WATCH_LIST_SIZE + HEADER_SIZE; + unsigned size = DEFAULT_WATCH_LIST_SIZE + HEADER_SIZE; unsigned * mem = reinterpret_cast(alloc_svect(char, size)); #ifdef _AMD64_ - ++mem; // make sure data is aligned in 64 bit machines + ++mem; // make sure data is aligned in 64 bit machines #endif *mem = 0; ++mem; @@ -62,9 +62,9 @@ namespace smt { unsigned * mem = reinterpret_cast(alloc_svect(char, new_capacity + HEADER_SIZE)); unsigned curr_end_cls = end_cls_core(); #ifdef _AMD64_ - ++mem; // make sure data is aligned in 64 bit machines + ++mem; // make sure data is aligned in 64 bit machines #endif - *mem = curr_end_cls; + *mem = curr_end_cls; ++mem; SASSERT(bin_bytes <= new_capacity); unsigned new_begin_bin = new_capacity - bin_bytes; From a76397d3b8174583db90c75f84ab60f8fa0c831f Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Tue, 2 Oct 2018 17:38:09 +0700 Subject: [PATCH 113/138] Refer to macOS rather than Mac OS / OSX. --- CMakeLists.txt | 2 +- README.md | 6 +++--- examples/c++/README | 4 ++-- examples/c/README | 4 ++-- examples/java/README | 2 +- examples/maxsat/README | 4 ++-- examples/ml/README | 2 +- examples/python/example.py | 2 +- examples/tptp/README | 4 ++-- src/api/python/README.txt | 4 ++-- src/api/python/setup.py | 2 +- src/cmd_context/pdecl.h | 2 +- src/math/polynomial/polynomial.cpp | 2 +- src/shell/main.cpp | 2 +- src/util/hwf.cpp | 12 ++++++------ src/util/scoped_timer.cpp | 10 +++++----- src/util/stopwatch.h | 2 +- 17 files changed, 33 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 28cd9fa33..500b67100 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -240,7 +240,7 @@ if ("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") list(APPEND Z3_COMPONENT_CXX_DEFINES "-D_USE_THREAD_LOCAL") endif() elseif ("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") - # Does OSX really not need any special flags? + # Does macOS really not need any special flags? message(STATUS "Platform: Darwin") elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "FreeBSD") message(STATUS "Platform: FreeBSD") diff --git a/README.md b/README.md index 447034a84..e02719161 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ See the [release notes](RELEASE_NOTES) for notes on various stable releases of Z ## Build status -| Windows x64 | Windows x86 | Windows x64 | Ubuntu x64 | Debian x64 | OSX | TravisCI | -| ----------- | ----------- | ----------- | ---------- | ---------- | --- | -------- | +| Windows x64 | Windows x86 | Windows x64 | Ubuntu x64 | Debian x64 | macOS | TravisCI | +| ----------- | ----------- | ----------- | ---------- | ---------- | ----- | -------- | [![win64-badge](https://z3build.visualstudio.com/_apis/public/build/definitions/2e0aa542-a22c-4b1a-8dcd-3ebae8e12db4/4/badge)](https://z3build.visualstudio.com/Z3Build/_build/index?definitionId=4) | [![win32-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/4/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=4) | [![win64-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/7/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=7) | [![ubuntu-x64-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/3/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=3) | [![debian-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/5/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=5) | [![osx-badge](https://cz3.visualstudio.com/_apis/public/build/definitions/bf14bcc7-ebd4-4240-812c-5972fa59e0ad/2/badge)](https://cz3.visualstudio.com/Z3/_build/index?definitionId=2) | [![Build Status](https://travis-ci.org/Z3Prover/z3.svg?branch=master)](https://travis-ci.org/Z3Prover/z3) [1]: #building-z3-on-windows-using-visual-studio-command-prompt @@ -75,7 +75,7 @@ A 32 bit build should work similarly (but is untested); the same is true for 32/ By default, it will install z3 executable at ``PREFIX/bin``, libraries at ``PREFIX/lib``, and include files at ``PREFIX/include``, where ``PREFIX`` installation prefix if inferred by the ``mk_make.py`` script. It is usually -``/usr`` for most Linux distros, and ``/usr/local`` for FreeBSD and OSX. Use +``/usr`` for most Linux distros, and ``/usr/local`` for FreeBSD and macOS. Use the ``--prefix=`` command line option to change the install prefix. For example: ```bash diff --git a/examples/c++/README b/examples/c++/README index 56775e537..2b3c5affc 100644 --- a/examples/c++/README +++ b/examples/c++/README @@ -5,6 +5,6 @@ in the build directory. This command will create the executable cpp_example. On Windows, you can just execute it. -On OSX and Linux, you must install z3 first using +On macOS and Linux, you must install z3 first using sudo make install -OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (OSX) with the build directory. You need that to be able to find the Z3 shared library. \ No newline at end of file +OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS) with the build directory. You need that to be able to find the Z3 shared library. diff --git a/examples/c/README b/examples/c/README index 4ca71e0f8..af9dd39f6 100644 --- a/examples/c/README +++ b/examples/c/README @@ -5,7 +5,7 @@ in the build directory. This command will create the executable c_example. On Windows, you can just execute it. -On OSX and Linux, you must install z3 first using +On macOS and Linux, you must install z3 first using sudo make install -OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (OSX) with the build directory. You need that to be able to find the Z3 shared library. +OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS) with the build directory. You need that to be able to find the Z3 shared library. diff --git a/examples/java/README b/examples/java/README index 1939afc49..d3ff93fe0 100644 --- a/examples/java/README +++ b/examples/java/README @@ -10,5 +10,5 @@ which can be run on Windows via On Linux and FreeBSD, we must use LD_LIBRARY_PATH=. java -cp com.microsoft.z3.jar:. JavaExample -On OSX, the corresponding option is DYLD_LIBRARY_PATH: +On macOS, the corresponding option is DYLD_LIBRARY_PATH: DYLD_LIBRARY_PATH=. java -cp com.microsoft.z3.jar:. JavaExample diff --git a/examples/maxsat/README b/examples/maxsat/README index 6c24da66b..8c7d3b0f7 100644 --- a/examples/maxsat/README +++ b/examples/maxsat/README @@ -5,8 +5,8 @@ in the build directory. This command will create the executable maxsat. On Windows, you can just execute it. -On OSX and Linux, you must install z3 first using +On macOS and Linux, you must install z3 first using sudo make install -OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (OSX) with the build directory. You need that to be able to find the Z3 shared library. +OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS) with the build directory. You need that to be able to find the Z3 shared library. This directory contains a test file (ex.smt) that can be used as input for the maxsat test application. diff --git a/examples/ml/README b/examples/ml/README index 1c474fe33..9797b85e3 100644 --- a/examples/ml/README +++ b/examples/ml/README @@ -20,4 +20,4 @@ ocamlfind ocamlopt -o ml_example -package Z3 -linkpkg ml_example.ml Note that the resulting binaries depend on the shared z3 library (libz3.dll/.so/.dylb), which needs to be in the PATH (Windows), LD_LIBRARY_PATH -(Linux), or DYLD_LIBRARY_PATH (OSX). +(Linux), or DYLD_LIBRARY_PATH (macOS). diff --git a/examples/python/example.py b/examples/python/example.py index a17668506..761ae10be 100644 --- a/examples/python/example.py +++ b/examples/python/example.py @@ -20,7 +20,7 @@ # export PYTHONPATH=MYZ3/bin/python # python example.py -# Running this example on OSX: +# Running this example on macOS: # export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:MYZ3/bin # export PYTHONPATH=MYZ3/bin/python # python example.py diff --git a/examples/tptp/README b/examples/tptp/README index c28a53da4..b3edfe6a8 100644 --- a/examples/tptp/README +++ b/examples/tptp/README @@ -5,9 +5,9 @@ in the build directory. This command will create the executable tptp. On Windows, you can just execute it. -On OSX and Linux, you must install z3 first using +On macOS and Linux, you must install z3 first using sudo make install -OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (OSX) +OR update LD_LIBRARY_PATH (Linux) or DYLD_LIBRARY_PATH (macOS) with the build directory. You need that to be able to find the Z3 shared library. diff --git a/src/api/python/README.txt b/src/api/python/README.txt index 9831c6fc6..9312b1119 100644 --- a/src/api/python/README.txt +++ b/src/api/python/README.txt @@ -12,8 +12,8 @@ If you are using a 64-bit Python interpreter, you should use msbuild /p:configuration=external /p:platform=x64 -On Linux and OSX, you must install Z3Py, before trying example.py. -To install Z3Py on Linux and OSX, you should execute the following +On Linux and macOS, you must install Z3Py, before trying example.py. +To install Z3Py on Linux and macOS, you should execute the following command in the Z3 root directory sudo make install-z3py diff --git a/src/api/python/setup.py b/src/api/python/setup.py index bce681584..2a750fee6 100644 --- a/src/api/python/setup.py +++ b/src/api/python/setup.py @@ -73,7 +73,7 @@ def _build_z3(): if subprocess.call(['nmake'], env=build_env, cwd=BUILD_DIR) != 0: raise LibError("Unable to build Z3.") - else: # linux and osx + else: # linux and macOS if subprocess.call(['make', '-j', str(multiprocessing.cpu_count())], env=build_env, cwd=BUILD_DIR) != 0: raise LibError("Unable to build Z3.") diff --git a/src/cmd_context/pdecl.h b/src/cmd_context/pdecl.h index 12e6399fe..d994a1e8f 100644 --- a/src/cmd_context/pdecl.h +++ b/src/cmd_context/pdecl.h @@ -53,7 +53,7 @@ public: class psort_inst_cache; #if defined(__APPLE__) && defined(__MACH__) -// CMW: for some unknown reason, llvm on OSX does not like the name `psort' +// CMW: for some unknown reason, llvm on macOS does not like the name `psort' #define psort Z3_psort #endif diff --git a/src/math/polynomial/polynomial.cpp b/src/math/polynomial/polynomial.cpp index 35a95ce82..24658fdcf 100644 --- a/src/math/polynomial/polynomial.cpp +++ b/src/math/polynomial/polynomial.cpp @@ -100,7 +100,7 @@ namespace polynomial { struct lt_var { bool operator()(power const & p1, power const & p2) { - // CMW: The assertion below does not hold on OSX, because + // CMW: The assertion below does not hold on macOS, because // their implementation of std::sort will try to compare // two items at the same index instead of comparing // the indices directly. I suspect that the purpose of diff --git a/src/shell/main.cpp b/src/shell/main.cpp index 1c8b6908d..3b97d4462 100644 --- a/src/shell/main.cpp +++ b/src/shell/main.cpp @@ -91,7 +91,7 @@ void display_usage() { std::cout << " -pp:name display Z3 parameter description, if 'name' is not provided, then all module names are listed.\n"; std::cout << " --" << " all remaining arguments are assumed to be part of the input file name. This option allows Z3 to read files with strange names such as: -foo.smt2.\n"; std::cout << "\nResources:\n"; - // timeout and memout are now available on Linux and OSX too. + // timeout and memout are now available on Linux and macOS too. std::cout << " -T:timeout set the timeout (in seconds).\n"; std::cout << " -t:timeout set the soft timeout (in milli seconds). It only kills the current query.\n"; std::cout << " -memory:Megabytes set a limit for virtual memory consumption.\n"; diff --git a/src/util/hwf.cpp b/src/util/hwf.cpp index b8f481329..32b1343e3 100644 --- a/src/util/hwf.cpp +++ b/src/util/hwf.cpp @@ -45,7 +45,7 @@ Revision History: // 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. -// Luckily, these are kind of standardized, at least for Windows/Linux/OSX. +// Luckily, these are kind of standardized, at least for Windows/Linux/macOS. #ifdef __clang__ #undef USE_INTRINSICS #endif @@ -75,14 +75,14 @@ hwf_manager::hwf_manager() : #endif #endif #else - // OSX/Linux: Nothing. + // macOS/Linux: Nothing. #endif // 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). - // I have yet to discover whether Linux and OSX save the FPU state when switching context. + // I have yet to discover whether Linux and macOS 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). } @@ -279,7 +279,7 @@ void hwf_manager::fma(mpf_rounding_mode rm, hwf const & x, hwf const & y, hwf co #endif #endif #else - // Linux, OSX + // Linux, macOS o.value = ::fma(x.value, y.value, z.value); #endif #endif @@ -331,7 +331,7 @@ void hwf_manager::round_to_integral(mpf_rounding_mode rm, hwf const & x, hwf & o } #endif #else - // Linux, OSX. + // Linux, macOS. o.value = nearbyint(x.value); #endif } @@ -623,7 +623,7 @@ void hwf_manager::set_rounding_mode(mpf_rounding_mode rm) UNREACHABLE(); // Note: MPF_ROUND_NEAREST_TAWAY is not supported by the hardware! } #endif -#else // OSX/Linux +#else // macOS/Linux switch (rm) { case MPF_ROUND_NEAREST_TEVEN: SETRM(FE_TONEAREST); diff --git a/src/util/scoped_timer.cpp b/src/util/scoped_timer.cpp index 8078c46ee..9850699be 100644 --- a/src/util/scoped_timer.cpp +++ b/src/util/scoped_timer.cpp @@ -27,7 +27,7 @@ Revision History: // Windows #include #elif defined(__APPLE__) && defined(__MACH__) -// Mac OS X +// macOS #include #include #include @@ -59,7 +59,7 @@ struct scoped_timer::imp { HANDLE m_timer; bool m_first; #elif defined(__APPLE__) && defined(__MACH__) - // Mac OS X + // macOS pthread_t m_thread_id; pthread_attr_t m_attributes; unsigned m_interval; @@ -89,7 +89,7 @@ struct scoped_timer::imp { } } #elif defined(__APPLE__) && defined(__MACH__) - // Mac OS X + // macOS static void * thread_func(void * arg) { scoped_timer::imp * st = static_cast(arg); @@ -153,7 +153,7 @@ struct scoped_timer::imp { ms, WT_EXECUTEINTIMERTHREAD); #elif defined(__APPLE__) && defined(__MACH__) - // Mac OS X + // macOS m_interval = ms?ms:0xFFFFFFFF; if (pthread_attr_init(&m_attributes) != 0) throw default_exception("failed to initialize timer thread attributes"); @@ -194,7 +194,7 @@ struct scoped_timer::imp { m_timer, INVALID_HANDLE_VALUE); #elif defined(__APPLE__) && defined(__MACH__) - // Mac OS X + // macOS // If the waiting-thread is not up and waiting yet, // we can make sure that it finishes quickly by diff --git a/src/util/stopwatch.h b/src/util/stopwatch.h index c606824b4..2c7551df0 100644 --- a/src/util/stopwatch.h +++ b/src/util/stopwatch.h @@ -70,7 +70,7 @@ public: #undef min -#elif defined(__APPLE__) && defined (__MACH__) // Mac OS X +#elif defined(__APPLE__) && defined (__MACH__) // macOS #include #include From a376a8d3432be226127af80c71877a13246412fd Mon Sep 17 00:00:00 2001 From: Kirill Bobyrev Date: Tue, 2 Oct 2018 16:14:01 +0300 Subject: [PATCH 114/138] [NFC] Cleanup arith_eq_solver.(cpp|h) Use for-range loops instead of for-index loops where possible, remove trailing whitespaces. This patch does not affect functionality. --- src/smt/arith_eq_solver.cpp | 135 ++++++++++++++++++------------------ src/smt/arith_eq_solver.h | 24 +++---- 2 files changed, 78 insertions(+), 81 deletions(-) diff --git a/src/smt/arith_eq_solver.cpp b/src/smt/arith_eq_solver.cpp index 128b35dd1..883255b8a 100644 --- a/src/smt/arith_eq_solver.cpp +++ b/src/smt/arith_eq_solver.cpp @@ -12,14 +12,11 @@ Abstract: Author: Nikolaj Bjorner (nbjorner) 2012-02-25 - + --*/ #include "smt/arith_eq_solver.h" -arith_eq_solver::~arith_eq_solver() { -} - arith_eq_solver::arith_eq_solver(ast_manager & m, params_ref const& p): m(m), m_params(p), @@ -93,9 +90,9 @@ void arith_eq_solver::gcd_normalize(vector& values) { if (g.is_zero() || g.is_one()) { return; } - for (unsigned i = 0; i < values.size(); ++i) { - values[i] = values[i] / g; - SASSERT(values[i].is_int()); + for (auto &value : values) { + value /= g; + SASSERT(value.is_int()); } } @@ -116,9 +113,9 @@ unsigned arith_eq_solver::find_abs_min(vector& values) { #ifdef _TRACE static void print_row(std::ostream& out, vector const& row) { - for(unsigned i = 0; i < row.size(); ++i) { - out << row[i] << " "; - } + for(unsigned i = 0; i < row.size(); ++i) { + out << row[i] << " "; + } out << "\n"; } @@ -165,7 +162,7 @@ bool arith_eq_solver::solve_integer_equation( bool& is_fresh ) { - TRACE("arith_eq_solver", + TRACE("arith_eq_solver", tout << "solving: "; print_row(tout, values); ); @@ -174,31 +171,31 @@ bool arith_eq_solver::solve_integer_equation( // // Given: // a1*x1 + a2*x2 + .. + a_n*x_n + a_{n+1} = 0 - // + // // Assume gcd(a1,..,a_n,a_{n+1}) = 1 // Assume gcd(a1,...,a_n) divides a_{n+1} (eg. gcd(a1,..,an) = 1) - // + // // post-condition: values[index] = -1. - // + // // Let a_index be index of least absolute value. // // If |a_index| = 1, then return row and index. // Otherwise: // Let m = |a_index| + 1 // Set - // - // m*x_index' - // = + // + // m*x_index' + // = // ((a1 mod_hat m)*x1 + (a2 mod_hat m)*x2 + .. + (a_n mod_hat m)*x_n + (k mod_hat m)) - // = + // = // (a1'*x1 + a2'*x2 + .. (-)1*x_index + ...) - // + // // <=> Normalize signs so that sign to x_index is -1. // (-)a1'*x1 + (-)a2'*x2 + .. -1*x_index + ... + m*x_index' = 0 - // + // // Return row, where the coefficient to x_index is implicit. // Instead used the coefficient 'm' at position 'index'. - // + // gcd_normalize(values); if (!gcd_test(values)) { @@ -216,8 +213,8 @@ bool arith_eq_solver::solve_integer_equation( return true; } if (a.is_one()) { - for (unsigned i = 0; i < values.size(); ++i) { - values[i].neg(); + for (auto &value : values) { + value.neg(); } } is_fresh = !abs_a.is_one(); @@ -225,19 +222,19 @@ bool arith_eq_solver::solve_integer_equation( if (is_fresh) { numeral m = abs_a + numeral(1); - for (unsigned i = 0; i < values.size(); ++i) { - values[i] = mod_hat(values[i], m); + for (auto &value : values) { + value = mod_hat(value, m); } if (values[index].is_one()) { - for (unsigned i = 0; i < values.size(); ++i) { - values[i].neg(); + for (auto &value : values) { + value.neg(); } } SASSERT(values[index].is_minus_one()); values[index] = m; } - TRACE("arith_eq_solver", + TRACE("arith_eq_solver", tout << "solved at index " << index << ": "; print_row(tout, values); ); @@ -253,7 +250,7 @@ void arith_eq_solver::substitute( ) { SASSERT(1 <= index && index < s.size()); - TRACE("arith_eq_solver", + TRACE("arith_eq_solver", tout << "substitute " << index << ":\n"; print_row(tout, r); print_row(tout, s); @@ -272,21 +269,21 @@ void arith_eq_solver::substitute( // s encodes an equation that contains a variable // with a unit coefficient. // - // Let + // Let // c = r[index] // s = s[index]*x + s'*y = 0 // r = c*x + r'*y = 0 - // - // => + // + // => // // 0 - // = - // -sign(s[index])*c*s + r - // = + // = + // -sign(s[index])*c*s + r + // = // -s[index]*sign(s[index])*c*x - sign(s[index])*c*s'*y + c*x + r'*y - // = + // = // -c*x - sign(s[index])*c*s'*y + c*x + r'*y - // = + // = // -sign(s[index])*c*s'*y + r'*y // numeral sign_s = s[index].is_pos()?numeral(1):numeral(-1); @@ -301,36 +298,36 @@ void arith_eq_solver::substitute( // // s encodes a substitution using an auxiliary variable. // the auxiliary variable is at position 'index'. - // - // Let + // + // Let // c = r[index] // s = s[index]*x + s'*y = 0 // r = c*x + r'*y = 0 // // s encodes : x |-> s[index]*x' + s'*y // - // Set: + // Set: // // r := c*s + r'*y - // + // r[index] = numeral(0); for (unsigned i = 0; i < r.size(); ++i) { r[i] += c*s[i]; - } + } for (unsigned i = r.size(); i < s.size(); ++i) { r.push_back(c*s[i]); } - } + } - TRACE("arith_eq_solver", + TRACE("arith_eq_solver", tout << "result: "; print_row(tout, r); ); } bool arith_eq_solver::solve_integer_equations( - vector& rows, + vector& rows, row& unsat_row ) { @@ -340,10 +337,10 @@ bool arith_eq_solver::solve_integer_equations( // // Naive integer equation solver where only units are eliminated. -// +// bool arith_eq_solver::solve_integer_equations_units( - vector& rows, + vector& rows, row& unsat_row ) { @@ -351,7 +348,7 @@ bool arith_eq_solver::solve_integer_equations_units( TRACE("arith_eq_solver", print_rows(tout << "solving:\n", rows);); unsigned_vector todo, done; - + for (unsigned i = 0; i < rows.size(); ++i) { todo.push_back(i); row& r = rows[i]; @@ -360,9 +357,9 @@ bool arith_eq_solver::solve_integer_equations_units( unsat_row = r; TRACE("arith_eq_solver", print_row(tout << "input is unsat: ", unsat_row); ); return false; - } + } } - for (unsigned i = 0; i < todo.size(); ++i) { + for (unsigned i = 0; i < todo.size(); ++i) { row& r = rows[todo[i]]; gcd_normalize(r); if (!gcd_test(r)) { @@ -388,7 +385,7 @@ bool arith_eq_solver::solve_integer_equations_units( todo.push_back(done[j]); done.erase(done.begin()+j); --j; - } + } } } else { @@ -396,7 +393,7 @@ bool arith_eq_solver::solve_integer_equations_units( } } - TRACE("arith_eq_solver", + TRACE("arith_eq_solver", tout << ((done.size()<=1)?"solved ":"incomplete check ") << done.size() << "\n"; for (unsigned i = 0; i < done.size(); ++i) { print_row(tout, rows[done[i]]); @@ -411,12 +408,12 @@ bool arith_eq_solver::solve_integer_equations_units( // // Partial solver based on the omega test equalities. -// unsatisfiability is not preserved when eliminating +// unsatisfiability is not preserved when eliminating // auxiliary variables. // bool arith_eq_solver::solve_integer_equations_omega( - vector & rows, + vector & rows, row& unsat_row ) { @@ -460,16 +457,16 @@ bool arith_eq_solver::solve_integer_equations_omega( // // solved_row: -x_index + m*sigma + r1 = 0 // unsat_row: k*sigma + r2 = 0 - // - // <=> - // + // + // <=> + // // solved_row: -k*x_index + k*m*sigma + k*r1 = 0 // unsat_row: m*k*sigma + m*r2 = 0 // // => // // m*k*sigma + m*r2 + k*x_index - k*m*sigma - k*r1 = 0 - // + // for (unsigned l = 0; l < unsat_row.size(); ++l) { unsat_row[l] *= m; unsat_row[l] -= k*solved_row[l]; @@ -479,7 +476,7 @@ bool arith_eq_solver::solve_integer_equations_omega( } gcd_normalize(unsat_row); - TRACE("arith_eq_solver", + TRACE("arith_eq_solver", tout << "gcd: "; print_row(tout, solved_row); print_row(tout, unsat_row); @@ -525,18 +522,18 @@ bool arith_eq_solver::solve_integer_equations_omega( // // Eliminate variables by searching for combination of rows where -// the coefficients have gcd = 1. -// +// the coefficients have gcd = 1. +// bool arith_eq_solver::solve_integer_equations_gcd( - vector & rows, + vector & rows, row& unsat_row ) -{ +{ unsigned_vector live, useful, gcd_pos; vector gcds; rational u, v; - + if (rows.empty()) { return true; } @@ -548,7 +545,7 @@ bool arith_eq_solver::solve_integer_equations_gcd( unsat_row = r; TRACE("arith_eq_solver", print_row(tout << "input is unsat: ", unsat_row); ); return false; - } + } } unsigned max_column = rows[0].size(); bool change = true; @@ -579,7 +576,7 @@ bool arith_eq_solver::solve_integer_equations_gcd( if (j == live.size()) { continue; } - + change = true; // found gcd, now identify reduced set of rows with GCD = 1. g = abs(rows[live[j]][i]); @@ -592,7 +589,7 @@ bool arith_eq_solver::solve_integer_equations_gcd( useful.push_back(gcd_pos[j]); g = gcd(g, gcds[j]); SASSERT(j == 0 || gcd(g,gcds[j-1]).is_one()); - } + } } // // we now have a set "useful" of rows whose combined GCD = 1. @@ -600,7 +597,7 @@ bool arith_eq_solver::solve_integer_equations_gcd( // row& r0 = rows[useful[0]]; for (j = 1; j < useful.size(); ++j) { - row& r1 = rows[useful[j]]; + row& r1 = rows[useful[j]]; g = gcd(r0[i], r1[i], u, v); for (unsigned k = 0; k < max_column; ++k) { r0[k] = u*r0[k] + v*r1[k]; @@ -626,7 +623,7 @@ bool arith_eq_solver::solve_integer_equations_gcd( } } - TRACE("arith_eq_solver", + TRACE("arith_eq_solver", tout << ((live.size()<=1)?"solved ":"incomplete check ") << live.size() << "\n"; for (unsigned i = 0; i < live.size(); ++i) { print_row(tout, rows[live[i]]); diff --git a/src/smt/arith_eq_solver.h b/src/smt/arith_eq_solver.h index b2db35ee1..68e58334d 100644 --- a/src/smt/arith_eq_solver.h +++ b/src/smt/arith_eq_solver.h @@ -12,7 +12,7 @@ Abstract: Author: Nikolaj Bjorner (nbjorner) 2012-02-25 - + --*/ #ifndef ARITH_EQ_SOLVER_H_ #define ARITH_EQ_SOLVER_H_ @@ -35,45 +35,45 @@ class arith_eq_solver { void prop_mod_const(expr * e, unsigned depth, numeral const& k, expr_ref& result); - bool gcd_test(vector& value); + bool gcd_test(vector& values); unsigned find_abs_min(vector& values); void gcd_normalize(vector& values); void substitute(vector& r, vector const& s, unsigned index); bool solve_integer_equations_units( - vector > & rows, + vector > & rows, vector& unsat_row ); - + bool solve_integer_equations_omega( - vector > & rows, + vector > & rows, vector& unsat_row ); void compute_hnf(vector >& A); bool solve_integer_equations_hermite( - vector > & rows, + vector > & rows, vector& unsat_row ); bool solve_integer_equations_gcd( - vector > & rows, + vector > & rows, vector& unsat_row ); public: arith_eq_solver(ast_manager & m, params_ref const& p = params_ref()); - ~arith_eq_solver(); + ~arith_eq_solver() = default; // Integer linear solver for a single equation. // The array values contains integer coefficients - // + // // Determine integer solutions to: // // a+k = 0 // // where a = sum_i a_i*k_i - // + // typedef vector row; typedef vector matrix; @@ -90,14 +90,14 @@ public: // a+k = 0 // // where a = sum_i a_i*k_i - // + // // Solution, if there is any, is returned as a substitution. // The return value is "true". // If there is no solution, then return "false". // together with equality "eq_unsat", such that // // eq_unsat = 0 - // + // // is implied and is unsatisfiable over the integers. // From 5bf57c270071a29835a8dcafa93f411a91ad56f7 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 2 Oct 2018 08:14:19 -0700 Subject: [PATCH 115/138] fix cubing semantics Signed-off-by: Nikolaj Bjorner --- src/sat/sat_solver/inc_sat_solver.cpp | 17 ++++++++--------- src/solver/tactic2solver.cpp | 1 + 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sat/sat_solver/inc_sat_solver.cpp b/src/sat/sat_solver/inc_sat_solver.cpp index 7a77e3b4e..df2da82e7 100644 --- a/src/sat/sat_solver/inc_sat_solver.cpp +++ b/src/sat/sat_solver/inc_sat_solver.cpp @@ -308,9 +308,9 @@ public: return nullptr; } - expr_ref_vector last_cube() { + expr_ref_vector last_cube(bool is_sat) { expr_ref_vector result(m); - result.push_back(m.mk_false()); + result.push_back(is_sat ? m.mk_true() : m.mk_false()); return result; } @@ -334,16 +334,16 @@ public: lbool result = m_solver.cube(vars, lits, backtrack_level); switch (result) { case l_true: - return expr_ref_vector(m); + return last_cube(true); case l_false: - return last_cube(); + return last_cube(false); default: - SASSERT(!lits.empty()); - if (lits.empty()) { - IF_VERBOSE(0, verbose_stream() << "empty cube for undef\n";); - } break; } + if (lits.empty()) { + set_reason_unknown(m_solver.get_reason_unknown()); + return expr_ref_vector(m); + } expr_ref_vector fmls(m); expr_ref_vector lit2expr(m); lit2expr.resize(m_solver.num_vars() * 2); @@ -358,7 +358,6 @@ public: vs.push_back(x); } } - if (fmls.empty()) { IF_VERBOSE(0, verbose_stream() << "no literals were produced in cube\n"); } return fmls; } diff --git a/src/solver/tactic2solver.cpp b/src/solver/tactic2solver.cpp index cf0c6f9bc..3a905bafd 100644 --- a/src/solver/tactic2solver.cpp +++ b/src/solver/tactic2solver.cpp @@ -79,6 +79,7 @@ public: expr_ref_vector cube(expr_ref_vector& vars, unsigned ) override { + set_reason_unknown("cubing is not supported on tactics"); return expr_ref_vector(get_manager()); } From 39fbf1e1742dfa88c63a0b236931af936277ad7f Mon Sep 17 00:00:00 2001 From: Murphy Berzish Date: Tue, 2 Oct 2018 12:28:53 -0400 Subject: [PATCH 116/138] Z3str3: don't use arith_value::get_value in get_arith_value --- src/smt/theory_str.cpp | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/smt/theory_str.cpp b/src/smt/theory_str.cpp index 662d76ae3..90bf171a0 100644 --- a/src/smt/theory_str.cpp +++ b/src/smt/theory_str.cpp @@ -4836,9 +4836,24 @@ namespace smt { } bool theory_str::get_arith_value(expr* e, rational& val) const { - arith_value v(get_context()); - return v.get_value(e, val); - } + context& ctx = get_context(); + ast_manager & m = get_manager(); + if (!ctx.e_internalized(e)) { + return false; + } + // check root of the eqc for an integer constant + // if an integer constant exists in the eqc, it should be the root + enode * en_e = ctx.get_enode(e); + enode * root_e = en_e->get_root(); + if (m_autil.is_numeral(root_e->get_owner(), val) && val.is_int()) { + TRACE("str", tout << mk_pp(e, m) << " ~= " << mk_pp(root_e->get_owner(), m) << std::endl;); + return true; + } else { + TRACE("str", tout << "root of eqc of " << mk_pp(e, m) << " is not a numeral" << std::endl;); + return false; + } + + } bool theory_str::lower_bound(expr* _e, rational& lo) { if (opt_DisableIntegerTheoryIntegration) { From fd9fd522713bce973b8978375d0f55adac8bf0e9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Tue, 2 Oct 2018 17:13:46 -0700 Subject: [PATCH 117/138] fixing #1847 Signed-off-by: Nikolaj Bjorner --- src/smt/theory_array_full.cpp | 60 ++++++++++++++++++++--------------- src/smt/theory_array_full.h | 6 ++++ 2 files changed, 41 insertions(+), 25 deletions(-) diff --git a/src/smt/theory_array_full.cpp b/src/smt/theory_array_full.cpp index 03cd3e1b2..d0a37175d 100644 --- a/src/smt/theory_array_full.cpp +++ b/src/smt/theory_array_full.cpp @@ -339,19 +339,14 @@ namespace smt { SASSERT(v != null_theory_var); v = find(v); var_data* d = m_var_data[v]; - TRACE("array", tout << "v" << v << "\n";); + TRACE("array", tout << "v" << v << " " << d->m_prop_upward << " " << m_params.m_array_delay_exp_axiom << "\n";); for (enode * store : d->m_stores) { SASSERT(is_store(store)); instantiate_default_store_axiom(store); } if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) { - for (enode * store : d->m_parent_stores) { - SASSERT(is_store(store)); - if (!m_params.m_array_cg || store->is_cgr()) { - instantiate_default_store_axiom(store); - } - } + instantiate_parent_stores_default(v); } } @@ -382,23 +377,14 @@ namespace smt { } void theory_array_full::relevant_eh(app* n) { - TRACE("array", tout << mk_pp(n, get_manager()) << "\n";); + TRACE("array", tout << mk_pp(n, get_manager()) << "\n";); theory_array::relevant_eh(n); - if (!is_default(n) && !is_select(n) && !is_map(n) && !is_const(n) && !is_as_array(n) && !is_store(n)) { + if (!is_default(n) && !is_select(n) && !is_map(n) && !is_const(n) && !is_as_array(n)){ return; } context & ctx = get_context(); enode* node = ctx.get_enode(n); - if (is_store(n)) { - enode * arg = ctx.get_enode(n->get_arg(0)); - if (is_const(arg)) { - TRACE("array", tout << expr_ref(arg->get_owner(), get_manager()) << " " << is_const(arg) << "\n";); - theory_var v = arg->get_th_var(get_id()); - set_prop_upward(v); - add_parent_default(v); - } - } - else if (is_select(n)) { + if (is_select(n)) { enode * arg = ctx.get_enode(n->get_arg(0)); theory_var v = arg->get_th_var(get_id()); SASSERT(v != null_theory_var); @@ -408,14 +394,18 @@ namespace smt { enode * arg = ctx.get_enode(n->get_arg(0)); theory_var v = arg->get_th_var(get_id()); SASSERT(v != null_theory_var); + set_prop_upward(v); add_parent_default(find(v)); } else if (is_const(n)) { instantiate_default_const_axiom(node); + theory_var v = node->get_th_var(get_id()); + set_prop_upward(v); + add_parent_default(find(v)); } else if (is_map(n)) { - for (unsigned i = 0; i < n->get_num_args(); ++i) { - enode* arg = ctx.get_enode(n->get_arg(i)); + for (expr * e : *n) { + enode* arg = ctx.get_enode(e); theory_var v_arg = find(arg->get_th_var(get_id())); add_parent_map(v_arg, node); set_prop_upward(v_arg); @@ -505,7 +495,7 @@ namespace smt { app* map = mp->get_owner(); ast_manager& m = get_manager(); context& ctx = get_context(); - if (!ctx.add_fingerprint(this, 0, 1, &mp)) { + if (!ctx.add_fingerprint(this, m_default_map_fingerprint, 1, &mp)) { return false; } TRACE("array", tout << mk_bounded_pp(map, get_manager()) << "\n";); @@ -530,7 +520,7 @@ namespace smt { bool theory_array_full::instantiate_default_const_axiom(enode* cnst) { context& ctx = get_context(); - if (!ctx.add_fingerprint(this, 0, 1, &cnst)) { + if (!ctx.add_fingerprint(this, m_default_const_fingerprint, 1, &cnst)) { return false; } m_stats.m_num_default_const_axiom++; @@ -551,7 +541,7 @@ namespace smt { return false; #if 0 context& ctx = get_context(); - if (!ctx.add_fingerprint(this, 0, 1, &arr)) { + if (!ctx.add_fingerprint(this, m_default_as_array_fingerprint, 1, &arr)) { return false; } m_stats.m_num_default_as_array_axiom++; @@ -668,7 +658,7 @@ namespace smt { app* store_app = store->get_owner(); context& ctx = get_context(); ast_manager& m = get_manager(); - if (!ctx.add_fingerprint(this, 0, 1, &store)) { + if (!ctx.add_fingerprint(this, m_default_store_fingerprint, 1, &store)) { return false; } @@ -741,6 +731,10 @@ namespace smt { var_data * d = m_var_data[v]; if (d->m_prop_upward && instantiate_axiom_map_for(v)) r = FC_CONTINUE; + if (d->m_prop_upward && !m_params.m_array_weak) { + if (instantiate_parent_stores_default(v)) + r = FC_CONTINUE; + } } } while (!m_eqsv.empty()) { @@ -755,6 +749,22 @@ namespace smt { return r; } + bool theory_array_full::instantiate_parent_stores_default(theory_var v) { + SASSERT(v != null_theory_var); + TRACE("array", tout << "v" << v << "\n";); + v = find(v); + var_data* d = m_var_data[v]; + bool result = false; + for (enode * store : d->m_parent_stores) { + TRACE("array", tout << expr_ref(store->get_owner(), get_manager()) << "\n";); + SASSERT(is_store(store)); + if (!m_params.m_array_cg || store->is_cgr()) { + if (instantiate_default_store_axiom(store)) + result = true; + } + } + return result; + } bool theory_array_full::try_assign_eq(expr* v1, expr* v2) { TRACE("array", diff --git a/src/smt/theory_array_full.h b/src/smt/theory_array_full.h index 3216b4286..b249ec0b4 100644 --- a/src/smt/theory_array_full.h +++ b/src/smt/theory_array_full.h @@ -38,6 +38,11 @@ namespace smt { ast2ast_trailmap m_sort2epsilon; obj_pair_map m_eqs; svector m_eqsv; + + static unsigned const m_default_map_fingerprint = UINT_MAX - 112; + static unsigned const m_default_store_fingerprint = UINT_MAX - 113; + static unsigned const m_default_const_fingerprint = UINT_MAX - 115; + static unsigned const m_default_as_array_fingerprint = UINT_MAX - 116; protected: @@ -70,6 +75,7 @@ namespace smt { bool instantiate_default_store_axiom(enode* store); bool instantiate_default_map_axiom(enode* map); bool instantiate_default_as_array_axiom(enode* arr); + bool instantiate_parent_stores_default(theory_var v); bool has_large_domain(app* array_term); app* mk_epsilon(sort* s); From 46cdefac4d1f54e74f3aebae37e03b047f43cb61 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 3 Oct 2018 10:57:02 -0700 Subject: [PATCH 118/138] fix memory leak when cuber isn't run to completion. Found by Daniel Selsam Signed-off-by: Nikolaj Bjorner --- src/sat/sat_solver.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index b65abd41c..426caa6dd 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -78,6 +78,8 @@ namespace sat { del_clauses(m_clauses); TRACE("sat", tout << "Delete learned\n";); del_clauses(m_learned); + dealloc(m_cuber); + m_cuber = nullptr; } void solver::del_clauses(clause_vector& clauses) { From 3bc2213d544a4fe62a9c43a3aee6b8879b7430b9 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 3 Oct 2018 17:43:42 -0700 Subject: [PATCH 119/138] fix #1577 Signed-off-by: Nikolaj Bjorner --- src/api/api_opt.cpp | 30 +++++++++++++++++++++++++--- src/api/c++/z3++.h | 13 +++++++++++- src/api/dotnet/Optimize.cs | 23 ++++++++++++++++++++-- src/api/java/Optimize.java | 33 +++++++++++++++++++++++++++++-- src/api/ml/z3.ml | 2 +- src/api/python/z3/z3.py | 14 ++++++++++--- src/api/z3_optimization.h | 14 +++++++++++-- src/cmd_context/cmd_context.cpp | 8 ++++---- src/cmd_context/cmd_context.h | 2 +- src/opt/opt_context.cpp | 35 ++++++++++++++++++++++++--------- src/opt/opt_context.h | 7 ++++--- src/shell/opt_frontend.cpp | 3 ++- 12 files changed, 152 insertions(+), 32 deletions(-) diff --git a/src/api/api_opt.cpp b/src/api/api_opt.cpp index 71f92eeba..45832b5b6 100644 --- a/src/api/api_opt.cpp +++ b/src/api/api_opt.cpp @@ -124,10 +124,16 @@ extern "C" { } - Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o) { + Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o, unsigned num_assumptions, Z3_ast const assumptions[]) { Z3_TRY; - LOG_Z3_optimize_check(c, o); + LOG_Z3_optimize_check(c, o, num_assumptions, assumptions); RESET_ERROR_CODE(); + for (unsigned i = 0; i < num_assumptions; i++) { + if (!is_expr(to_ast(assumptions[i]))) { + SET_ERROR_CODE(Z3_INVALID_ARG, "assumption is not an expression"); + return Z3_L_UNDEF; + } + } lbool r = l_undef; cancel_eh eh(mk_c(c)->m().limit()); unsigned timeout = to_optimize_ptr(o)->get_params().get_uint("timeout", mk_c(c)->get_timeout()); @@ -137,7 +143,9 @@ extern "C" { scoped_timer timer(timeout, &eh); scoped_rlimit _rlimit(mk_c(c)->m().limit(), rlimit); try { - r = to_optimize_ptr(o)->optimize(); + expr_ref_vector asms(mk_c(c)->m()); + asms.append(num_assumptions, to_exprs(assumptions)); + r = to_optimize_ptr(o)->optimize(asms); } catch (z3_exception& ex) { if (!mk_c(c)->m().canceled()) { @@ -157,6 +165,22 @@ extern "C" { Z3_CATCH_RETURN(Z3_L_UNDEF); } + Z3_ast_vector Z3_API Z3_optimize_get_unsat_core(Z3_context c, Z3_optimize o) { + Z3_TRY; + LOG_Z3_optimize_get_unsat_core(c, o); + RESET_ERROR_CODE(); + expr_ref_vector core(mk_c(c)->m()); + to_optimize_ptr(o)->get_unsat_core(core); + Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m()); + mk_c(c)->save_object(v); + for (expr* e : core) { + v->m_ast_vector.push_back(e); + } + RETURN_Z3(of_ast_vector(v)); + Z3_CATCH_RETURN(nullptr); + } + + Z3_string Z3_API Z3_optimize_get_reason_unknown(Z3_context c, Z3_optimize o) { Z3_TRY; LOG_Z3_optimize_to_string(c, o); diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index a283571d0..92993da0d 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -2455,8 +2455,19 @@ namespace z3 { void pop() { Z3_optimize_pop(ctx(), m_opt); } - check_result check() { Z3_lbool r = Z3_optimize_check(ctx(), m_opt); check_error(); return to_check_result(r); } + check_result check() { Z3_lbool r = Z3_optimize_check(ctx(), m_opt, 0, 0); check_error(); return to_check_result(r); } + check_result check(expr_vector const& asms) { + array _asms(n); + for (unsigned i = 0; i < n; i++) { + check_context(*this, asms[i]); + _asms[i] = asms[i]; + } + Z3_lbool r = Z3_optimize_check(ctx(), m_opt, n, _asms.ptr()); + check_error(); + return to_check_result(r); + } model get_model() const { Z3_model m = Z3_optimize_get_model(ctx(), m_opt); check_error(); return model(ctx(), m); } + expr_vector unsat_core() const { Z3_ast_vector r = Z3_optimize_get_unsat_core(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } void set(params const & p) { Z3_optimize_set_params(ctx(), m_opt, p); check_error(); } expr lower(handle const& h) { Z3_ast r = Z3_optimize_get_lower(ctx(), m_opt, h.h()); diff --git a/src/api/dotnet/Optimize.cs b/src/api/dotnet/Optimize.cs index 29e5e869a..ce3cc55f7 100644 --- a/src/api/dotnet/Optimize.cs +++ b/src/api/dotnet/Optimize.cs @@ -183,9 +183,9 @@ namespace Microsoft.Z3 /// don't use strict inequalities) meets the objectives. /// /// - public Status Check() + public Status Check(params Expr[] assumptions) { - Z3_lbool r = (Z3_lbool)Native.Z3_optimize_check(Context.nCtx, NativeObject); + Z3_lbool r = (Z3_lbool)Native.Z3_optimize_check(Context.nCtx, NativeObject, (uint)assumptions.Length, AST.ArrayToNative(assumptions)); switch (r) { case Z3_lbool.Z3_L_TRUE: @@ -236,6 +236,25 @@ namespace Microsoft.Z3 } } + /// + /// The unsat core of the last Check. + /// + /// + /// The unsat core is a subset of assumptions + /// The result is empty if Check was not invoked before, + /// if its results was not UNSATISFIABLE, or if core production is disabled. + /// + public BoolExpr[] UnsatCore + { + get + { + Contract.Ensures(Contract.Result() != null); + + ASTVector core = new ASTVector(Context, Native.Z3_optimize_get_unsat_core(Context.nCtx, NativeObject)); + return core.ToBoolExprArray(); + } + } + /// /// Declare an arithmetical maximization objective. /// Return a handle to the objective. The handle is used as diff --git a/src/api/java/Optimize.java b/src/api/java/Optimize.java index c5f8f9449..a91e2a573 100644 --- a/src/api/java/Optimize.java +++ b/src/api/java/Optimize.java @@ -161,9 +161,23 @@ public class Optimize extends Z3Object { * Produce a model that (when the objectives are bounded and * don't use strict inequalities) meets the objectives. **/ - public Status Check() + public Status Check(Expr... assumptions) { - Z3_lbool r = Z3_lbool.fromInt(Native.optimizeCheck(getContext().nCtx(), getNativeObject())); + Z3_lbool r; + if (assumptions == null) { + r = Z3_lbool.fromInt( + Native.optimizeCheck( + getContext().nCtx(), + getNativeObject(), 0, null); + } + else { + r = Z3_lbool.fromInt( + Native.optimizeCheck( + getContext().nCtx(), + getNativeObject(), + assumptions.length, + AST.arrayToNative(assumptions))); + } switch (r) { case Z3_L_TRUE: return Status.SATISFIABLE; @@ -209,6 +223,21 @@ public class Optimize extends Z3Object { } } + /** + * The unsat core of the last {@code Check}. + * Remarks: The unsat core + * is a subset of {@code Assumptions} The result is empty if + * {@code Check} was not invoked before, if its results was not + * {@code UNSATISFIABLE}, or if core production is disabled. + * + * @throws Z3Exception + **/ + public BoolExpr[] getUnsatCore() + { + ASTVector core = new ASTVector(getContext(), Native.optimizeGetUnsatCore(getContext().nCtx(), getNativeObject())); + return core.ToBoolExprArray(); + } + /** * Declare an arithmetical maximization objective. * Return a handle to the objective. The handle is used as diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml index 231587729..2ad07aee4 100644 --- a/src/api/ml/z3.ml +++ b/src/api/ml/z3.ml @@ -1947,7 +1947,7 @@ struct let minimize (x:optimize) (e:Expr.expr) = mk_handle x (Z3native.optimize_minimize (gc x) x e) let check (x:optimize) = - let r = lbool_of_int (Z3native.optimize_check (gc x) x) in + let r = lbool_of_int (Z3native.optimize_check (gc x) x) 0 [] in match r with | L_TRUE -> Solver.SATISFIABLE | L_FALSE -> Solver.UNSATISFIABLE diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py index cefe8bbbb..703105356 100644 --- a/src/api/python/z3/z3.py +++ b/src/api/python/z3/z3.py @@ -7311,10 +7311,15 @@ class Optimize(Z3PPObject): """restore to previously created backtracking point""" Z3_optimize_pop(self.ctx.ref(), self.optimize) - def check(self): + def check(self, *assumptions): """Check satisfiability while optimizing objective functions.""" - return CheckSatResult(Z3_optimize_check(self.ctx.ref(), self.optimize)) - + assumptions = _get_args(assumptions) + num = len(assumptions) + _assumptions = (Ast * num)() + for i in range(num): + _assumptions[i] = assumptions[i].as_ast() + return CheckSatResult(Z3_optimize_check(self.ctx.ref(), self.optimize, num, _assumptions)) + def reason_unknown(self): """Return a string that describes why the last `check()` returned `unknown`.""" return Z3_optimize_get_reason_unknown(self.ctx.ref(), self.optimize) @@ -7326,6 +7331,9 @@ class Optimize(Z3PPObject): except Z3Exception: raise Z3Exception("model is not available") + def unsat_core(self): + return AstVector(Z3_optimize_get_unsat_core(self.ctx.ref(), self.optimize), self.ctx) + def lower(self, obj): if not isinstance(obj, OptimizeObjective): raise Z3Exception("Expecting objective handle returned by maximize/minimize") diff --git a/src/api/z3_optimization.h b/src/api/z3_optimization.h index f49e1b9ce..8f9e2470c 100644 --- a/src/api/z3_optimization.h +++ b/src/api/z3_optimization.h @@ -117,10 +117,12 @@ extern "C" { \brief Check consistency and produce optimal values. \param c - context \param o - optimization context + \param num_assumptions - number of additional assumptions + \param assumptions - the additional assumptions - def_API('Z3_optimize_check', INT, (_in(CONTEXT), _in(OPTIMIZE))) + def_API('Z3_optimize_check', INT, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT), _in_array(2, AST))) */ - Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o); + Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o, unsigned num_assumptions, Z3_ast const assumptions[]); /** @@ -143,6 +145,14 @@ extern "C" { */ Z3_model Z3_API Z3_optimize_get_model(Z3_context c, Z3_optimize o); + /** + \brief Retrieve the unsat core for the last #Z3_optimize_chec + The unsat core is a subset of the assumptions \c a. + + def_API('Z3_optimize_get_unsat_core', AST_VECTOR, (_in(CONTEXT), _in(OPTIMIZE))) + */ + Z3_ast_vector Z3_API Z3_optimize_get_unsat_core(Z3_context c, Z3_optimize o); + /** \brief Set parameters on optimization context. diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index be3acc261..89fb4f3cc 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1488,13 +1488,13 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions scoped_ctrl_c ctrlc(eh); scoped_timer timer(timeout, &eh); scoped_rlimit _rlimit(m().limit(), rlimit); + expr_ref_vector asms(m()); + asms.append(num_assumptions, assumptions); if (!m_processing_pareto) { - ptr_vector cnstr(m_assertions); - cnstr.append(num_assumptions, assumptions); - get_opt()->set_hard_constraints(cnstr); + get_opt()->set_hard_constraints(m_assertions); } try { - r = get_opt()->optimize(); + r = get_opt()->optimize(asms); if (r == l_true && get_opt()->is_pareto()) { m_processing_pareto = true; } diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index a0093191d..87d80babe 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -148,7 +148,7 @@ public: virtual bool empty() = 0; virtual void push() = 0; virtual void pop(unsigned n) = 0; - virtual lbool optimize() = 0; + virtual lbool optimize(expr_ref_vector const& asms) = 0; virtual void set_hard_constraints(ptr_vector & hard) = 0; virtual void display_assignment(std::ostream& out) = 0; virtual bool is_pareto() = 0; diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 87da85af5..8941f5a6d 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -130,6 +130,7 @@ namespace opt { m_fm(alloc(generic_model_converter, m, "opt")), m_model_fixed(), m_objective_refs(m), + m_core(m), m_enable_sat(false), m_is_clausal(false), m_pp_neat(false), @@ -173,10 +174,9 @@ namespace opt { } void context::get_unsat_core(expr_ref_vector & r) { - throw default_exception("Unsat cores are not supported with optimization"); + r.append(m_core); } - void context::set_hard_constraints(ptr_vector& fmls) { if (m_scoped_state.set(fmls)) { clear_state(); @@ -253,7 +253,7 @@ namespace opt { m_hard_constraints.append(s.m_hard); } - lbool context::optimize() { + lbool context::optimize(expr_ref_vector const& asms) { if (m_pareto) { return execute_pareto(); } @@ -263,7 +263,10 @@ namespace opt { clear_state(); init_solver(); import_scoped_state(); - normalize(); + normalize(asms); + if (m_hard_constraints.size() == 1 && m.is_false(m_hard_constraints.get(0))) { + return l_false; + } internalize(); update_solver(); if (contains_quantifiers()) { @@ -281,7 +284,7 @@ namespace opt { symbol pri = optp.priority(); IF_VERBOSE(1, verbose_stream() << "(optimize:check-sat)\n"); - lbool is_sat = s.check_sat(0,nullptr); + lbool is_sat = s.check_sat(asms.size(),asms.c_ptr()); TRACE("opt", s.display(tout << "initial search result: " << is_sat << "\n");); if (is_sat != l_false) { s.get_model(m_model); @@ -290,6 +293,9 @@ namespace opt { } if (is_sat != l_true) { TRACE("opt", tout << m_hard_constraints << "\n";); + if (!asms.empty()) { + s.get_unsat_core(m_core); + } return is_sat; } IF_VERBOSE(1, verbose_stream() << "(optimize:sat)\n"); @@ -751,22 +757,25 @@ namespace opt { return m_arith.is_numeral(e, n) || m_bv.is_numeral(e, n, sz); } - void context::normalize() { + void context::normalize(expr_ref_vector const& asms) { expr_ref_vector fmls(m); to_fmls(fmls); - simplify_fmls(fmls); + simplify_fmls(fmls, asms); from_fmls(fmls); } - void context::simplify_fmls(expr_ref_vector& fmls) { + void context::simplify_fmls(expr_ref_vector& fmls, expr_ref_vector const& asms) { if (m_is_clausal) { return; } - goal_ref g(alloc(goal, m, true, false)); + goal_ref g(alloc(goal, m, true, !asms.empty())); for (expr* fml : fmls) { g->assert_expr(fml); } + for (expr * a : asms) { + g->assert_expr(a, a); + } tactic_ref tac0 = and_then(mk_simplify_tactic(m, m_params), mk_propagate_values_tactic(m), @@ -788,6 +797,7 @@ namespace opt { set_simplify(tac0.get()); } goal_ref_buffer result; + TRACE("opt", g->display(tout);); (*m_simplify)(g, result); SASSERT(result.size() == 1); goal* r = result[0]; @@ -797,6 +807,12 @@ namespace opt { for (unsigned i = 0; i < r->size(); ++i) { fmls.push_back(r->form(i)); } + if (r->inconsistent()) { + ptr_vector core_elems; + expr_dependency_ref core(r->dep(0), m); + m.linearize(core, core_elems); + m_core.append(core_elems.size(), core_elems.c_ptr()); + } } bool context::is_maximize(expr* fml, app_ref& term, expr_ref& orig_term, unsigned& index) { @@ -1395,6 +1411,7 @@ namespace opt { m_box_index = UINT_MAX; m_model.reset(); m_model_fixed.reset(); + m_core.reset(); } void context::set_pareto(pareto_base* p) { diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index 1172e68b6..f57e937ba 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -164,6 +164,7 @@ namespace opt { obj_map m_objective_fns; obj_map m_objective_orig; func_decl_ref_vector m_objective_refs; + expr_ref_vector m_core; tactic_ref m_simplify; bool m_enable_sat; bool m_enable_sls; @@ -187,7 +188,7 @@ namespace opt { void pop(unsigned n) override; bool empty() override { return m_scoped_state.m_objectives.empty(); } void set_hard_constraints(ptr_vector & hard) override; - lbool optimize() override; + lbool optimize(expr_ref_vector const& asms) override; void set_model(model_ref& _m) override { m_model = _m; } void get_model_core(model_ref& _m) override; void get_box_model(model_ref& _m, unsigned index) override; @@ -254,7 +255,7 @@ namespace opt { void reset_maxsmts(); void import_scoped_state(); - void normalize(); + void normalize(expr_ref_vector const& asms); void internalize(); bool is_maximize(expr* fml, app_ref& term, expr_ref& orig_term, unsigned& index); bool is_minimize(expr* fml, app_ref& term, expr_ref& orig_term, unsigned& index); @@ -270,7 +271,7 @@ namespace opt { expr* mk_objective_fn(unsigned index, objective_t ty, unsigned sz, expr*const* args); void to_fmls(expr_ref_vector& fmls); void from_fmls(expr_ref_vector const& fmls); - void simplify_fmls(expr_ref_vector& fmls); + void simplify_fmls(expr_ref_vector& fmls, expr_ref_vector const& asms); void mk_atomic(expr_ref_vector& terms); void update_lower() { update_bound(true); } diff --git a/src/shell/opt_frontend.cpp b/src/shell/opt_frontend.cpp index 0a2a8f4a1..afa7b79c5 100644 --- a/src/shell/opt_frontend.cpp +++ b/src/shell/opt_frontend.cpp @@ -108,7 +108,8 @@ static unsigned parse_opt(std::istream& in, opt_format f) { unsigned rlimit = std::stoi(gparams::get_value("rlimit")); scoped_timer timer(timeout, &eh); scoped_rlimit _rlimit(m.limit(), rlimit); - lbool r = opt.optimize(); + expr_ref_vector asms(m); + lbool r = opt.optimize(asms); switch (r) { case l_true: std::cout << "sat\n"; break; case l_false: std::cout << "unsat\n"; break; From f8e5d989bf0f914d9911b0b8fa27e081f1e16f7f Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Wed, 3 Oct 2018 17:49:57 -0700 Subject: [PATCH 120/138] fix #1577 Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 92993da0d..13232dd28 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -2457,6 +2457,7 @@ namespace z3 { } check_result check() { Z3_lbool r = Z3_optimize_check(ctx(), m_opt, 0, 0); check_error(); return to_check_result(r); } check_result check(expr_vector const& asms) { + unsigned n = asms.size(); array _asms(n); for (unsigned i = 0; i < n; i++) { check_context(*this, asms[i]); @@ -2467,7 +2468,7 @@ namespace z3 { return to_check_result(r); } model get_model() const { Z3_model m = Z3_optimize_get_model(ctx(), m_opt); check_error(); return model(ctx(), m); } - expr_vector unsat_core() const { Z3_ast_vector r = Z3_optimize_get_unsat_core(ctx(), m_solver); check_error(); return expr_vector(ctx(), r); } + expr_vector unsat_core() const { Z3_ast_vector r = Z3_optimize_get_unsat_core(ctx(), m_opt); check_error(); return expr_vector(ctx(), r); } void set(params const & p) { Z3_optimize_set_params(ctx(), m_opt, p); check_error(); } expr lower(handle const& h) { Z3_ast r = Z3_optimize_get_lower(ctx(), m_opt, h.h()); From a549e73b86967989d55e99f2e008aa5348909d0b Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 4 Oct 2018 13:43:01 -0700 Subject: [PATCH 121/138] na Signed-off-by: Nikolaj Bjorner --- src/opt/opt_context.cpp | 4 +--- src/opt/opt_pareto.cpp | 2 +- src/smt/theory_arith_aux.h | 5 ++++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 87da85af5..74d1ca955 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -479,7 +479,6 @@ namespace opt { return r; } - expr_ref context::mk_le(unsigned i, model_ref& mdl) { objective const& obj = m_objectives[i]; return mk_cmp(false, mdl, obj); @@ -489,8 +488,7 @@ namespace opt { objective const& obj = m_objectives[i]; return mk_cmp(true, mdl, obj); } - - + expr_ref context::mk_gt(unsigned i, model_ref& mdl) { expr_ref result = mk_le(i, mdl); result = mk_not(m, result); diff --git a/src/opt/opt_pareto.cpp b/src/opt/opt_pareto.cpp index fe92c3ba6..ada5d0d14 100644 --- a/src/opt/opt_pareto.cpp +++ b/src/opt/opt_pareto.cpp @@ -71,7 +71,7 @@ namespace opt { fmls.push_back(mk_or(gt)); fml = mk_and(fmls); IF_VERBOSE(10, verbose_stream() << "dominates: " << fml << "\n";); - TRACE("opt", tout << fml << "\n"; model_smt2_pp(tout, m, *m_model, 0);); + TRACE("opt", model_smt2_pp(tout << fml << "\n", m, *m_model, 0);); m_solver->assert_expr(fml); } diff --git a/src/smt/theory_arith_aux.h b/src/smt/theory_arith_aux.h index 9526637e9..89737fb42 100644 --- a/src/smt/theory_arith_aux.h +++ b/src/smt/theory_arith_aux.h @@ -1103,6 +1103,7 @@ namespace smt { e = m_util.mk_gt(obj, e); } } + TRACE("opt", tout << e << "\n";); return e; } @@ -1119,6 +1120,8 @@ namespace smt { std::ostringstream strm; strm << val << " <= " << mk_pp(get_enode(v)->get_owner(), get_manager()); app* b = m.mk_const(symbol(strm.str().c_str()), m.mk_bool_sort()); + expr_ref result(b, m); + TRACE("opt", tout << result << "\n";); if (!ctx.b_internalized(b)) { fm.hide(b->get_decl()); bool_var bv = ctx.mk_bool_var(b); @@ -1133,7 +1136,7 @@ namespace smt { TRACE("arith", tout << mk_pp(b, m) << "\n"; display_atom(tout, a, false);); } - return expr_ref(b, m); + return result; } From 2097983db301831c539c499ed806a7437dbc5029 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 4 Oct 2018 14:05:38 -0700 Subject: [PATCH 122/138] fix java bindings Signed-off-by: Nikolaj Bjorner --- src/api/java/Optimize.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/java/Optimize.java b/src/api/java/Optimize.java index a91e2a573..ae50d7f33 100644 --- a/src/api/java/Optimize.java +++ b/src/api/java/Optimize.java @@ -168,7 +168,7 @@ public class Optimize extends Z3Object { r = Z3_lbool.fromInt( Native.optimizeCheck( getContext().nCtx(), - getNativeObject(), 0, null); + getNativeObject(), 0, null)); } else { r = Z3_lbool.fromInt( From 99339798ee98c6317995a4ed10a26bd39e5a8433 Mon Sep 17 00:00:00 2001 From: Lev Date: Thu, 4 Oct 2018 16:42:32 -0700 Subject: [PATCH 123/138] fix the value oflar_solver.m_status during pop() Signed-off-by: Lev --- src/util/lp/lar_solver.cpp | 2 +- src/util/lp/lar_solver.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp index 5b3028a98..31e9d2654 100644 --- a/src/util/lp/lar_solver.cpp +++ b/src/util/lp/lar_solver.cpp @@ -377,7 +377,7 @@ void lar_solver::pop(unsigned k) { m_settings.simplex_strategy() = m_simplex_strategy; lp_assert(sizes_are_correct()); lp_assert((!m_settings.use_tableau()) || m_mpq_lar_core_solver.m_r_solver.reduced_costs_are_correct_tableau()); - m_status = m_mpq_lar_core_solver.m_r_solver.current_x_is_feasible()? lp_status::OPTIMAL: lp_status::UNKNOWN; + set_status(lp_status::UNKNOWN); } vector lar_solver::get_all_constraint_indices() const { diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index 8541a9b86..7af9cfacb 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -94,7 +94,6 @@ class lar_solver : public column_namer { var_register m_var_register; stacked_vector m_columns_to_ul_pairs; vector m_constraints; -private: stacked_value m_constraint_count; // the set of column indices j such that bounds have changed for j int_set m_columns_with_changed_bound; From 44a0dbbc6122a83f27ceb8f50f59633c37bf53e6 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 6 Oct 2018 08:06:51 -0700 Subject: [PATCH 124/138] fix #1864 Signed-off-by: Nikolaj Bjorner --- src/api/java/Context.java | 16 ++++++++++++++++ src/opt/opt_context.cpp | 2 +- src/smt/theory_lra.cpp | 2 +- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/src/api/java/Context.java b/src/api/java/Context.java index 57085a09e..fda1a78f3 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -1976,6 +1976,22 @@ public class Context implements AutoCloseable { { return (SeqExpr) Expr.create(this, Native.mkString(nCtx(), s)); } + + /** + * Convert an integer expression to a string. + */ + public SeqExpr intToString(Expr e) + { + return (SeqExpr) Expr.create(this, Native.Z3_mkIntToStr(nCtx(), e.getNativeObject())); + } + + /** + * Convert an integer expression to a string. + */ + public IntExpr stringToInt(Expr e) + { + return (IntExpr) Expr.create(this, Native.mkStrToInt(nCtx(), e.getNativeObject())); + } /** * Concatenate sequences. diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 8d7639851..8741ec419 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -292,7 +292,7 @@ namespace opt { model_updated(m_model.get()); } if (is_sat != l_true) { - TRACE("opt", tout << m_hard_constraints << "\n";); + TRACE("opt", tout << m_hard_constraints << " " << asms << "\n";); if (!asms.empty()) { s.get_unsat_core(m_core); } diff --git a/src/smt/theory_lra.cpp b/src/smt/theory_lra.cpp index 1d49c4386..24309ba88 100644 --- a/src/smt/theory_lra.cpp +++ b/src/smt/theory_lra.cpp @@ -1412,7 +1412,7 @@ public: } final_check_status final_check_eh() { - IF_VERBOSE(2, verbose_stream() << "final-check\n"); + IF_VERBOSE(2, verbose_stream() << "final-check " << m_solver->get_status() << "\n"); m_use_nra_model = false; lbool is_sat = l_true; if (m_solver->get_status() != lp::lp_status::OPTIMAL) { From c4829dfa22978ff7c8041b4826721d67ae6d9250 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 6 Oct 2018 09:01:01 -0700 Subject: [PATCH 125/138] fix #1577 again Signed-off-by: Nikolaj Bjorner --- src/api/api_datalog.cpp | 6 ++---- src/api/api_opt.cpp | 6 ++---- src/api/api_parsers.cpp | 6 ++---- src/api/api_solver.cpp | 6 ++---- src/ast/pattern/expr_pattern_match.cpp | 6 ++---- src/cmd_context/cmd_context.cpp | 20 ++++++++++++++------ src/cmd_context/cmd_context.h | 9 +++------ src/cmd_context/cmd_context_to_goal.cpp | 16 +++++++--------- src/muz/fp/dl_cmds.cpp | 6 ++---- src/opt/opt_context.cpp | 23 ++++++++++++++++++----- src/opt/opt_context.h | 4 ++-- src/parsers/smt2/marshal.cpp | 9 +++++---- 12 files changed, 61 insertions(+), 56 deletions(-) diff --git a/src/api/api_datalog.cpp b/src/api/api_datalog.cpp index a95f1d8b1..c2f437391 100644 --- a/src/api/api_datalog.cpp +++ b/src/api/api_datalog.cpp @@ -379,10 +379,8 @@ extern "C" { for (unsigned i = 0; i < coll.m_rules.size(); ++i) { to_fixedpoint_ref(d)->add_rule(coll.m_rules[i].get(), coll.m_names[i]); } - ptr_vector::const_iterator it = ctx.begin_assertions(); - ptr_vector::const_iterator end = ctx.end_assertions(); - for (; it != end; ++it) { - to_fixedpoint_ref(d)->ctx().assert_expr(*it); + for (expr * e : ctx.assertions()) { + to_fixedpoint_ref(d)->ctx().assert_expr(e); } return of_ast_vector(v); diff --git a/src/api/api_opt.cpp b/src/api/api_opt.cpp index 45832b5b6..0b56b788d 100644 --- a/src/api/api_opt.cpp +++ b/src/api/api_opt.cpp @@ -354,10 +354,8 @@ extern "C" { return; } - ptr_vector::const_iterator it = ctx->begin_assertions(); - ptr_vector::const_iterator end = ctx->end_assertions(); - for (; it != end; ++it) { - to_optimize_ptr(opt)->add_hard_constraint(*it); + for (expr * e : ctx->assertions()) { + to_optimize_ptr(opt)->add_hard_constraint(e); } } diff --git a/src/api/api_parsers.cpp b/src/api/api_parsers.cpp index b88f273f9..32c133d2b 100644 --- a/src/api/api_parsers.cpp +++ b/src/api/api_parsers.cpp @@ -71,10 +71,8 @@ extern "C" { SET_ERROR_CODE(Z3_PARSER_ERROR, errstrm.str().c_str()); return of_ast_vector(v); } - ptr_vector::const_iterator it = ctx->begin_assertions(); - ptr_vector::const_iterator end = ctx->end_assertions(); - for (; it != end; ++it) { - v->m_ast_vector.push_back(*it); + for (expr * e : ctx->assertions()) { + v->m_ast_vector.push_back(e); } return of_ast_vector(v); Z3_CATCH_RETURN(nullptr); diff --git a/src/api/api_solver.cpp b/src/api/api_solver.cpp index fc4e01f3a..204370346 100644 --- a/src/api/api_solver.cpp +++ b/src/api/api_solver.cpp @@ -157,10 +157,8 @@ extern "C" { bool initialized = to_solver(s)->m_solver.get() != nullptr; if (!initialized) init_solver(c, s); - ptr_vector::const_iterator it = ctx->begin_assertions(); - ptr_vector::const_iterator end = ctx->end_assertions(); - for (; it != end; ++it) { - to_solver_ref(s)->assert_expr(*it); + for (expr * e : ctx->assertions()) { + to_solver_ref(s)->assert_expr(e); } to_solver_ref(s)->set_model_converter(ctx->get_model_converter()); } diff --git a/src/ast/pattern/expr_pattern_match.cpp b/src/ast/pattern/expr_pattern_match.cpp index 47b3e9203..5cd3542df 100644 --- a/src/ast/pattern/expr_pattern_match.cpp +++ b/src/ast/pattern/expr_pattern_match.cpp @@ -393,10 +393,8 @@ expr_pattern_match::initialize(char const * spec_string) { VERIFY(parse_smt2_commands(ctx, is)); ctx.set_print_success(ps); - ptr_vector::const_iterator it = ctx.begin_assertions(); - ptr_vector::const_iterator end = ctx.end_assertions(); - for (; it != end; ++it) { - compile(*it); + for (expr * e : ctx.assertions()) { + compile(e); } TRACE("expr_pattern_match", display(tout); ); } diff --git a/src/cmd_context/cmd_context.cpp b/src/cmd_context/cmd_context.cpp index 89fb4f3cc..8a23f80a0 100644 --- a/src/cmd_context/cmd_context.cpp +++ b/src/cmd_context/cmd_context.cpp @@ -1312,7 +1312,7 @@ void cmd_context::assert_expr(expr * t) { m().inc_ref(t); m_assertions.push_back(t); if (produce_unsat_cores()) - m_assertion_names.push_back(0); + m_assertion_names.push_back(nullptr); if (m_solver) m_solver->assert_expr(t); } @@ -1491,7 +1491,18 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions expr_ref_vector asms(m()); asms.append(num_assumptions, assumptions); if (!m_processing_pareto) { - get_opt()->set_hard_constraints(m_assertions); + expr_ref_vector assertions(m()); + unsigned sz = m_assertions.size(); + for (unsigned i = 0; i < sz; ++i) { + if (m_assertion_names.size() > i && m_assertion_names[i]) { + asms.push_back(m_assertion_names[i]); + assertions.push_back(m().mk_implies(m_assertion_names[i], m_assertions[i])); + } + else { + assertions.push_back(m_assertions[i]); + } + } + get_opt()->set_hard_constraints(assertions); } try { r = get_opt()->optimize(asms); @@ -1802,11 +1813,8 @@ void cmd_context::validate_model() { cancel_eh eh(m().limit()); expr_ref r(m()); scoped_ctrl_c ctrlc(eh); - ptr_vector::const_iterator it = begin_assertions(); - ptr_vector::const_iterator end = end_assertions(); bool invalid_model = false; - for (; it != end; ++it) { - expr * a = *it; + for (expr * a : assertions()) { if (is_ground(a)) { r = nullptr; evaluator(a, r); diff --git a/src/cmd_context/cmd_context.h b/src/cmd_context/cmd_context.h index 87d80babe..cb49d1825 100644 --- a/src/cmd_context/cmd_context.h +++ b/src/cmd_context/cmd_context.h @@ -149,7 +149,7 @@ public: virtual void push() = 0; virtual void pop(unsigned n) = 0; virtual lbool optimize(expr_ref_vector const& asms) = 0; - virtual void set_hard_constraints(ptr_vector & hard) = 0; + virtual void set_hard_constraints(expr_ref_vector const & hard) = 0; virtual void display_assignment(std::ostream& out) = 0; virtual bool is_pareto() = 0; virtual void set_logic(symbol const& s) = 0; @@ -452,11 +452,8 @@ public: double get_seconds() const { return m_watch.get_seconds(); } - ptr_vector::const_iterator begin_assertions() const { return m_assertions.begin(); } - ptr_vector::const_iterator end_assertions() const { return m_assertions.end(); } - - ptr_vector::const_iterator begin_assertion_names() const { return m_assertion_names.begin(); } - ptr_vector::const_iterator end_assertion_names() const { return m_assertion_names.end(); } + ptr_vector const& assertions() const { return m_assertions; } + ptr_vector const& assertion_names() const { return m_assertion_names; } /** \brief Hack: consume assertions if there are no scopes. diff --git a/src/cmd_context/cmd_context_to_goal.cpp b/src/cmd_context/cmd_context_to_goal.cpp index a66f9e5de..de19805f2 100644 --- a/src/cmd_context/cmd_context_to_goal.cpp +++ b/src/cmd_context/cmd_context_to_goal.cpp @@ -28,20 +28,18 @@ void assert_exprs_from(cmd_context const & ctx, goal & t) { ast_manager & m = t.m(); bool proofs_enabled = t.proofs_enabled(); if (ctx.produce_unsat_cores()) { - ptr_vector::const_iterator it = ctx.begin_assertions(); - ptr_vector::const_iterator end = ctx.end_assertions(); - ptr_vector::const_iterator it2 = ctx.begin_assertion_names(); - SASSERT(end - it == ctx.end_assertion_names() - it2); + ptr_vector::const_iterator it = ctx.assertions().begin(); + ptr_vector::const_iterator end = ctx.assertions().end(); + ptr_vector::const_iterator it2 = ctx.assertion_names().begin(); + SASSERT(ctx.assertions().size() == ctx.assertion_names().size()); for (; it != end; ++it, ++it2) { t.assert_expr(*it, proofs_enabled ? m.mk_asserted(*it) : nullptr, m.mk_leaf(*it2)); } } else { - ptr_vector::const_iterator it = ctx.begin_assertions(); - ptr_vector::const_iterator end = ctx.end_assertions(); - for (; it != end; ++it) { - t.assert_expr(*it, proofs_enabled ? m.mk_asserted(*it) : nullptr, nullptr); + for (expr * e : ctx.assertions()) { + t.assert_expr(e, proofs_enabled ? m.mk_asserted(e) : nullptr, nullptr); } - SASSERT(ctx.begin_assertion_names() == ctx.end_assertion_names()); + SASSERT(ctx.assertion_names().empty()); } } diff --git a/src/muz/fp/dl_cmds.cpp b/src/muz/fp/dl_cmds.cpp index 231dca0d3..8bea46bea 100644 --- a/src/muz/fp/dl_cmds.cpp +++ b/src/muz/fp/dl_cmds.cpp @@ -332,10 +332,8 @@ public: private: void set_background(cmd_context& ctx) { datalog::context& dlctx = m_dl_ctx->dlctx(); - ptr_vector::const_iterator it = ctx.begin_assertions(); - ptr_vector::const_iterator end = ctx.end_assertions(); - for (; it != end; ++it) { - dlctx.assert_expr(*it); + for (expr * e : ctx.assertions()) { + dlctx.assert_expr(e); } } diff --git a/src/opt/opt_context.cpp b/src/opt/opt_context.cpp index 8741ec419..ff37bfa95 100644 --- a/src/opt/opt_context.cpp +++ b/src/opt/opt_context.cpp @@ -79,13 +79,13 @@ namespace opt { m_hard.push_back(hard); } - bool context::scoped_state::set(ptr_vector & hard) { + bool context::scoped_state::set(expr_ref_vector const & hard) { bool eq = hard.size() == m_hard.size(); for (unsigned i = 0; eq && i < hard.size(); ++i) { - eq = hard[i] == m_hard[i].get(); + eq = hard.get(i) == m_hard.get(i); } m_hard.reset(); - m_hard.append(hard.size(), hard.c_ptr()); + m_hard.append(hard); return !eq; } @@ -177,7 +177,7 @@ namespace opt { r.append(m_core); } - void context::set_hard_constraints(ptr_vector& fmls) { + void context::set_hard_constraints(expr_ref_vector const& fmls) { if (m_scoped_state.set(fmls)) { clear_state(); } @@ -803,7 +803,20 @@ namespace opt { fmls.reset(); expr_ref tmp(m); for (unsigned i = 0; i < r->size(); ++i) { - fmls.push_back(r->form(i)); + if (asms.empty()) { + fmls.push_back(r->form(i)); + continue; + } + + ptr_vector deps; + expr_dependency_ref core(r->dep(i), m); + m.linearize(core, deps); + if (!deps.empty()) { + fmls.push_back(m.mk_implies(m.mk_and(deps.size(), deps.c_ptr()), r->form(i))); + } + else { + fmls.push_back(r->form(i)); + } } if (r->inconsistent()) { ptr_vector core_elems; diff --git a/src/opt/opt_context.h b/src/opt/opt_context.h index f57e937ba..fe0d4a13e 100644 --- a/src/opt/opt_context.h +++ b/src/opt/opt_context.h @@ -133,7 +133,7 @@ namespace opt { void push(); void pop(); void add(expr* hard); - bool set(ptr_vector & hard); + bool set(expr_ref_vector const& hard); unsigned add(expr* soft, rational const& weight, symbol const& id); unsigned add(app* obj, bool is_max); unsigned get_index(symbol const& id) { return m_indices[id]; } @@ -187,7 +187,7 @@ namespace opt { void push() override; void pop(unsigned n) override; bool empty() override { return m_scoped_state.m_objectives.empty(); } - void set_hard_constraints(ptr_vector & hard) override; + void set_hard_constraints(expr_ref_vector const& hard) override; lbool optimize(expr_ref_vector const& asms) override; void set_model(model_ref& _m) override { m_model = _m; } void get_model_core(model_ref& _m) override; diff --git a/src/parsers/smt2/marshal.cpp b/src/parsers/smt2/marshal.cpp index 11244760a..327414300 100644 --- a/src/parsers/smt2/marshal.cpp +++ b/src/parsers/smt2/marshal.cpp @@ -36,11 +36,12 @@ std::string marshal(expr_ref e, ast_manager &m) { expr_ref unmarshal(std::istream &is, ast_manager &m) { cmd_context ctx(false, &m); ctx.set_ignore_check(true); - if (!parse_smt2_commands(ctx, is)) { return expr_ref(nullptr, m); } + if (!parse_smt2_commands(ctx, is)) { + return expr_ref(nullptr, m); + } - ptr_vector::const_iterator it = ctx.begin_assertions(); - ptr_vector::const_iterator end = ctx.end_assertions(); - unsigned size = static_cast(end - it); + ptr_vector::const_iterator it = ctx.assertions().begin(); + unsigned size = ctx.assertions().size(); return expr_ref(mk_and(m, size, it), m); } From aad09816cb08b97f8a149fcefb4424ff6e52c4c5 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 6 Oct 2018 15:16:23 -0700 Subject: [PATCH 126/138] build Signed-off-by: Nikolaj Bjorner --- src/test/arith_rewriter.cpp | 5 ++--- src/test/polynorm.cpp | 3 +-- src/test/qe_arith.cpp | 3 +-- src/test/quant_solve.cpp | 3 +-- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/test/arith_rewriter.cpp b/src/test/arith_rewriter.cpp index d0a110c4f..da49e58dc 100644 --- a/src/test/arith_rewriter.cpp +++ b/src/test/arith_rewriter.cpp @@ -1,4 +1,3 @@ - /*++ Copyright (c) 2015 Microsoft Corporation @@ -26,8 +25,8 @@ static expr_ref parse_fml(ast_manager& m, char const* str) { << "(assert " << str << ")\n"; std::istringstream is(buffer.str()); VERIFY(parse_smt2_commands(ctx, is)); - ENSURE(ctx.begin_assertions() != ctx.end_assertions()); - result = *ctx.begin_assertions(); + ENSURE(!ctx.assertions().empty()); + result = ctx.assertions().get(0); return result; } diff --git a/src/test/polynorm.cpp b/src/test/polynorm.cpp index ef01bd27d..58481938b 100644 --- a/src/test/polynorm.cpp +++ b/src/test/polynorm.cpp @@ -25,8 +25,7 @@ static expr_ref parse_fml(ast_manager& m, char const* str) { << "(assert " << str << ")\n"; std::istringstream is(buffer.str()); VERIFY(parse_smt2_commands(ctx, is)); - ENSURE(ctx.begin_assertions() != ctx.end_assertions()); - result = *ctx.begin_assertions(); + result = ctx.assertions().get(0); return result; } diff --git a/src/test/qe_arith.cpp b/src/test/qe_arith.cpp index 031912c46..2b1b9686c 100644 --- a/src/test/qe_arith.cpp +++ b/src/test/qe_arith.cpp @@ -37,8 +37,7 @@ static expr_ref parse_fml(ast_manager& m, char const* str) { << "(assert " << str << ")\n"; std::istringstream is(buffer.str()); VERIFY(parse_smt2_commands(ctx, is)); - ENSURE(ctx.begin_assertions() != ctx.end_assertions()); - result = *ctx.begin_assertions(); + result = ctx.assertions().get(0); return result; } diff --git a/src/test/quant_solve.cpp b/src/test/quant_solve.cpp index 04a75c42e..d0c616166 100644 --- a/src/test/quant_solve.cpp +++ b/src/test/quant_solve.cpp @@ -131,8 +131,7 @@ static expr_ref parse_fml(ast_manager& m, char const* str) { << "(assert " << str << ")\n"; std::istringstream is(buffer.str()); VERIFY(parse_smt2_commands(ctx, is)); - ENSURE(ctx.begin_assertions() != ctx.end_assertions()); - result = *ctx.begin_assertions(); + result = ctx.assertions().get(0); return result; } From 8e0eb2ac508ad27ab48e3f682e6897329874abe7 Mon Sep 17 00:00:00 2001 From: Andrew Helwer Date: Sun, 26 Aug 2018 13:17:33 -0700 Subject: [PATCH 127/138] Added NuGet package icon --- package/icon.jpg | Bin 0 -> 45731 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 package/icon.jpg diff --git a/package/icon.jpg b/package/icon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a862aa824bc48821b027719b064d7759357c678e GIT binary patch literal 45731 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

pA*idn>A~<;wnxM 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 Yjt>w5Dsyxz*8zeGkIdl#`@hBi2X&SJX8-^I literal 0 HcmV?d00001 From f14a2b9a7c63cfe59d8ad00eafd2ac574871b864 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 6 Oct 2018 16:13:23 -0700 Subject: [PATCH 128/138] fix java Signed-off-by: Nikolaj Bjorner --- src/api/java/Context.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/java/Context.java b/src/api/java/Context.java index fda1a78f3..08d20dfb2 100644 --- a/src/api/java/Context.java +++ b/src/api/java/Context.java @@ -1982,7 +1982,7 @@ public class Context implements AutoCloseable { */ public SeqExpr intToString(Expr e) { - return (SeqExpr) Expr.create(this, Native.Z3_mkIntToStr(nCtx(), e.getNativeObject())); + return (SeqExpr) Expr.create(this, Native.mkIntToStr(nCtx(), e.getNativeObject())); } /** From 7941074fd116ea300cac8d2369a7c489b12e3e19 Mon Sep 17 00:00:00 2001 From: Andrew Helwer Date: Sat, 6 Oct 2018 18:22:55 -0700 Subject: [PATCH 129/138] Added packaging directions, removed linkresource flag --- package/Microsoft.Z3.x64.nuspec | 22 +++++++++++++++++ package/Microsoft.Z3.x64.targets | 12 ++++++++++ package/PackageCreationDirections.md | 36 ++++++++++++++++++++++++++++ scripts/mk_util.py | 3 --- src/api/dotnet/CMakeLists.txt | 5 ---- 5 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 package/Microsoft.Z3.x64.nuspec create mode 100644 package/Microsoft.Z3.x64.targets create mode 100644 package/PackageCreationDirections.md diff --git a/package/Microsoft.Z3.x64.nuspec b/package/Microsoft.Z3.x64.nuspec new file mode 100644 index 000000000..95d594dfb --- /dev/null +++ b/package/Microsoft.Z3.x64.nuspec @@ -0,0 +1,22 @@ + + + + Microsoft.Z3.x64 + $(releaseVersion) + © Microsoft Corporation. All rights reserved. + Microsoft + Microsoft,Z3Prover + $(iconUrlFromReleaseCommit) + https://github.com/Z3Prover/z3 + $(licenseUrlFromReleaseCommit) + + true + Z3 is a constraint/SMT solver and theorem prover from Microsoft Research. + smt constraint solver theorem prover + + diff --git a/package/Microsoft.Z3.x64.targets b/package/Microsoft.Z3.x64.targets new file mode 100644 index 000000000..772ed2ff7 --- /dev/null +++ b/package/Microsoft.Z3.x64.targets @@ -0,0 +1,12 @@ + + + + + + false + libz3.dll + PreserveNewest + + + + diff --git a/package/PackageCreationDirections.md b/package/PackageCreationDirections.md new file mode 100644 index 000000000..930ba14f7 --- /dev/null +++ b/package/PackageCreationDirections.md @@ -0,0 +1,36 @@ +# Z3 NuGet packaging + +## Creation + + 1. After tagging a commit for release, sign Microsoft.Z3.dll and libz3.dll (both x86 and x64 versions) with Microsoft's Authenticode certificate + 2. Test the signed DLLs with the `Get-AuthenticodeSignature` PowerShell commandlet + 3. Create the following directory structure for the x64 package (for x86, substitute the "x64" strings for "x86" and use x86 DLLs): + ``` + +-- Microsoft.Z3.x64 + | +-- Microsoft.Z3.x64.nuspec + | +-- lib + | +-- net40 + | +-- Microsoft.Z3.dll + | +-- build + | +-- Microsoft.Z3.x64.targets + | +-- libz3.dll + ``` + 4. Open the nuspec file and fill in the appropriate macro values (note that for all URLs, preserve link integrity by linking to a specific commit): + * $(releaseVersion) - the Z3 version being released in this package + * $(iconUrlFromReleaseCommit) - URL for the Z3 icon file + * $(licenseUrlFromReleaseCommit) - URL for the Z3 repo license + * $(releaseCommitHash) - hash of the release commit + 5. Run `nuget pack Microsoft.Z3.x64\Microsoft.Z3.x64.nuspec` + 6. Test the resulting nupkg file (described below) then submit the package for signing before uploading to NuGet.org + +## Testing + + 1. Create a directory on your machine at C:\nuget-test-source + 2. Put the Microsoft.Z3.x64.nupkg file in the directory + 3. Open Visual Studio 2017, create a new C# project, then right click the project and click "Manage NuGet packages" + 4. Add a new package source - your C:\nuget-test-source directory + 5. Find the Microsoft.Z3.x64 package, ensuring in preview window that icon is present and all fields correct + 6. Install the Microsoft.Z3.x64 package, ensuring you are asked to accept the license + 7. Build your project. Check the output directory to ensure both Microsoft.Z3.dll and libz3.dll are present + 8. Import Microsoft.Z3 to your project then add a simple line of code like `using (var ctx = new Context()) { }`; build then run your project to ensure the assemblies load properly + \ No newline at end of file diff --git a/scripts/mk_util.py b/scripts/mk_util.py index ebe017739..471edd7cb 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -1668,9 +1668,6 @@ class DotNetDLLComponent(Component): '/noconfig', '/nostdlib+', '/reference:mscorlib.dll', - # Under mono this isn't neccessary as mono will search the system - # library paths for libz3.so - '/linkresource:{}.dll'.format(get_component(Z3_DLL_COMPONENT).dll_name), ] ) diff --git a/src/api/dotnet/CMakeLists.txt b/src/api/dotnet/CMakeLists.txt index 50f643c8d..20621e4fc 100644 --- a/src/api/dotnet/CMakeLists.txt +++ b/src/api/dotnet/CMakeLists.txt @@ -147,11 +147,6 @@ if (DOTNET_TOOLCHAIN_IS_WINDOWS) "/nostdlib+" "/reference:mscorlib.dll" ) - # FIXME: This flag only works when the working directory of csc.exe is - # the directory containing the ``libz3`` target. I can't get this to work - # correctly with multi-configuration generators (i.e. Visual Studio) so - # just don't set the flag for now. - #list(APPEND CSC_FLAGS "/linkresource:$") elseif (DOTNET_TOOLCHAIN_IS_MONO) # We need to give the assembly a strong name so that it can be installed # into the GAC. From e532b4c5856a5e22de6c88b66f39956c34fcd3fd Mon Sep 17 00:00:00 2001 From: Andrew Helwer Date: Sat, 6 Oct 2018 18:59:34 -0700 Subject: [PATCH 130/138] Normalized formatting --- package/Microsoft.Z3.x64.targets | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/package/Microsoft.Z3.x64.targets b/package/Microsoft.Z3.x64.targets index 772ed2ff7..a5b636f69 100644 --- a/package/Microsoft.Z3.x64.targets +++ b/package/Microsoft.Z3.x64.targets @@ -1,12 +1,10 @@ - - - - false - libz3.dll - PreserveNewest - - - + + + false + libz3.dll + PreserveNewest + + From 13183b7c7c533be01392b7d169c8b43175254037 Mon Sep 17 00:00:00 2001 From: nabice Date: Wed, 10 Oct 2018 17:12:16 +0800 Subject: [PATCH 131/138] Ignore current dir when searching for jni --- scripts/mk_util.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mk_util.py b/scripts/mk_util.py index 471edd7cb..97e2b65f2 100644 --- a/scripts/mk_util.py +++ b/scripts/mk_util.py @@ -396,7 +396,7 @@ def check_java(): libdirs = m.group(1).split(',') for libdir in libdirs: q = os.path.dirname(libdir) - if cdirs.count(q) == 0: + if cdirs.count(q) == 0 and len(q) > 0: cdirs.append(q) t.close() From 3b86ea3f8afa0771e58d140099282154d4b05519 Mon Sep 17 00:00:00 2001 From: xlauko Date: Wed, 10 Oct 2018 14:27:08 +0200 Subject: [PATCH 132/138] Add a floating-point support to c++ api. --- src/api/c++/z3++.h | 325 ++++++++++++++++++++++++++++++++------------- 1 file changed, 236 insertions(+), 89 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 13232dd28..ff953ca8f 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -138,11 +138,22 @@ namespace z3 { \brief A Context manages all other Z3 objects, global configuration options, etc. */ class context { + public: + enum class rounding_mode { + RNA, + RNE, + RTP, + RTN, + RTZ + }; + private: bool m_enable_exceptions; + rounding_mode m_rounding_mode; Z3_context m_ctx; void init(config & c) { m_ctx = Z3_mk_context_rc(c); m_enable_exceptions = true; + m_rounding_mode = rounding_mode::RNA; Z3_set_error_handler(m_ctx, 0); Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); } @@ -171,7 +182,7 @@ namespace z3 { } /** - \brief The C++ API uses by defaults exceptions on errors. + \brief The C++ API uses by defaults exceptions on errors. For applications that don't work well with exceptions (there should be only few) you have the ability to turn off exceptions. The tradeoffs are that applications have to be very careful about using check_error() after calls that may result in an @@ -247,6 +258,26 @@ namespace z3 { */ sort array_sort(sort d, sort r); sort array_sort(sort_vector const& d, sort r); + /** + \brief Return a floating point sort. + \c ebits is a number of exponent bits, + \c sbits is a number of significand bits, + \pre where ebits must be larger than 1 and sbits must be larger than 2. + */ + sort fpa_sort(unsigned ebits, unsigned sbits); + /** + \brief Return a FloatingPoint sort with given precision bitwidth (16, 32, 64 or 128). + */ + template + sort fpa_sort(); + /** + \brief Return a RoundingMode sort. + */ + sort fpa_rounding_mode(); + /** + \breif Sets RoundingMode of FloatingPoints. + */ + void set_rounding_mode(rounding_mode rm); /** \brief Return an enumeration sort: enum_names[0], ..., enum_names[n-1]. \c cs and \c ts are output parameters. The method stores in \c cs the constants corresponding to the enumerated elements, @@ -284,6 +315,10 @@ namespace z3 { expr int_const(char const * name); expr real_const(char const * name); expr bv_const(char const * name, unsigned sz); + expr fpa_const(char const * name, unsigned ebits, unsigned sbits); + + template + expr fpa_const(char const * name); expr bool_val(bool b); @@ -307,6 +342,9 @@ namespace z3 { expr bv_val(char const * n, unsigned sz); expr bv_val(unsigned n, bool const* bits); + expr fpa_val(double n); + expr fpa_val(float n); + expr string_val(char const* s); expr string_val(std::string const& s); @@ -465,6 +503,7 @@ namespace z3 { public: sort(context & c):ast(c) {} sort(context & c, Z3_sort s):ast(c, reinterpret_cast(s)) {} + sort(context & c, Z3_ast a):ast(c, a) {} sort(sort const & s):ast(s) {} operator Z3_sort() const { return reinterpret_cast(m_ast); } /** @@ -523,6 +562,10 @@ namespace z3 { \brief Return true if this sort is a Finite domain sort. */ bool is_finite_domain() const { return sort_kind() == Z3_FINITE_DOMAIN_SORT; } + /** + \brief Return true if this sort is a Floating point sort. + */ + bool is_fpa() const { return sort_kind() == Z3_FLOATING_POINT_SORT; } /** \brief Return the size of this Bit-vector sort. @@ -531,6 +574,9 @@ namespace z3 { */ unsigned bv_size() const { assert(is_bv()); unsigned r = Z3_get_bv_sort_size(ctx(), *this); check_error(); return r; } + unsigned fpa_ebits() const { assert(is_fpa()); unsigned r = Z3_fpa_get_ebits(ctx(), *this); check_error(); return r; } + + unsigned fpa_sbits() const { assert(is_fpa()); unsigned r = Z3_fpa_get_sbits(ctx(), *this); check_error(); return r; } /** \brief Return the domain of this Array sort. @@ -634,7 +680,7 @@ namespace z3 { \brief Return true if this is a regular expression. */ bool is_re() const { return get_sort().is_re(); } - + /** \brief Return true if this is a Finite-domain expression. @@ -644,6 +690,10 @@ namespace z3 { */ bool is_finite_domain() const { return get_sort().is_finite_domain(); } + /** + \brief Return true if this is a FloatingPoint expression. . + */ + bool is_fpa() const { return get_sort().is_fpa(); } /** \brief Return true if this expression is a numeral. @@ -696,29 +746,29 @@ namespace z3 { \brief Return true if this expression is well sorted (aka type correct). */ bool is_well_sorted() const { bool r = Z3_is_well_sorted(ctx(), m_ast) != 0; check_error(); return r; } - + /** \brief Return string representation of numeral or algebraic number This method assumes the expression is numeral or algebraic - + \pre is_numeral() || is_algebraic() */ std::string get_decimal_string(int precision) const { assert(is_numeral() || is_algebraic()); return std::string(Z3_get_numeral_decimal_string(ctx(), m_ast, precision)); } - + /** \brief Return int value of numeral, throw if result cannot fit in machine int It only makes sense to use this function if the caller can ensure that - the result is an integer or if exceptions are enabled. + the result is an integer or if exceptions are enabled. If exceptions are disabled, then use the is_numeral_i function. - + \pre is_numeral() */ - int get_numeral_int() const { + int get_numeral_int() const { int result = 0; if (!is_numeral_i(result)) { assert(ctx().enable_exceptions()); @@ -727,13 +777,13 @@ namespace z3 { } return result; } - + /** \brief Return uint value of numeral, throw if result cannot fit in machine uint It only makes sense to use this function if the caller can ensure that - the result is an integer or if exceptions are enabled. + the result is an integer or if exceptions are enabled. If exceptions are disabled, then use the is_numeral_u function. \pre is_numeral() */ @@ -747,11 +797,11 @@ namespace z3 { } return result; } - + /** \brief Return \c int64_t value of numeral, throw if result cannot fit in \c int64_t. - + \pre is_numeral() */ int64_t get_numeral_int64() const { @@ -764,11 +814,11 @@ namespace z3 { } return result; } - + /** \brief Return \c uint64_t value of numeral, throw if result cannot fit in \c uint64_t. - + \pre is_numeral() */ uint64_t get_numeral_uint64() const { @@ -786,7 +836,7 @@ namespace z3 { return Z3_get_bool_value(ctx(), m_ast); } - expr numerator() const { + expr numerator() const { assert(is_numeral()); Z3_ast r = Z3_get_numerator(ctx(), m_ast); check_error(); @@ -794,7 +844,7 @@ namespace z3 { } - expr denominator() const { + expr denominator() const { assert(is_numeral()); Z3_ast r = Z3_get_denominator(ctx(), m_ast); check_error(); @@ -803,6 +853,17 @@ namespace z3 { operator Z3_app() const { assert(is_app()); return reinterpret_cast(m_ast); } + /** + \brief Return a RoundingMode sort. + */ + sort fpa_rounding_mode() { + assert(is_fpa()); + Z3_sort s = ctx().fpa_rounding_mode(); + check_error(); + return sort(ctx(), s); + } + + /** \brief Return the declaration associated with this application. This method assumes the expression is an application. @@ -905,7 +966,7 @@ namespace z3 { bool is_implies() const { return is_app() && Z3_OP_IMPLIES == decl().decl_kind(); } bool is_eq() const { return is_app() && Z3_OP_EQ == decl().decl_kind(); } bool is_ite() const { return is_app() && Z3_OP_ITE == decl().decl_kind(); } - + friend expr distinct(expr_vector const& args); friend expr concat(expr const& a, expr const& b); friend expr concat(expr_vector const& args); @@ -992,23 +1053,34 @@ namespace z3 { friend expr nor(expr const& a, expr const& b); friend expr xnor(expr const& a, expr const& b); + friend expr min(expr const& a, expr const& b); + friend expr max(expr const& a, expr const& b); + expr rotate_left(unsigned i) { Z3_ast r = Z3_mk_rotate_left(ctx(), i, *this); ctx().check_error(); return expr(ctx(), r); } expr rotate_right(unsigned i) { Z3_ast r = Z3_mk_rotate_right(ctx(), i, *this); ctx().check_error(); return expr(ctx(), r); } expr repeat(unsigned i) { Z3_ast r = Z3_mk_repeat(ctx(), i, *this); ctx().check_error(); return expr(ctx(), r); } + friend expr abs(expr const & a); + friend expr sqrt(expr const & a, expr const & rm); + friend expr operator~(expr const & a); - expr extract(unsigned hi, unsigned lo) const { Z3_ast r = Z3_mk_extract(ctx(), hi, lo, *this); ctx().check_error(); return expr(ctx(), r); } + expr extract(unsigned hi, unsigned lo) const { Z3_ast r = Z3_mk_extract(ctx(), hi, lo, *this); ctx().check_error(); return expr(ctx(), r); } 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 FloatingPoint fused multiply-add. + */ + friend expr fma(expr const& a, expr const& b, expr const& c); + /** \brief sequence and regular expression operations. + is overloaded as sequence concatenation and regular expression union. concat is overloaded to handle sequences and regular expressions */ - expr extract(expr const& offset, expr const& length) const { + expr extract(expr const& offset, expr const& length) const { check_context(*this, offset); check_context(offset, length); - Z3_ast r = Z3_mk_seq_extract(ctx(), *this, offset, length); check_error(); return expr(ctx(), r); + Z3_ast r = Z3_mk_seq_extract(ctx(), *this, offset, length); check_error(); return expr(ctx(), r); } expr replace(expr const& src, expr const& dst) const { check_context(*this, src); check_context(src, dst); @@ -1049,19 +1121,19 @@ namespace z3 { return expr(ctx(), r); } - friend expr range(expr const& lo, expr const& hi); + friend expr range(expr const& lo, expr const& hi); /** \brief create a looping regular expression. */ expr loop(unsigned lo) { - Z3_ast r = Z3_mk_re_loop(ctx(), m_ast, lo, 0); - check_error(); - return expr(ctx(), r); + Z3_ast r = Z3_mk_re_loop(ctx(), m_ast, lo, 0); + check_error(); + return expr(ctx(), r); } expr loop(unsigned lo, unsigned hi) { - Z3_ast r = Z3_mk_re_loop(ctx(), m_ast, lo, hi); - check_error(); - return expr(ctx(), r); + Z3_ast r = Z3_mk_re_loop(ctx(), m_ast, lo, hi); + check_error(); + return expr(ctx(), r); } @@ -1094,7 +1166,7 @@ namespace z3 { inline expr implies(expr const & a, expr const & b) { - assert(a.is_bool() && b.is_bool()); + assert(a.is_bool() && b.is_bool()); _Z3_MK_BIN_(a, b, Z3_mk_implies); } inline expr implies(expr const & a, bool b) { return implies(a, a.ctx().bool_val(b)); } @@ -1109,7 +1181,13 @@ namespace z3 { inline expr mod(expr const & a, int b) { return mod(a, a.ctx().num_val(b, a.get_sort())); } inline expr mod(int a, expr const & b) { return mod(b.ctx().num_val(a, b.get_sort()), b); } - inline expr rem(expr const& a, expr const& b) { _Z3_MK_BIN_(a, b, Z3_mk_rem); } + inline expr rem(expr const& a, expr const& b) { + if (a.is_fpa() && b.is_fpa()) { + _Z3_MK_BIN_(a, b, Z3_mk_fpa_rem); + } else { + _Z3_MK_BIN_(a, b, Z3_mk_rem); + } + } inline expr rem(expr const & a, int b) { return rem(a, a.ctx().num_val(b, a.get_sort())); } inline expr rem(int a, expr const & b) { return rem(b.ctx().num_val(a, b.get_sort()), b); } @@ -1158,8 +1236,8 @@ namespace z3 { a.check_error(); return expr(a.ctx(), r); } - inline expr operator==(expr const & a, int b) { assert(a.is_arith() || a.is_bv()); return a == a.ctx().num_val(b, a.get_sort()); } - inline expr operator==(int a, expr const & b) { assert(b.is_arith() || b.is_bv()); return b.ctx().num_val(a, b.get_sort()) == b; } + inline expr operator==(expr const & a, int b) { assert(a.is_arith() || a.is_bv() || a.is_fpa()); return a == a.ctx().num_val(b, a.get_sort()); } + inline expr operator==(int a, expr const & b) { assert(b.is_arith() || b.is_bv() || b.is_fpa()); return b.ctx().num_val(a, b.get_sort()) == b; } inline expr operator!=(expr const & a, expr const & b) { check_context(a, b); @@ -1168,8 +1246,8 @@ namespace z3 { a.check_error(); return expr(a.ctx(), r); } - inline expr operator!=(expr const & a, int b) { assert(a.is_arith() || a.is_bv()); return a != a.ctx().num_val(b, a.get_sort()); } - inline expr operator!=(int a, expr const & b) { assert(b.is_arith() || b.is_bv()); return b.ctx().num_val(a, b.get_sort()) != b; } + inline expr operator!=(expr const & a, int b) { assert(a.is_arith() || a.is_bv() || a.is_fpa()); return a != a.ctx().num_val(b, a.get_sort()); } + inline expr operator!=(int a, expr const & b) { assert(b.is_arith() || b.is_bv() || b.is_fpa()); return b.ctx().num_val(a, b.get_sort()) != b; } inline expr operator+(expr const & a, expr const & b) { check_context(a, b); @@ -1188,6 +1266,9 @@ namespace z3 { Z3_ast _args[2] = { a, b }; r = Z3_mk_re_union(a.ctx(), 2, _args); } + else if (a.is_fpa() && b.is_fpa()) { + r = Z3_mk_fpa_add(a.ctx(), a.ctx().fpa_rounding_mode(), a, b); + } else { // operator is not supported by given arguments. assert(false); @@ -1208,6 +1289,9 @@ namespace z3 { else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvmul(a.ctx(), a, b); } + else if (a.is_fpa() && b.is_fpa()) { + r = Z3_mk_fpa_mul(a.ctx(), a.ctx().fpa_rounding_mode(), a, b); + } else { // operator is not supported by given arguments. assert(false); @@ -1245,6 +1329,9 @@ namespace z3 { else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvsdiv(a.ctx(), a, b); } + else if (a.is_fpa() && b.is_fpa()) { + r = Z3_mk_fpa_div(a.ctx(), a.ctx().fpa_rounding_mode(), a, b); + } else { // operator is not supported by given arguments. assert(false); @@ -1263,6 +1350,9 @@ namespace z3 { else if (a.is_bv()) { r = Z3_mk_bvneg(a.ctx(), a); } + else if (a.is_fpa()) { + r = Z3_mk_fpa_neg(a.ctx(), a); + } else { // operator is not supported by given arguments. assert(false); @@ -1281,6 +1371,9 @@ namespace z3 { else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvsub(a.ctx(), a, b); } + else if (a.is_fpa() && b.is_fpa()) { + r = Z3_mk_fpa_sub(a.ctx(), a.ctx().fpa_rounding_mode(), a, b); + } else { // operator is not supported by given arguments. assert(false); @@ -1300,6 +1393,9 @@ namespace z3 { else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvsle(a.ctx(), a, b); } + else if (a.is_fpa() && b.is_fpa()) { + r = Z3_mk_fpa_leq(a.ctx(), a, b); + } else { // operator is not supported by given arguments. assert(false); @@ -1322,6 +1418,9 @@ namespace z3 { else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvslt(a.ctx(), a, b); } + else if (a.is_fpa() && b.is_fpa()) { + r = Z3_mk_fpa_lt(a.ctx(), a, b); + } else { // operator is not supported by given arguments. assert(false); @@ -1341,6 +1440,9 @@ namespace z3 { else if (a.is_bv() && b.is_bv()) { r = Z3_mk_bvsgt(a.ctx(), a, b); } + else if (a.is_fpa() && b.is_fpa()) { + r = Z3_mk_fpa_gt(a.ctx(), a, b); + } else { // operator is not supported by given arguments. assert(false); @@ -1366,17 +1468,30 @@ namespace z3 { inline expr nand(expr const& a, expr const& b) { check_context(a, b); Z3_ast r = Z3_mk_bvnand(a.ctx(), a, b); return expr(a.ctx(), r); } inline expr nor(expr const& a, expr const& b) { check_context(a, b); Z3_ast r = Z3_mk_bvnor(a.ctx(), a, b); return expr(a.ctx(), r); } inline expr xnor(expr const& a, expr const& b) { check_context(a, b); Z3_ast r = Z3_mk_bvxnor(a.ctx(), a, b); return expr(a.ctx(), r); } - + inline expr min(expr const& a, expr const& b) { check_context(a, b); Z3_ast r = Z3_mk_fpa_min(a.ctx(), a, b); return expr(a.ctx(), r); } + inline expr max(expr const& a, expr const& b) { check_context(a, b); Z3_ast r = Z3_mk_fpa_max(a.ctx(), a, b); return expr(a.ctx(), r); } + inline expr abs(expr const & a) { Z3_ast r = Z3_mk_fpa_abs(a.ctx(), a); return expr(a.ctx(), r); } + inline expr sqrt(expr const & a, expr const& rm) { + check_context(a, rm); + assert(a.is_fpa()); + Z3_ast r = Z3_mk_fpa_sqrt(a.ctx(), rm, a); + return expr(a.ctx(), r); + } inline expr operator~(expr const & a) { Z3_ast r = Z3_mk_bvnot(a.ctx(), a); return expr(a.ctx(), r); } - + inline expr fma(expr const& a, expr const& b, expr const& c, expr const& rm) { + check_context(a, b); check_context(a, c); check_context(a, rm); + assert(a.is_fpa() && b.is_fpa() && c.is_fpa()); + Z3_ast r = Z3_mk_fpa_fma(a.ctx(), rm, a, b, c); + a.check_error(); + return expr(a.ctx(), r); + } /** \brief Create the if-then-else expression ite(c, t, e) \pre c.is_bool() */ - inline expr ite(expr const & c, expr const & t, expr const & e) { check_context(c, t); check_context(c, e); assert(c.is_bool()); @@ -1453,45 +1568,45 @@ namespace z3 { inline expr smod(expr const & a, expr const & b) { return to_expr(a.ctx(), Z3_mk_bvsmod(a.ctx(), a, b)); } inline expr smod(expr const & a, int b) { return smod(a, a.ctx().num_val(b, a.get_sort())); } inline expr smod(int a, expr const & b) { return smod(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 { @@ -1563,7 +1678,7 @@ namespace z3 { unsigned m_index; public: iterator(ast_vector_tpl const* v, unsigned i): m_vector(v), m_index(i) {} - iterator(iterator& other): m_vector(other.m_vector), m_index(other.m_index) {} + iterator(iterator& other): m_vector(other.m_vector), m_index(other.m_index) {} iterator operator=(iterator const& other) { m_vector = other.m_vector; m_index = other.m_index; return *this; } bool operator==(iterator const& other) { @@ -1773,7 +1888,7 @@ namespace z3 { return expr(ctx, r); } - inline expr mk_or(expr_vector const& args) { + inline expr mk_or(expr_vector const& args) { array _args(args); Z3_ast r = Z3_mk_or(args.ctx(), _args.size(), _args.ptr()); args.check_error(); @@ -1852,7 +1967,7 @@ namespace z3 { model(context & c):object(c) { init(Z3_mk_model(c)); } model(context & c, Z3_model m):object(c) { init(m); } model(model const & s):object(s) { init(s.m_model); } - model(model& src, context& dst, translate) : object(dst) { init(Z3_model_translate(src.ctx(), src, dst)); } + model(model& src, context& dst, translate) : object(dst) { init(Z3_model_translate(src.ctx(), src, dst)); } ~model() { Z3_model_dec_ref(ctx(), m_model); } operator Z3_model() const { return m_model; } model & operator=(model const & s) { @@ -1884,7 +1999,7 @@ namespace z3 { } // returns interpretation of constant declaration c. - // If c is not assigned any value in the model it returns + // If c is not assigned any value in the model it returns // an expression with a null ast reference. expr get_const_interp(func_decl c) const { check_context(*this, c); @@ -1898,7 +2013,7 @@ namespace z3 { check_error(); return func_interp(ctx(), r); } - + // returns true iff the model contains an interpretation // for function f. bool has_interp(func_decl f) const { @@ -1945,7 +2060,7 @@ namespace z3 { bool is_uint(unsigned i) const { Z3_bool r = Z3_stats_is_uint(ctx(), m_stats, i); check_error(); return r != 0; } bool is_double(unsigned i) const { Z3_bool r = Z3_stats_is_double(ctx(), m_stats, i); check_error(); return r != 0; } unsigned uint_value(unsigned i) const { unsigned r = Z3_stats_get_uint_value(ctx(), m_stats, i); check_error(); return r; } - double double_value(unsigned i) const { double r = Z3_stats_get_double_value(ctx(), m_stats, i); check_error(); return r; } + double double_value(unsigned i) const { double r = Z3_stats_get_double_value(ctx(), m_stats, i); check_error(); return r; } friend std::ostream & operator<<(std::ostream & out, stats const & s); }; inline std::ostream & operator<<(std::ostream & out, stats const & s) { out << Z3_stats_to_string(s.ctx(), s); return out; } @@ -2001,7 +2116,7 @@ namespace z3 { void add(expr const & e, char const * p) { add(e, ctx().bool_const(p)); } - // fails for some compilers: + // fails for some compilers: // void add(expr_vector const& v) { check_context(*this, v); for (expr e : v) add(e); } void from_file(char const* file) { Z3_solver_from_file(ctx(), m_solver, file); ctx().check_parser_error(); } void from_string(char const* s) { Z3_solver_from_string(ctx(), m_solver, s); ctx().check_parser_error(); } @@ -2066,11 +2181,11 @@ namespace z3 { param_descrs get_param_descrs() { return param_descrs(ctx(), Z3_solver_get_param_descrs(ctx(), m_solver)); } - expr_vector cube(expr_vector& vars, unsigned cutoff) { - Z3_ast_vector r = Z3_solver_cube(ctx(), m_solver, vars, cutoff); - check_error(); - return expr_vector(ctx(), r); - } + expr_vector cube(expr_vector& vars, unsigned cutoff) { + Z3_ast_vector r = Z3_solver_cube(ctx(), m_solver, vars, cutoff); + check_error(); + return expr_vector(ctx(), r); + } class cube_iterator { solver& m_solver; @@ -2118,7 +2233,7 @@ namespace z3 { cube_iterator operator++(int) { assert(false); return *this; } expr_vector const * operator->() const { return &(operator*()); } expr_vector const& operator*() const { return m_cube; } - + bool operator==(cube_iterator const& other) { return other.m_end == m_end; }; @@ -2407,7 +2522,7 @@ namespace z3 { class optimize : public object { Z3_optimize m_opt; - + public: class handle { unsigned m_h; @@ -2456,16 +2571,16 @@ namespace z3 { Z3_optimize_pop(ctx(), m_opt); } check_result check() { Z3_lbool r = Z3_optimize_check(ctx(), m_opt, 0, 0); check_error(); return to_check_result(r); } - check_result check(expr_vector const& asms) { + check_result check(expr_vector const& asms) { unsigned n = asms.size(); array _asms(n); for (unsigned i = 0; i < n; i++) { check_context(*this, asms[i]); _asms[i] = asms[i]; } - Z3_lbool r = Z3_optimize_check(ctx(), m_opt, n, _asms.ptr()); - check_error(); - return to_check_result(r); + Z3_lbool r = Z3_optimize_check(ctx(), m_opt, n, _asms.ptr()); + check_error(); + return to_check_result(r); } model get_model() const { Z3_model m = Z3_optimize_get_model(ctx(), m_opt); check_error(); return model(ctx(), m); } expr_vector unsat_core() const { Z3_ast_vector r = Z3_optimize_get_unsat_core(ctx(), m_opt); check_error(); return expr_vector(ctx(), r); } @@ -2495,25 +2610,25 @@ namespace z3 { public: fixedpoint(context& c):object(c) { m_fp = Z3_mk_fixedpoint(c); Z3_fixedpoint_inc_ref(c, m_fp); } ~fixedpoint() { Z3_fixedpoint_dec_ref(ctx(), m_fp); } - operator Z3_fixedpoint() const { return m_fp; } + operator Z3_fixedpoint() const { return m_fp; } void from_string(char const* s) { Z3_fixedpoint_from_string(ctx(), m_fp, s); check_error(); } void from_file(char const* s) { Z3_fixedpoint_from_file(ctx(), m_fp, s); check_error(); } void add_rule(expr& rule, symbol const& name) { Z3_fixedpoint_add_rule(ctx(), m_fp, rule, name); check_error(); } void add_fact(func_decl& f, unsigned * args) { Z3_fixedpoint_add_fact(ctx(), m_fp, f, f.arity(), args); check_error(); } check_result query(expr& q) { Z3_lbool r = Z3_fixedpoint_query(ctx(), m_fp, q); check_error(); return to_check_result(r); } - check_result query(func_decl_vector& relations) { + check_result query(func_decl_vector& relations) { array rs(relations); - Z3_lbool r = Z3_fixedpoint_query_relations(ctx(), m_fp, rs.size(), rs.ptr()); - check_error(); - return to_check_result(r); + Z3_lbool r = Z3_fixedpoint_query_relations(ctx(), m_fp, rs.size(), rs.ptr()); + check_error(); + return to_check_result(r); } expr get_answer() { Z3_ast r = Z3_fixedpoint_get_answer(ctx(), m_fp); check_error(); return expr(ctx(), r); } std::string reason_unknown() { return Z3_fixedpoint_get_reason_unknown(ctx(), m_fp); } void update_rule(expr& rule, symbol const& name) { Z3_fixedpoint_update_rule(ctx(), m_fp, rule, name); check_error(); } unsigned get_num_levels(func_decl& p) { unsigned r = Z3_fixedpoint_get_num_levels(ctx(), m_fp, p); check_error(); return r; } - expr get_cover_delta(int level, func_decl& p) { - Z3_ast r = Z3_fixedpoint_get_cover_delta(ctx(), m_fp, level, p); - check_error(); + expr get_cover_delta(int level, func_decl& p) { + Z3_ast r = Z3_fixedpoint_get_cover_delta(ctx(), m_fp, level, p); + check_error(); return expr(ctx(), r); } void add_cover(int level, func_decl& p, expr& property) { Z3_fixedpoint_add_cover(ctx(), m_fp, level, p, property); check_error(); } @@ -2527,7 +2642,7 @@ namespace z3 { std::string to_string() { return Z3_fixedpoint_to_string(ctx(), m_fp, 0, 0); } std::string to_string(expr_vector const& queries) { array qs(queries); - return Z3_fixedpoint_to_string(ctx(), m_fp, qs.size(), qs.ptr()); + return Z3_fixedpoint_to_string(ctx(), m_fp, qs.size(), qs.ptr()); } void push() { Z3_fixedpoint_push(ctx(), m_fp); check_error(); } void pop() { Z3_fixedpoint_pop(ctx(), m_fp); check_error(); } @@ -2562,11 +2677,36 @@ namespace z3 { inline sort context::string_sort() { Z3_sort s = Z3_mk_string_sort(m_ctx); check_error(); return sort(*this, s); } inline sort context::seq_sort(sort& s) { Z3_sort r = Z3_mk_seq_sort(m_ctx, s); check_error(); return sort(*this, r); } inline sort context::re_sort(sort& s) { Z3_sort r = Z3_mk_re_sort(m_ctx, s); check_error(); return sort(*this, r); } + inline sort context::fpa_sort(unsigned ebits, unsigned sbits) { Z3_sort s = Z3_mk_fpa_sort(m_ctx, ebits, sbits); check_error(); return sort(*this, s); } + + template<> + inline sort context::fpa_sort<16>() { return fpa_sort(5, 11); } + + template<> + inline sort context::fpa_sort<32>() { return fpa_sort(8, 24); } + + template<> + inline sort context::fpa_sort<64>() { return fpa_sort(11, 53); } + + template<> + inline sort context::fpa_sort<128>() { return fpa_sort(15, 113); } + + inline sort context::fpa_rounding_mode() { + switch (m_rounding_mode) { + case rounding_mode::RNA: return sort(*this, Z3_mk_fpa_rna(m_ctx)); + case rounding_mode::RNE: return sort(*this, Z3_mk_fpa_rne(m_ctx)); + case rounding_mode::RTP: return sort(*this, Z3_mk_fpa_rtp(m_ctx)); + case rounding_mode::RTN: return sort(*this, Z3_mk_fpa_rtn(m_ctx)); + case rounding_mode::RTZ: return sort(*this, Z3_mk_fpa_rtz(m_ctx)); + } + } + + inline void context::set_rounding_mode(rounding_mode rm) { m_rounding_mode = rm; } inline sort context::array_sort(sort d, sort r) { Z3_sort s = Z3_mk_array_sort(m_ctx, d, r); check_error(); return sort(*this, s); } inline sort context::array_sort(sort_vector const& d, sort r) { array dom(d); - Z3_sort s = Z3_mk_array_sort_n(m_ctx, dom.size(), dom.ptr(), r); check_error(); return sort(*this, s); + Z3_sort s = Z3_mk_array_sort_n(m_ctx, dom.size(), dom.ptr(), r); check_error(); return sort(*this, s); } inline sort context::enumeration_sort(char const * name, unsigned n, char const * const * enum_names, func_decl_vector & cs, func_decl_vector & ts) { array _enum_names(n); @@ -2681,6 +2821,10 @@ namespace z3 { inline expr context::int_const(char const * name) { return constant(name, int_sort()); } inline expr context::real_const(char const * name) { return constant(name, real_sort()); } inline expr context::bv_const(char const * name, unsigned sz) { return constant(name, bv_sort(sz)); } + inline expr context::fpa_const(char const * name, unsigned ebits, unsigned sbits) { return constant(name, fpa_sort(ebits, sbits)); } + + template + inline expr context::fpa_const(char const * name) { return constant(name, fpa_sort()); } inline expr context::bool_val(bool b) { return b ? expr(*this, Z3_mk_true(m_ctx)) : expr(*this, Z3_mk_false(m_ctx)); } @@ -2702,12 +2846,15 @@ namespace z3 { inline expr context::bv_val(int64_t n, unsigned sz) { sort s = bv_sort(sz); Z3_ast r = Z3_mk_int64(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr context::bv_val(uint64_t n, unsigned sz) { sort s = bv_sort(sz); Z3_ast r = Z3_mk_unsigned_int64(m_ctx, n, s); check_error(); return expr(*this, r); } inline expr context::bv_val(char const * n, unsigned sz) { sort s = bv_sort(sz); Z3_ast r = Z3_mk_numeral(m_ctx, n, s); check_error(); return expr(*this, r); } - inline expr context::bv_val(unsigned n, bool const* bits) { + inline expr context::bv_val(unsigned n, bool const* bits) { array _bits(n); for (unsigned i = 0; i < n; ++i) _bits[i] = bits[i] ? 1 : 0; - Z3_ast r = Z3_mk_bv_numeral(m_ctx, n, _bits.ptr()); check_error(); return expr(*this, r); + Z3_ast r = Z3_mk_bv_numeral(m_ctx, n, _bits.ptr()); check_error(); return expr(*this, r); } + inline expr context::fpa_val(double n) { sort s = fpa_sort<64>(); Z3_ast r = Z3_mk_fpa_numeral_double(m_ctx, n, s); check_error(); return expr(*this, r); } + inline expr context::fpa_val(float n) { sort s = fpa_sort<32>(); Z3_ast r = Z3_mk_fpa_numeral_float(m_ctx, n, s); check_error(); return expr(*this, r); } + inline expr context::string_val(char const* s) { Z3_ast r = Z3_mk_string(m_ctx, s); check_error(); return expr(*this, r); } inline expr context::string_val(std::string const& s) { Z3_ast r = Z3_mk_string(m_ctx, s.c_str()); check_error(); return expr(*this, r); } @@ -2831,8 +2978,8 @@ namespace z3 { a.check_error(); return expr(a.ctx(), r); } - inline expr select(expr const & a, int i) { - return select(a, a.ctx().num_val(i, a.get_sort().array_domain())); + inline expr select(expr const & a, int i) { + return select(a, a.ctx().num_val(i, a.get_sort().array_domain())); } inline expr select(expr const & a, expr_vector const & i) { check_context(a, i); @@ -2862,10 +3009,10 @@ namespace z3 { return expr(a.ctx(), r); } - inline expr as_array(func_decl & f) { - Z3_ast r = Z3_mk_as_array(f.ctx(), f); - f.check_error(); - return expr(f.ctx(), r); + inline expr as_array(func_decl & f) { + Z3_ast r = Z3_mk_as_array(f.ctx(), f); + f.check_error(); + return expr(f.ctx(), r); } #define MK_EXPR1(_fn, _arg) \ @@ -2897,21 +3044,21 @@ namespace z3 { inline expr set_del(expr const& s, expr const& e) { MK_EXPR2(Z3_mk_set_del, s, e); - } + } inline expr set_union(expr const& a, expr const& b) { - check_context(a, b); + check_context(a, b); Z3_ast es[2] = { a, b }; Z3_ast r = Z3_mk_set_union(a.ctx(), 2, es); - a.check_error(); + a.check_error(); return expr(a.ctx(), r); } inline expr set_intersect(expr const& a, expr const& b) { - check_context(a, b); + check_context(a, b); Z3_ast es[2] = { a, b }; Z3_ast r = Z3_mk_set_intersect(a.ctx(), 2, es); - a.check_error(); + a.check_error(); return expr(a.ctx(), r); } @@ -2995,10 +3142,10 @@ namespace z3 { MK_EXPR1(Z3_mk_re_complement, a); } inline expr range(expr const& lo, expr const& hi) { - check_context(lo, hi); - Z3_ast r = Z3_mk_re_range(lo.ctx(), lo, hi); - lo.check_error(); - return expr(lo.ctx(), r); + check_context(lo, hi); + Z3_ast r = Z3_mk_re_range(lo.ctx(), lo, hi); + lo.check_error(); + return expr(lo.ctx(), r); } @@ -3009,7 +3156,7 @@ namespace z3 { Z3_ast_vector r = Z3_parse_smtlib2_string(*this, s, 0, 0, 0, 0, 0, 0); check_error(); return expr_vector(*this, r); - + } inline expr_vector context::parse_file(char const* s) { Z3_ast_vector r = Z3_parse_smtlib2_file(*this, s, 0, 0, 0, 0, 0, 0); From f5fea8ae3040ee05d20f0b8515902ab7d657e512 Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Thu, 11 Oct 2018 22:05:04 -0700 Subject: [PATCH 133/138] add parameter to force sat-cleaning on initialization and on simplification phases Signed-off-by: Nikolaj Bjorner --- src/sat/sat_config.cpp | 2 ++ src/sat/sat_config.h | 2 ++ src/sat/sat_params.pyg | 1 + src/sat/sat_solver.cpp | 10 +++++----- src/sat/sat_solver.h | 2 +- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/sat/sat_config.cpp b/src/sat/sat_config.cpp index c7f2377c9..e9e6c4ee6 100644 --- a/src/sat/sat_config.cpp +++ b/src/sat/sat_config.cpp @@ -152,6 +152,8 @@ namespace sat { m_gc_burst = p.gc_burst(); m_gc_defrag = p.gc_defrag(); + m_force_cleanup = p.force_cleanup(); + m_minimize_lemmas = p.minimize_lemmas(); m_core_minimize = p.core_minimize(); m_core_minimize_partial = p.core_minimize_partial(); diff --git a/src/sat/sat_config.h b/src/sat/sat_config.h index 37efe69ed..63e979394 100644 --- a/src/sat/sat_config.h +++ b/src/sat/sat_config.h @@ -136,6 +136,8 @@ namespace sat { bool m_gc_burst; bool m_gc_defrag; + bool m_force_cleanup; + bool m_minimize_lemmas; bool m_dyn_sub_res; diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index 113a8133e..6202ff155 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -31,6 +31,7 @@ def_module_params('sat', ('gc.burst', BOOL, False, 'perform eager garbage collection during initialization'), ('gc.defrag', BOOL, True, 'defragment clauses when garbage collecting'), ('simplify.delay', UINT, 0, 'set initial delay of simplification by a conflict count'), + ('force_cleanup', BOOL, False, 'force cleanup to remove tautologies and simplify clauses'), ('minimize_lemmas', BOOL, True, 'minimize learned clauses'), ('dyn_sub_res', BOOL, True, 'dynamic subsumption resolution for minimizing learned clauses'), ('core.minimize', BOOL, False, 'minimize computed core'), diff --git a/src/sat/sat_solver.cpp b/src/sat/sat_solver.cpp index 426caa6dd..8b0d23f9c 100644 --- a/src/sat/sat_solver.cpp +++ b/src/sat/sat_solver.cpp @@ -1086,7 +1086,7 @@ namespace sat { init_assumptions(num_lits, lits); propagate(false); if (check_inconsistent()) return l_false; - cleanup(); + cleanup(m_config.m_force_cleanup); if (m_config.m_unit_walk) { return do_unit_walk(); @@ -1458,7 +1458,7 @@ namespace sat { if (should_restart()) return l_undef; if (at_base_lvl()) { - cleanup(); // cleaner may propagate frozen clauses + cleanup(false); // cleaner may propagate frozen clauses if (inconsistent()) { TRACE("sat", tout << "conflict at level 0\n";); return l_false; @@ -1654,7 +1654,7 @@ namespace sat { SASSERT(at_base_lvl()); - m_cleaner(); + m_cleaner(m_config.m_force_cleanup); CASSERT("sat_simplify_bug", check_invariant()); m_scc(); @@ -3695,10 +3695,10 @@ namespace sat { // Simplification // // ----------------------- - void solver::cleanup() { + void solver::cleanup(bool force) { if (!at_base_lvl() || inconsistent()) return; - if (m_cleaner() && m_ext) + if (m_cleaner(force) && m_ext) m_ext->clauses_modifed(); } diff --git a/src/sat/sat_solver.h b/src/sat/sat_solver.h index e6e6c9d7b..fd9af4a02 100644 --- a/src/sat/sat_solver.h +++ b/src/sat/sat_solver.h @@ -549,7 +549,7 @@ namespace sat { // // ----------------------- public: - void cleanup(); + void cleanup(bool force); void simplify(bool learned = true); void asymmetric_branching(); unsigned scc_bin(); From 5bd93b8a778139ce238d6dcc024db606855e8f9b Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Fri, 12 Oct 2018 23:38:53 +0700 Subject: [PATCH 134/138] Typo fixes. --- RELEASE_NOTES | 4 ++-- src/sat/sat_params.pyg | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/RELEASE_NOTES b/RELEASE_NOTES index 3337f098b..acdb627d8 100644 --- a/RELEASE_NOTES +++ b/RELEASE_NOTES @@ -21,7 +21,7 @@ Version 4.8.0 extracting models from apply_result have been replaced. - An optional mode handles xor constraints using a custom xor propagator. It is off by default and its value not demonstrated. - - The SAT solver includes new inprocessing technques that are available during simplification. + - The SAT solver includes new inprocessing techniques that are available during simplification. It performs asymmetric tautology elimination by default, and one can turn on more powerful inprocessing techniques (known as ACCE, ABCE, CCE). Asymmetric branching also uses features introduced in Lingeling by exploiting binary implication graphs. Use sat.acce=true to enable the full repertoire of inprocessing methods. By default, clauses that are "eliminated" by acce are tagged @@ -318,7 +318,7 @@ First source code release (October 2, 2012) - Added support for numbers in scientific notation at Z3_ast Z3_mk_numeral(__in Z3_context c, __in Z3_string numeral, __in Z3_sort ty). -- New builtin symbols in the arithmetic theory: pi, euler, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh. The first two are constants, and the others are unary functions. These symbols are not available if the a SMT 2.0 logic is specified (e.g., QF_LRA, QF_NRA, QF_LIA, etc) because these symbols are not defined in these logics. That is, the new symbols are only available if the logic is not specified. +- New builtin symbols in the arithmetic theory: pi, euler, sin, cos, tan, asin, acos, atan, sinh, cosh, tanh, asinh, acosh, atanh. The first two are constants, and the others are unary functions. These symbols are not available if a SMT 2.0 logic is specified (e.g., QF_LRA, QF_NRA, QF_LIA, etc) because these symbols are not defined in these logics. That is, the new symbols are only available if the logic is not specified. Version 4.1 =========== diff --git a/src/sat/sat_params.pyg b/src/sat/sat_params.pyg index 6202ff155..3f0479707 100644 --- a/src/sat/sat_params.pyg +++ b/src/sat/sat_params.pyg @@ -15,7 +15,7 @@ def_module_params('sat', ('restart.margin', DOUBLE, 1.1, 'margin between fast and slow restart factors. For ema'), ('restart.emafastglue', DOUBLE, 3e-2, 'ema alpha factor for fast moving average'), ('restart.emaslowglue', DOUBLE, 1e-5, 'ema alpha factor for slow moving average'), - ('variable_decay', UINT, 110, 'multiplier (divided by 100) for the VSIDS activity increement'), + ('variable_decay', UINT, 110, 'multiplier (divided by 100) for the VSIDS activity increment'), ('inprocess.max', UINT, UINT_MAX, 'maximal number of inprocessing passes'), ('branching.heuristic', SYMBOL, 'vsids', 'branching heuristic vsids, lrb or chb'), ('branching.anti_exploration', BOOL, False, 'apply anti-exploration heuristic for branch selection'), @@ -58,7 +58,7 @@ def_module_params('sat', # - psat: Let psat_heur := (Sum_{clause C} (psat.clause_base ^ {-|C|+1})) / |freevars|^psat.var_exp # Cut if the value of psat_heur exceeds psat.trigger # - adaptive_freevars: Cut if the number of current unassigned variables drops below a fraction of free variables - # at the time of the last conflict. The fraction is increased every time the a cutoff is created. + # at the time of the last conflict. The fraction is increased every time the cutoff is created. # - adative_psat: Cut based on psat_heur in an adaptive way. ('lookahead.cube.fraction', DOUBLE, 0.4, 'adaptive fraction to create lookahead cubes. Used when lookahead.cube.cutoff is adaptive_freevars or adaptive_psat'), ('lookahead.cube.depth', UINT, 1, 'cut-off depth to create cubes. Used when lookahead.cube.cutoff is depth.'), From 58682c20be2fb53adf5bbfb08898469dcbbb161e Mon Sep 17 00:00:00 2001 From: Bruce Mitchener Date: Sat, 13 Oct 2018 07:58:27 +0700 Subject: [PATCH 135/138] dl_util: Use an unsigned to match other values. --- src/muz/base/dl_util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muz/base/dl_util.h b/src/muz/base/dl_util.h index 1a2b64dc4..c6e214f79 100644 --- a/src/muz/base/dl_util.h +++ b/src/muz/base/dl_util.h @@ -276,7 +276,7 @@ namespace datalog { } unsigned n = container.size(); unsigned ofs = 1; - int r_i = 1; + unsigned r_i = 1; for(unsigned i=removed_cols[0]+1; i Date: Fri, 12 Oct 2018 22:44:23 -0700 Subject: [PATCH 136/138] remove class from enum class, add default to avoid compiler warning Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index ff953ca8f..e009113fb 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -139,7 +139,7 @@ namespace z3 { */ class context { public: - enum class rounding_mode { + enum rounding_mode { RNA, RNE, RTP, @@ -2693,11 +2693,12 @@ namespace z3 { inline sort context::fpa_rounding_mode() { switch (m_rounding_mode) { - case rounding_mode::RNA: return sort(*this, Z3_mk_fpa_rna(m_ctx)); - case rounding_mode::RNE: return sort(*this, Z3_mk_fpa_rne(m_ctx)); - case rounding_mode::RTP: return sort(*this, Z3_mk_fpa_rtp(m_ctx)); - case rounding_mode::RTN: return sort(*this, Z3_mk_fpa_rtn(m_ctx)); - case rounding_mode::RTZ: return sort(*this, Z3_mk_fpa_rtz(m_ctx)); + case rounding_mode::RNA: return sort(*this, Z3_mk_fpa_rna(m_ctx)); + case rounding_mode::RNE: return sort(*this, Z3_mk_fpa_rne(m_ctx)); + case rounding_mode::RTP: return sort(*this, Z3_mk_fpa_rtp(m_ctx)); + case rounding_mode::RTN: return sort(*this, Z3_mk_fpa_rtn(m_ctx)); + case rounding_mode::RTZ: return sort(*this, Z3_mk_fpa_rtz(m_ctx)); + default: return sort(*this); } } From 6277ed61c942f1f150c8d792d9f9d3f09da925ae Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 13 Oct 2018 02:09:35 -0700 Subject: [PATCH 137/138] pull rounding mode top-level to deal with build Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index e009113fb..54fc735b0 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -127,6 +127,14 @@ namespace z3 { unsat, sat, unknown }; + enum rounding_mode { + RNA, + RNE, + RTP, + RTN, + RTZ + }; + inline check_result to_check_result(Z3_lbool l) { if (l == Z3_L_TRUE) return sat; else if (l == Z3_L_FALSE) return unsat; @@ -137,15 +145,9 @@ namespace z3 { /** \brief A Context manages all other Z3 objects, global configuration options, etc. */ + + class context { - public: - enum rounding_mode { - RNA, - RNE, - RTP, - RTN, - RTZ - }; private: bool m_enable_exceptions; rounding_mode m_rounding_mode; From 70f3fa36c5e14026a332be40299bbd7f9b731d1d Mon Sep 17 00:00:00 2001 From: Nikolaj Bjorner Date: Sat, 13 Oct 2018 09:39:48 -0700 Subject: [PATCH 138/138] remove qualifiers that downlevel compilers complain about Signed-off-by: Nikolaj Bjorner --- src/api/c++/z3++.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/api/c++/z3++.h b/src/api/c++/z3++.h index 54fc735b0..1b1820909 100644 --- a/src/api/c++/z3++.h +++ b/src/api/c++/z3++.h @@ -155,7 +155,7 @@ namespace z3 { void init(config & c) { m_ctx = Z3_mk_context_rc(c); m_enable_exceptions = true; - m_rounding_mode = rounding_mode::RNA; + m_rounding_mode = RNA; Z3_set_error_handler(m_ctx, 0); Z3_set_ast_print_mode(m_ctx, Z3_PRINT_SMTLIB2_COMPLIANT); } @@ -2695,11 +2695,11 @@ namespace z3 { inline sort context::fpa_rounding_mode() { switch (m_rounding_mode) { - case rounding_mode::RNA: return sort(*this, Z3_mk_fpa_rna(m_ctx)); - case rounding_mode::RNE: return sort(*this, Z3_mk_fpa_rne(m_ctx)); - case rounding_mode::RTP: return sort(*this, Z3_mk_fpa_rtp(m_ctx)); - case rounding_mode::RTN: return sort(*this, Z3_mk_fpa_rtn(m_ctx)); - case rounding_mode::RTZ: return sort(*this, Z3_mk_fpa_rtz(m_ctx)); + case RNA: return sort(*this, Z3_mk_fpa_rna(m_ctx)); + case RNE: return sort(*this, Z3_mk_fpa_rne(m_ctx)); + case RTP: return sort(*this, Z3_mk_fpa_rtp(m_ctx)); + case RTN: return sort(*this, Z3_mk_fpa_rtn(m_ctx)); + case RTZ: return sort(*this, Z3_mk_fpa_rtz(m_ctx)); default: return sort(*this); } }